strange side effect with lists!?
Bengt Richter
bokr at oz.net
Wed Oct 13 08:57:34 EDT 2004
On Wed, 13 Oct 2004 14:19:56 +0200, aleaxit at yahoo.com (Alex Martelli) wrote:
><Wolfgang.Stoecher at profactor.at> wrote:
>
>> Hello,
>>
>> I'm new to Python and playing around. I'm confused by the following
>> behaviour:
>>
>> >>> l1 = [1] # define list
>> >>> l2 = l1 # copy of l1 ?
>> >>> l2,l1
>> ([1], [1])
>> >>> l2.extend(l1) # only l2 should be altered !?
>> >>> l2,l1
>> ([1, 1], [1, 1]) # but also l1 is altered!
>>
>> So what is the policy of assignment? When is a copy of an object created?
>> Where to find dox on this?
>
>Bruno's answer seems very thorough so I'll just try to briefly summarize
>the answers:
>
>1. simple assignment (to a bare name, at least), per se, never
> implicitly copies objects, but rather it sets a reference to an
> object (_another_ reference if the object already had some).
>
>2. a new object is created when you request such creation or perform
> operations that require it. Lists are particularly rich in such
> operations (see later). Simple assignment to a bare name is not
> an operation, per se -- it only affects the name, by making it refer
> to whatever object (new, or used;-) is on the righht of the '='.
>
>3. I believe any decent book on Python will cover this in detail.
>
>Now for ways to have a new list object L2 made, with just the same items
>and in the same order as another list object L1 ("shallow copy"):
>
>a. import copy; L2 = copy.copy(L1)
>
>This works to shallow-copy _any_ copyable object L1; unfortunately you
>do have to import copy first. Module copy also exposes function
>deepcopy, for those rare cases in which you wish to recursively also get
>copies of all objects to which a "root object" refers (as items, or
>attributes; there are some anomalies, e.g., copy.deepcopy(X) is X when X
>is a class, or type...).
>
>b. L2 = list(L1)
>
>I find this is most often what I use - it works (making a new list
>object) whatever kind of sequence, iterator, or other iterable L1 may
>be. It is also what I recommend you use unless you have some very
>specific need best met otherwise.
>
>c. various operations such as...:
> L2 = L1 * 1
> L2 = L1 + []
> L2 = L1[:]
>i.e. "multiply by one", "concatenate the empty list", or "get a slice of
>all items". I'm not sure why, but the latter seems to be very popular,
>even though it's neither as concise as L1*1 nor as clear and readable as
>list(L1).
>
I got curious:
>>> L = range(5)
>>> L
[0, 1, 2, 3, 4]
Make it self-referential:
>>> L[2]=L
>>> L
[0, 1, [...], 3, 4]
>>> import copy
>>> Lc = copy.copy(L)
>>> Lc
[0, 1, [0, 1, [...], 3, 4], 3, 4]
>>> Ldc = copy.deepcopy(L)
>>> Ldc
[0, 1, [...], 3, 4]
Interesting that the deep copy made the copy self-referential
(i.e., to the copy itself) like the original's reference to
its (different) self:
>>> id(Ldc) == id(Ldc[2])
True
Unlike the shallow copy:
>>> id(Lc) == id(Lc[2])
False
...whose middle reference was merely copied and sill refers to L:
>>> id(Lc[2]) == id(L)
True
But like the original:
>>> id(L) == id(L[2])
True
I'm impressed ;-)
Regards,
Bengt Richter
More information about the Python-list
mailing list