Understanding python syntax for lists [duplicate] - python

This question already has answers here:
Understanding slicing
(38 answers)
Closed 5 years ago.
list = sorted(set(list))
list[:] = sorted(set(list))
list[::] = sorted(set(list))
I am new to Python, and the first thing I am noticing is that the syntax is concise, but non obvious.
For example, it is not clear what is going on in the three statements above. I ran them and got some results and seems like statement 1 is not updating the list, while statement 2 and statement 3 are. But, I am sure there is more going on here.
What do each of the above assignments mean?

2 and 3 do the same (the step argument of a slice is optional, and both these slices use the default step of 1), but they both are inherently different from 1. Slice assignment (lst[:] = ...) mutates the original object while a common assignment (lst = ...) rebinds the variable to a new object.
>>> lst = [3,3,2,2,1,1]
>>> id(lst)
139793325565704
>>> lst[:] = sorted(set(lst))
>>> lst
[1, 2, 3]
>>> id(lst)
139793325565704 # same object
>>> lst = sorted(set(lst))
>>> id(lst)
139793325524744 # different object
A point worth noting is that slice assignment can have any iterable on the rhs (for partial slices their number of elements must match the length of the slice):
>>> lst = [1,2,3]
>>> lst[1:] = 'ab'
>>> lst
[1, 'a', 'b']
See some of the slice docs for more detailed information.

Related

Having problem with Shared Reference in case of Lists [duplicate]

This question already has answers here:
Is there a difference between "==" and "is"?
(13 answers)
Closed 2 years ago.
a=b=[1,2,3]
print (a is b) #True
But
a=[1,2,3]
print (a is [1,2,3]) #False
Why does the second part print False ?
Multiple assignment in Python creates two names that point to the same object. For example,
>>> a=b=[1,2,3]
>>> a[0] = 10
>>> b
[10, 2, 3]
is can be used to check whether two names (a and b) hold the reference to the same memory location (object). Therefore,
a=b=[1,2,3] # a and b hold the same reference
print (a is b) # True
Now in this example,
a = [1,2,3]
print (a is [1,2,3]) # False
a does not hold the same reference to the object [1, 2, 3], even though a and [1, 2, 3] are lists with identical elements.
In case you want to compare whether two lists contain the same elements, you can use ==:
>>> a=b=[1, 2, 3]
>>> a == b
True
>>>
>>> a = [1, 2, 3]
>>> a == [1, 2, 3]
True
Your first one explicitly makes a and b references to the object created by the list display [1,2,3].
In your second code, both uses of the list display [1,2,3] necessarily create new list objects, because lists are mutable and you don't want to implicitly share references to them.
Consider a simpler example:
a = []
b = []
a.append(1)
Do you want b to be modified as well?
For immutable values, like ints, the language implementation may cause literals to reuse references to existing objects, but it's not something that can be relied on.
the problem is the logic operator you are using.
You are asking are these identical object with is and not if they are the equal (same data).
One is a reference to a object and the other is the object so even though they are equal the are not the same.
Why your results
When you are setting a and b as the same list you are saying that a and b should be linked and should reference the same data so they are identical to each other but a and b are not the object [1,2,3] they are a reference to a list that is the same.
In summary
== - equal to (same).
is - identical to.
So if you want to check if they are equal(same) use:
>>> a=[1,2,3]
>>> print (a == [1,2,3])
True
Similar question worth reading:
Is there a difference between "==" and "is"?
Hope this helps, Harry.

Id of list returned by slicing in Python [duplicate]

This question already has answers here:
Why is the id of a Python class not unique when called quickly?
(6 answers)
Closed 3 years ago.
I read that slicing of list returns a new list with the contents of the original list.
While running the following code why am I seeing the same id for all the list returned by slicing. Can someone explain what is happening here.
list_4 = [0, 1, 2, 3, 4, 5]
print(id(list_4))
print(id(list_4[0:1]))
print(id(list_4[0:2]))
print(id(list_4[-1:]))
Output:
2812068811464
2812100759880
2812100759880
2812100759880
Your slice got deleted before the next statement, so the new slice has the same id. If you keep the slice, the id is never the same:
>>> print(id(list_4[0:1]))
139887348117232
>>> print(id(list_4[0:2]))
139887348117232
>>> print(id(list_4[-1:]))
139887348117232
>>> b = list_4[0:1]
>>> c = list_4[0:2]
>>> id(b)
139887348117232
>>> id(c)
139887348207200
>>>

Why do python lists act like this when using the = operator [duplicate]

This question already has answers here:
Variable assignment and modification (in python) [duplicate]
(6 answers)
Closed 4 years ago.
How come the following code:
a = [1,2,3]
b = a
b[0] = 3
print(a)
will print list b after it has been altered?[3,2,3].
Also why is this true but that the following code:
a = [1,2,3]
b = a
b = [0,0,0]
print(a,b)
prints [1, 2, 3] [0, 0, 0]?? This seems inconsistent. If the first code is true, then shouldn't the second code print [0,0,0][0,0,0]? Can someone please provide an explanation for this?
In python there are two types of data... mutable and immutable. Numbers, strings, boolean, tuples, and other simple types are immutable. Dicts, lists, sets, objects, classes, and other complex types are mutable.
When you say:
a = [1,2,3]
b = a
You've created a single mutable list in memory, assigned a to point to it, and then assigned b to point to it. It's the same thing in memory.
Therefore when you mutate it (modify it):
b[0] = 3
It is a modification (mutation) of the index [0] of the value which b points to at that same memory location.
However, when you replace it:
b = [0,0,0]
It is creating a new mutable list in memory and assigning b to point at it.
Check out the id() function. It will tell you the "address" of any variable. You can see which names are pointing to the same memory location with id(varname).
Bonus: Every value in python is passed by reference... meaning that when you assign it to a variable it simply causes that variable to point to that value where it was in memory. Having immutable types allows python to "reuse" the same memory location for common immutable types.
Consider some common values when the interpreter starts up:
>>> import sys
>>> sys.getrefcount('abc')
68
>>> sys.getrefcount(100)
110
>>> sys.getrefcount(2)
6471
However, a value that is definitely not present would return 2. This has to do with the fact that a couple of references to that value were in-use during the call to sys.getrefcount
>>> sys.getrefcount('nope not me. I am definitely not here already.')
2
Notice that an empty tuple has a lot of references:
>>> sys.getrefcount(tuple())
34571
But an empty list has no extra references:
>>> sys.getrefcount(list())
1
Why is this? Because tuple is immutable so it is fine to share that value across any number of variables. However, lists are mutable so they MUST NOT be shared across arbitrary variables or changes to one would affect the others.
Incidentally, this is also why you must NEVER use mutable types as default argument values to functions. Consider this innocent little function:
>>> def foo(value=[]):
... value.append(1)
... print(value)
...
...
When you call it you might expect to get [1] printed...
>>> foo()
[1]
However, when you call it again, you prob. won't expect to get [1,1] out... ???
>>> foo()
[1, 1]
And on and on...
>>> foo()
[1, 1, 1]
>>> foo()
[1, 1, 1, 1]
WHY IS THIS? Because default arguments to functions are evaluated once during function definition, and not at function run time. That way if you use a mutable value as a default argument value, then you will be stuck with that one value, mutating in unexpected ways as the function is called multiple times.
The proper way to do it is this:
>>> def foo(value=None):
... if value is None:
... value = []
... value.append(1)
... print(value)
...
...
>>>
>>> foo()
[1]
>>> foo()
[1]
>>> foo()
[1]

Retrieving list elements in Python [duplicate]

This question already has answers here:
Understanding slicing
(38 answers)
Closed 6 years ago.
I'm a beginner attempting to learn Python. I am familiarising myself with the list data type; I've defined the following list:
>>> numbers = [1, 2, 3, 4]
Typing:
>>> numbers[0]
1
>>> numbers[1]
2
>>> numbers[2]
3
>>> numbers[3]
4
Given this, why do I get the following when I attempt to retrieve the following list elements:
>>> numbers[0:3]
[1, 2, 3]
Why isn't the list element '4' included in the response from the interpreter?
Thank you for your help.
Slice notation does not include the last element (similar to the range() function in that respect). If you want to include the last element, simply omit an index. Also, the default start is the beginning, so you don't need 0 there either:
>>> numbers[:]
[1, 2, 3, 4]
Note that this is a (shallow) copy of numbers. If you save a reference to it, you can mutate it without affecting the original numbers.
That's how slicing works in Python. To quote a tutorial:
Note how the start is always included, and the end always excluded.
This makes sure that s[:i] + s[i:] is always equal to s.
The example uses a string, but slicing works the same way with lists.
numbers[0:3] list from 0 up to 3 but 3 is excluded (like range(0,3))

Recursive reference to a list within itself [duplicate]

This question already has answers here:
Why does list(my_list) modify the object?
(2 answers)
Closed 9 years ago.
So I came across something very weird in python. I tried adding a reference to the list to itself. The code might help demonstrate what I am saying better than I can express. I am using IDLE editor(interactive mode).
>>>l=[1,2,3]
>>>l.append(l)
>>>print(l)
[1,2,3,[...]]
>>>del l[:-1]
>>>print(l)
[[...]]
So far the output is as expected. But when I do this.
y=l[:]
print(y)
To me it seems that the output should be
[[...]]
But it is
[[[...]]]
Apparently instead of creating a copy of the list, it puts a reference to the list in y.
y[0] is l returns True. I can't seem to find a good explanation for this. Any ideas?
The difference is only in the way the list is displayed. I.e. the value of y is exactly what you'd expect.
The difference in the way the lists are displayed results from the fact that, unlike l, y is not a self-referencing list:
l[0] is l
=> True
y[0] is y
=> False
y is not self-referencing, because y does not reference y. It references l, which is self-referencing.
Therefor, the logic which translates the list to a string detects the potential infinite-recursion one level deeper when working on y, than on l.
This is perfectly expected. When Python prints recursive lists, it checks that the list it is printing hasn't yet been encountered and if it has prints [...]. An important point to understand is that it doesn't test for equality (as in ==) but for identity (as in is). Therefore,
when you print l = [l]. You have l[0] is l returns True and therefore it prints [[...]].
now y = l[:] makes a copy of l and therefore y is l returns False. So here is what happens. It starts printing y so it prints [ ??? ] where ??? is replaced by the printing of y[0]. Now y[0] is l and is not y. So it prints [[???]] with ??? replaced by y[0][0]. Now y[0][0] is l which has already been encountered. So it prints [...] for it giving finally [[[...]]].
You need to have a full copy of the objects. You need to use copy.deepcopy and you would see the expected results.
>>> from copy import deepcopy
>>> l=[1,2,3]
>>> l.append(l)
>>> print(l)
[1, 2, 3, [...]]
>>> del l[:-1]
>>> print(l)
[[...]]
>>> y=deepcopy(l)
>>> print(y)
[[...]]
>>> y[0] is l
False
>>>
When you use the slice notation to copy the list, the inner references are retained which cause the behavior that you observe.
Slicing generates list of items. There is only one item - list "l". So, we have new list of one element - list "l".

Categories

Resources