0

I have a list containing a set of x mutable items. I would like to create another list where the set of x mutable items is repeated n times. However, the items must be references to unique objects rather than simply references to the original objects.

For example, let a = [[1],[2],[3]]. Suppose I would like the numbers inside to be repeated n=3 times, i.e. [[0],[2],[3],[1],[2],[3],[1],[2],[3]].

I can do this easily by using a * 3. The problem is that if I change a[0][0] = 0, then I will get a == [[0],[2],[3],[0],[2],[3],[0],[2],[3]], which is not desirable because I would like to change only the first element. Is there any other way to achieve this?

2
  • 1
    Just some pedantics: 1 is not mutable, though [1] is. What changes when you do lst[idx] = val is that the binding of lst[idx] is changed, the original value that the binding referred to remains untouched. Once you grok the underlying binding/immutability concepts, your Python skills reach a new level. Commented Jan 10, 2021 at 22:58
  • @paxdiablo. Ok I edited my question for future references. Commented Jan 10, 2021 at 23:04

6 Answers 6

2

itertools has a method for this called repeat. Just pair it with chain.

from itertools import repeat, chain

a = [1,2,3]

b = list(chain(*repeat(a, 3)))

>>> b
[1,2,3,1,2,3,1,2,3]
Sign up to request clarification or add additional context in comments.

1 Comment

Doesn't that give [[1, 2, 3], [1, 2, 3], [1, 2, 3]]?
0
a = [1,2,3]
a = a*3  
a[0] =0
print(a)

1 Comment

If you get the id of the first two 2s (id(a[1]) and id(a[4])), you'll see they're the same object.
0

Very simply, you need to make copies of a, rather than repeating references to the original, mutable object. If you need just one level of copy, you can do it like this, noting that it's a shallow copy (see second output).

a = [[1, 1], [2], [3, 3, 3]]
b = a[:] * 3
b[0] = "change"
print(b)
b[2][1] = "deep"
print(b)

Output:

['change', [2], [3, 3, 3], [1, 1], [2], [3, 3, 3], [1, 1], [2], [3, 3, 3]]
['change', [2], [3, 'deep', 3], [1, 1], [2], [3, 'deep', 3], [1, 1], [2], [3, 'deep', 3]]

See how only the first level is given a new reference. When you try to change a deeper element, you see that the nested lists are still the same object. To fix that problem, use a deep copy for each element of b:

import copy

a = [[1, 1], [2], [3, 3, 3]]
b = [copy.deepcopy(a) for _ in range(3)]
b[0] = "change"
print(b)
b[2][1] = "deep"
print(b)

Output:

['change', [[1, 1], [2], [3, 3, 3]], [[1, 1], [2], [3, 3, 3]]]
['change', [[1, 1], [2], [3, 3, 3]], [[1, 1], 'deep', [3, 3, 3]]]

Comments

0

Just do:

n = [sub_a.copy() for sub_a in a] * 3

You need copy() to avoid the problem.

a = [[1],[2],[3]]

n = [sub_a.copy() for sub_a in a] * 3

a[0] = [-1]
a[2][0] = -1

print (n)

Output:

[[1], [2], [3], [1], [2], [3], [1], [2], [3]]

7 Comments

This will just create three references to the same underlying copy, effectively changing nothing.
Thanks. I thought copy() will create a shallow copy according to the docs. Did you mean deepcopy()?
Well, I may be wrong on the exact terminology I edited. copy() is sufficient to do what you want tho, as deepcopy() should be too I think.
@Synthase Well, of course your code "works", because a is a list of integers. Integers are immutable. The OP was specifically asking about a collection of mutable objects. Try this with a list of lists.
@Paul, as far as I see, the OP took integers in the example. Sorry to use the OP's example.
|
0

To get new instances of mutable objects, you can map the object type's copy() method to the multiplied list:

a  = [{1},{2},{3}]
b  = [*map(set.copy,a*3)]

a[0].add(7)
b[0].add(8)
b[3].add(9)
print(a) # [{1, 7}, {2}, {3}]
print(b) # [{8, 1}, {2}, {3}, {1, 9}, {2}, {3}, {1}, {2}, {3}]

If you don't know the type of the items, or if they have different types, you can do it in a list comprehension:

b = [ i.copy() for i in a*3 ]

If not all of the items are mutable, you can do it like this:

b = [type(i)(i) for i in a*3]

If some can be None ...

b = [i if i is None else type(i)(i) for i in a*3]

You can also use deepcopy which covers all these cases including objects that contain nested objects ...

from copy import deepcopy
b  = [*map(deepcopy,a*3)]

Comments

0

I just figured out I can do this through:

from copy import deepcopy
a = [[1],[2],[3]]
n = 3
b = [deepcopy(val) for _ in range(n) for val in a]

Now if I set b[0][0] = 0, I will get b == [[0],[2],[3],[1],[2],[3],[1],[2],[3]].

3 Comments

It appears that b[0] = 1 has set it to zero :-)
@Synthase: terms like "below" or "above" are a bad idea on a site where order can change.
@paxdiablo well must be too late for me ahaha

Your Answer

By clicking “Post Your Answer”, you agree to our terms of service and acknowledge you have read our privacy policy.

Start asking to get answers

Find the answer to your question by asking.

Ask question

Explore related questions

See similar questions with these tags.