On 11/05/2012 06:30 PM, Oscar Benjamin wrote:
> On 6 November 2012 02:01, Chris Angelico<rosuav at gmail.com> wrote:
>> On Tue, Nov 6, 2012 at 12:32 PM, Oscar Benjamin
>> <oscar.j.benjamin at gmail.com> wrote:
>>> I was just thinking to myself that it would be a hard thing to change
>>> because the list would need to know how to instantiate copies of all
>>> the different types of the elements in the list. Then I realised it
>>> doesn't. It is simply a case of how the list multiplication operator
>>> is implemented and whether it chooses to use a reference to the same
>>> list or make a copy of that list. Since all of this is implemented
>>> within the same list type it is a relatively easy change to make
>>> (ignoring backward compatibility concerns).
>>>>>> I don't see this non-copying list multiplication behaviour as
>>> contradictory but has anyone ever actually found a use for it?
>> Stupid example of why it can't copy:
>>>> bad = [open("test_file")] * 4
>>>> How do you clone something that isn't Plain Old Data? Ultimately,
>> that's where the problem comes from. It's easy enough to clone
>> something that's all scalars (strings, integers, None, etc) and
>> non-recursive lists/dicts of scalars, but anything more complicated
>> than that is rather harder.
> That's not what I meant. But now you've made me realise that I was
> wrong about what I did mean. In the case of
>> stuff = [[obj] * n] * m
>> I thought that the multiplication of the inner list ([obj] * n) by m
> could create a new list of lists using copies. On closer inspection I
> see that the list being multiplied is in fact [[obj] * n] and that
> this list can only know that it is a list of lists by inspecting its
> element(s) which makes things more complicated.
>> I retract my claim that this change would be easy to implement.
>>> Oscar
Hi Oscar,
In general, people don't use element multiplication (that I have *ever*
seen) to make lists where all elements of the outer most list point to
the same sub-*list* by reference. The most common use of the
multiplication is to fill an array with a constant, or short list of
constants; Hence, almost everyone has to work around the issue as the
initial poster did by using a much longer construction.
The most compact notation in programming really ought to reflect the
most *commonly* desired operation. Otherwise, we're really just making
people do extra typing for no reason.
Further, list comprehensions take quite a bit longer to run than low
level copies; by a factor of roughly 10. SO, it really would be worth
implementing the underlying logic -- even if it wasn't super easy.
I really don't think doing a shallow copy of lists would break anyone's
program.
The non-list elements, whatever they are, can be left as reference
copies -- but any element which is a list ought to be shallow copied.
The behavior observed in the opening post where modifying one element of
a sub-list, modifies all elements of all sub-lists is never desired as
far as I have ever witnessed.
The underlying implementation of Python can check an object type
trivially, and the only routine needed is a shallow list copy. So, no
it really isn't a complicated operation to do shallow copies of lists.
:)