[Python] Slice notation and object copies

Started by
22 comments, last by Gage64 16 years, 2 months ago
I've decided to try and learn Python. I'm using the tutorial in the documentation and there's something I don't quite understand. When using slice notation, it looks like I am sometimes getting a copy of the object and other times I get the object itself. Sounds vague, I know, but the following example shows what I mean. If I write the following in the python shell:
a = [1, 2, 3]
a[:] = [4, 5, 6]
a
the output is: [4, 5, 6], so the object 'a' was modified, but if I write the following:
a[:].append(7)
a
the output is: [4, 5, 6], so 'a' was not modified, which I assume is because append() was called on a copy of 'a' returned by the [:]. So what's going on? Another question: Why does Python allow me to write the second code snippet? I mean, modifying a temporary copy of 'a' doesn't make sense, so shouldn't this be flagged as an error (like it does in C++)? Thanks in advance.
Advertisement
Quote:Original post by Gage64
modifying a temporary copy of 'a' doesn't make sense, so shouldn't this be flagged as an error (like it does in C++)?

Modifying temporary copies is completely legal in C++:
std::vector<int> a;a.push_back(1);std::vector<int>(a).push_back(2); // !!!



Quote:Original post by bubu LV
Modifying temporary copies is completely legal in C++


I just tried a few examples and you're right. I was going by this (under the Temporaries section), but it looks like it's incorrect (even though what is written there makes sense).
Python objects have several functions available to them in order to deal with slices including the __getslice__ and __setslice__ methods. What you're doing in your first example is (implicitly) calling the __setslice__ method - it takes a slice of an object, modifies it, and reinserts it into the object. In your second example, you are implicitly calling __getslice__ which returns a copy of the slice selected.

Basically, whenever you have a assign operation following a slice, the __setslice__ method is called. Otherwise the __getslice__ method is called.
CrimsonSun - Thank you for the thorough explanation, I understand now, though I still think it's strange that the second snippet doesn't cause an error.
In your second example, you're just appending 7 to a temporary copy. If you don't do anything with this temporary, then the temporary is just thrown away. You can assign this modified temporary to a variable:
a = [1, 2, 3]b = a[:].append(7)


Now b contains [1, 2, 3, 7].
Quote:a = [1, 2, 3]

b = a[:].append(7)



Now b contains [1, 2, 3, 7].


I just typed this, and the value of b is None because append() doesn't return anything.
You're absolutely right, I made a mistake above - append doesn't return anything, so b gets the value of None. Please disregard my above post.

However, you can always assign a variable to the return value of a slice. Notice also that this is the only way to get a copy of a sequence such as a list.

If you have:
a = [1, 2, 3]

And then assign b to a:
b = a

Then you've assigned b to the same list as that of a. So anything that modifies the list accessed from a also modifies the list accessed from b (they are the same list!):

b[1] = 4
Now both a and b contain the list [1, 4, 3]. If you want to have a copy of the sequence in a (such that modification of the list accessed by a does not modify the list accessed by b), you just take the default slice:

b = a[:]
Quote:Original post by CrimsonSun
However, you can always assign a variable to the return value of a slice. Notice also that this is the only way to get a copy of a sequence such as a list.

If you have:
a = [1, 2, 3]

And then assign b to a:
b = a

Then you've assigned b to the same list as that of a. So anything that modifies the list accessed from a also modifies the list accessed from b (they are the same list!):

b[1] = 4
Now both a and b contain the list [1, 4, 3]. If you want to have a copy of the sequence in a (such that modification of the list accessed by a does not modify the list accessed by b), you just take the default slice:

b = a[:]


I don't understand how any of that is relevant to my question :)

If you write:

a[:].append(5)

you are modifying a temporary object that will cease to exist after that statement (or at least it will no longer be accessible). I can't think of an example where you would want to do this, so if you wrote something like this, you probably made a mistake, which is why I think the interpreter should flag this as an error.

Thanks for your help so far.
It's not trivial for an interpreter to spot that such things are mistakes. Sometimes the function call on the right (eg. append() ) has side-effects that you're relying upon, so it can't assume it was an error. Maybe you were just testing the append() functionality, for example!

This topic is closed to new replies.

Advertisement