Is list's variable is an object in Python? - python

a = [1,2,3]
b = [1,2,3]
I understand a is b is false but i can't understand a is [1,2,3] is false
I learned variable is like nickname for objects like x = 2 and id(x) == id(2)
but id(a) is not as same as id(b)...
In this case, a is an object? not a variable?

Variables are references to objects. a does not reference the same object as b. Even though the two objects are the same they have unique addresses in memory and do not depend on each other.
>>> a = b = [1,2,3]
>>> c = [1,2,3]
>>> print(a is b)
True
>>> print(a is c or b is c)
False
>>> a.remove(1)
>>> print(a)
[2, 3]
>>> print(b)
[2, 3]
>>> print(c)
[1, 2, 3]
In the case of the x = 2 and id(x) == id(2) integers are immutable and in CPython id is simply the location of an object in memory. Integers are always the same so storing the same integer several times at different addresses would be a waste of memory.
However, in general DO NOT use integers with is operator as it can lead to different results across different python implementations.

Everything in Python is an object. a is b evaluates to false because, as you know, they are not the same object. They are both instances of the List class, therefore having different locations in memory, and being completely separate in every way except their class structure, or blueprint, as you can think of it. id(a) and id(b) will reflect this, they are not the same object and will therefore not share an ID. This is a reference to their location in memory, and while IDs are not referenced pointers, they are similar in that they describe their not being the same.
>>> a = [1, 2, 3]
>>> b = [1, 2, 3]
>>> id(a) == id(b)
False
>>> # the below is an example, every session will result in a different ID,
>>> # as it's being stored at a unique mem location each time
>>> id(a)
2770873780160
>>> id(b)
2770873412800
>>> id(a) - id(b) # simply to show the difference in location.
367360
double equals vs is in python
https://www.tutorialspoint.com/difference-between-and-is-operator-in-python

Related

Object id in Python

Let's assume I execute the following:
>>> id(a)
139926795932424
>>> a
[1, 2, 3, 4]
>>> a = a + [5]
>>> id(a)
Will the last line of this script print 139926795932424? Or a new id will be assigned to a?
Will the last line of this script print 139926795932424? Or a new id will be assigned to a?
A new id will be assigned to a. Why? Because a refers to a different object.
a = a + [5] is syntactic sugar for a = a.__add__([5]), and since a.__add__([5]) returns a new object, a holds a reference to the new object rather than the old object. This can be observed with a simple example:
>>> a = [1, 2]
>>> b = a # copy the reference in a to b
>>> id(a) == id(b) # they match
True
>>> a = a + [5] # syntatic sugar for a = a.__add__([5])
>>> id(a) == id(b) # they no longer match
False
>>>
Note that in that sense, a = a + [5] and a += [5] are not identical even though they produce the same result (a = [1, 2, 5]). The first example as said above, is syntactic sugar for a = a.__add__([5]). While the second is syntactic sugar for a = a.__iadd__([5]). The difference is that the latter method - __iadd__() - does not create a new object in memory. Rather, it modifies the existing list object, and returns a reference to it. Again like previously:
>>> a = [1, 2]
>>> b = a # copy the reference in a to b
>>> id(a) == id(b) # they match
True
>>> a += [5]
>>> id(a) == id(b) # they still match, `b` has mutated too!
True
>>> b
[1, 2, 5]
From the documentation:
id(object)
Return the “identity” of an object. This is an integer (or long integer) which is guaranteed to be unique and constant for this object during its lifetime. Two objects with non-overlapping lifetimes may have the same id() value.
CPython implementation detail: This is the address of the object in memory.
Since you assigned a new value to a, this new object will have another id.
See more details in this question.
>>> a= [1,2,3]
>>> a
[1, 2, 3]
>>> id(a)
39384488
>>> a + [5]
[1, 2, 3, 5]
>>> id(a)
39384488
No the id() does not change as you are not creating a new instance or lifetime of the variable. See the documentation for id().
EDIT:
The above example does not reassign the variable like in the question. In that scenario it would be a new variable as a = a + [5] returns a new object.
In Python, objects are given unique identification numbers that are “guaranteed to be unique and constant for the object during its lifetime.”
In your example, the line a = a + [5] is directly changing the list a and when we call the id() function on the list, we will see a different unique id number. This essentially creates a new list (new object!)
>>> a = [1, 2, 3, 4]
>>> id(a)
140250098481992
>>> a = a + [5]
>>> id(a)
140250098532296

python: why is id(x) != id(y) when x and y are lists with equal values?

Why is id(a) == id(b) while id(x) != id(y) in the example below:
>>> a = 9
>>> b = 9
>>> id(a)
10901176
>>> id (b)
10901176
>>> id(a) == id(b)
True
>>> x = [1, 2, 3]
>>> y = [1, 2, 3]
>>> id(x) != id(y)
True
>>> id(x)
11428848
>>> id(y)
12943768
From the python 3.6 doc I get for id() "Return the “identity” of an object. This is an integer which is guaranteed to be unique and constant for this object during its lifetime. Two objects with non-overlapping lifetimes may have the same id() value."... But that doesn't explain it to me. Any ideas?
the fact that a and b integers have the same id is just a storage optimization performed by python on immutable objects (which cannot be relied upon, ex: if the numbers are big enough, ids can be different)
Try to change the value of b and you'll see that id(b) changes.
Of course, it's different for lists: cannot benefit from storage optimization since they're mutable: you don't want x to be changed when you change y.

Two variables with the same list have different IDs.....why is that?

Trying to understand the following
Why is it that the ID's assigned by Python are different for the same lists?
x = [1, 2, 3]
y = [1, 2, 3]
id(x) != id(y)
True
id(x)
11428848
id(y)
12943768
Every distinct object in Python has its own ID. It's not related to the contents -- it's related to the location where the information that describes the object is stored. Any distinct object stored in a distinct place will have a distinct id. (It's sometimes, but not always, the memory address of the object.)
This is especially important to understand for mutable objects -- that is, objects that can be changed, like lists. If an object can be changed, then you can create two different objects with the same contents. They will have different IDs, and if you change one later, the second will not change.
For immutable objects like integers and strings, this is less important, because the contents can never change. Even if two immutable objects have different IDs, they are essentially identical if they have identical contents.
This set of ideas goes pretty deep. You can think of a variable name as a tag assigned to an ID number, which in turn uniquely identifies an object. Multiple variable names can be used to tag the same object. Observe:
>>> a = [1, 2, 3]
>>> b = [1, 2, 3]
>>> id(a)
4532949432
>>> id(b)
4533024888
That, you've already discovered. Now let's create a new variable name:
>>> c = b
>>> id(c)
4533024888
No new object has been created. The object tagged with b is now tagged with c as well. What happens when we change a?
>>> a[1] = 1000
>>> a
[1, 1000, 3]
>>> b
[1, 2, 3]
a and b are different, as we know because they have different IDs. So a change to one doesn't affect the other. But b and c are the same object -- remember? So...
>>> b[1] = 2000
>>> b
[1, 2000, 3]
>>> c
[1, 2000, 3]
Now, if I assign a new value to b, it doesn't change anything about the objects themselves -- just the way they're tagged:
>>> b = a
>>> a
[1, 1000, 3]
>>> b
[1, 1000, 3]
>>> c
[1, 2000, 3]
The why to that is that if you do that:
l = [1, 2, 3]
m = [1, 2, 3]
l.append(4)
Ids should not be the same and ids must not change for any objects since they identify them.
All mutable objects works this way. But it is also the case for tuples (which are unmutable).
Edit:
As commented below, the ids may refer to memory address in some python implementation but not in all.
Those aren't the same lists. They may contain identical information, but they are not the same. If you made y = x, you'd find that actually the id is the same.
Python keep the mutable variables with different IDs, that's why.
You can check it with immutable object ids too; a tuple, for example.

Python assigning multiple variables to same value? list behavior

I tried to use multiple assignment as show below to initialize variables, but I got confused by the behavior, I expect to reassign the values list separately, I mean b[0] and c[0] equal 0 as before.
a=b=c=[0,3,5]
a[0]=1
print(a)
print(b)
print(c)
Result is:
[1, 3, 5]
[1, 3, 5]
[1, 3, 5]
Is that correct? what should I use for multiple assignment?
what is different from this?
d=e=f=3
e=4
print('f:',f)
print('e:',e)
result:
('f:', 3)
('e:', 4)
If you're coming to Python from a language in the C/Java/etc. family, it may help you to stop thinking about a as a "variable", and start thinking of it as a "name".
a, b, and c aren't different variables with equal values; they're different names for the same identical value. Variables have types, identities, addresses, and all kinds of stuff like that.
Names don't have any of that. Values do, of course, and you can have lots of names for the same value.
If you give Notorious B.I.G. a hot dog,* Biggie Smalls and Chris Wallace have a hot dog. If you change the first element of a to 1, the first elements of b and c are 1.
If you want to know if two names are naming the same object, use the is operator:
>>> a=b=c=[0,3,5]
>>> a is b
True
You then ask:
what is different from this?
d=e=f=3
e=4
print('f:',f)
print('e:',e)
Here, you're rebinding the name e to the value 4. That doesn't affect the names d and f in any way.
In your previous version, you were assigning to a[0], not to a. So, from the point of view of a[0], you're rebinding a[0], but from the point of view of a, you're changing it in-place.
You can use the id function, which gives you some unique number representing the identity of an object, to see exactly which object is which even when is can't help:
>>> a=b=c=[0,3,5]
>>> id(a)
4473392520
>>> id(b)
4473392520
>>> id(a[0])
4297261120
>>> id(b[0])
4297261120
>>> a[0] = 1
>>> id(a)
4473392520
>>> id(b)
4473392520
>>> id(a[0])
4297261216
>>> id(b[0])
4297261216
Notice that a[0] has changed from 4297261120 to 4297261216—it's now a name for a different value. And b[0] is also now a name for that same new value. That's because a and b are still naming the same object.
Under the covers, a[0]=1 is actually calling a method on the list object. (It's equivalent to a.__setitem__(0, 1).) So, it's not really rebinding anything at all. It's like calling my_object.set_something(1). Sure, likely the object is rebinding an instance attribute in order to implement this method, but that's not what's important; what's important is that you're not assigning anything, you're just mutating the object. And it's the same with a[0]=1.
user570826 asked:
What if we have, a = b = c = 10
That's exactly the same situation as a = b = c = [1, 2, 3]: you have three names for the same value.
But in this case, the value is an int, and ints are immutable. In either case, you can rebind a to a different value (e.g., a = "Now I'm a string!"), but the won't affect the original value, which b and c will still be names for. The difference is that with a list, you can change the value [1, 2, 3] into [1, 2, 3, 4] by doing, e.g., a.append(4); since that's actually changing the value that b and c are names for, b will now b [1, 2, 3, 4]. There's no way to change the value 10 into anything else. 10 is 10 forever, just like Claudia the vampire is 5 forever (at least until she's replaced by Kirsten Dunst).
* Warning: Do not give Notorious B.I.G. a hot dog. Gangsta rap zombies should never be fed after midnight.
Cough cough
>>> a,b,c = (1,2,3)
>>> a
1
>>> b
2
>>> c
3
>>> a,b,c = ({'test':'a'},{'test':'b'},{'test':'c'})
>>> a
{'test': 'a'}
>>> b
{'test': 'b'}
>>> c
{'test': 'c'}
>>>
In python, everything is an object, also "simple" variables types (int, float, etc..).
When you changes a variable value, you actually changes it's pointer, and if you compares between two variables it's compares their pointers.
(To be clear, pointer is the address in physical computer memory where a variable is stored).
As a result, when you changes an inner variable value, you changes it's value in the memory and it's affects all the variables that point to this address.
For your example, when you do:
a = b = 5
This means that a and b points to the same address in memory that contains the value 5, but when you do:
a = 6
It's not affect b because a is now points to another memory location that contains 6 and b still points to the memory address that contains 5.
But, when you do:
a = b = [1,2,3]
a and b, again, points to the same location but the difference is that if you change the one of the list values:
a[0] = 2
It's changes the value of the memory that a is points on, but a is still points to the same address as b, and as a result, b changes as well.
Yes, that's the expected behavior. a, b and c are all set as labels for the same list. If you want three different lists, you need to assign them individually. You can either repeat the explicit list, or use one of the numerous ways to copy a list:
b = a[:] # this does a shallow copy, which is good enough for this case
import copy
c = copy.deepcopy(a) # this does a deep copy, which matters if the list contains mutable objects
Assignment statements in Python do not copy objects - they bind the name to an object, and an object can have as many labels as you set. In your first edit, changing a[0], you're updating one element of the single list that a, b, and c all refer to. In your second, changing e, you're switching e to be a label for a different object (4 instead of 3).
You can use id(name) to check if two names represent the same object:
>>> a = b = c = [0, 3, 5]
>>> print(id(a), id(b), id(c))
46268488 46268488 46268488
Lists are mutable; it means you can change the value in place without creating a new object. However, it depends on how you change the value:
>>> a[0] = 1
>>> print(id(a), id(b), id(c))
46268488 46268488 46268488
>>> print(a, b, c)
[1, 3, 5] [1, 3, 5] [1, 3, 5]
If you assign a new list to a, then its id will change, so it won't affect b and c's values:
>>> a = [1, 8, 5]
>>> print(id(a), id(b), id(c))
139423880 46268488 46268488
>>> print(a, b, c)
[1, 8, 5] [1, 3, 5] [1, 3, 5]
Integers are immutable, so you cannot change the value without creating a new object:
>>> x = y = z = 1
>>> print(id(x), id(y), id(z))
507081216 507081216 507081216
>>> x = 2
>>> print(id(x), id(y), id(z))
507081248 507081216 507081216
>>> print(x, y, z)
2 1 1
in your first example a = b = c = [1, 2, 3] you are really saying:
'a' is the same as 'b', is the same as 'c' and they are all [1, 2, 3]
If you want to set 'a' equal to 1, 'b' equal to '2' and 'c' equal to 3, try this:
a, b, c = [1, 2, 3]
print(a)
--> 1
print(b)
--> 2
print(c)
--> 3
Hope this helps!
What you need is this:
a, b, c = [0,3,5] # Unpack the list, now a, b, and c are ints
a = 1 # `a` did equal 0, not [0,3,5]
print(a)
print(b)
print(c)
Simply put, in the first case, you are assigning multiple names to a list. Only one copy of list is created in memory and all names refer to that location. So changing the list using any of the names will actually modify the list in memory.
In the second case, multiple copies of same value are created in memory. So each copy is independent of one another.
The code that does what I need could be this:
# test
aux=[[0 for n in range(3)] for i in range(4)]
print('aux:',aux)
# initialization
a,b,c,d=[[0 for n in range(3)] for i in range(4)]
# changing values
a[0]=1
d[2]=5
print('a:',a)
print('b:',b)
print('c:',c)
print('d:',d)
Result:
('aux:', [[0, 0, 0], [0, 0, 0], [0, 0, 0], [0, 0, 0]])
('a:', [1, 0, 0])
('b:', [0, 0, 0])
('c:', [0, 0, 0])
('d:', [0, 0, 5])
To assign multiple variables same value I prefer list
a, b, c = [10]*3#multiplying 3 because we have 3 variables
print(a, type(a), b, type(b), c, type(c))
output:
10 <class 'int'> 10 <class 'int'> 10 <class 'int'>
Initialize multiple objects:
import datetime
time1, time2, time3 = [datetime.datetime.now()]*3
print(time1)
print(time2)
print(time3)
output:
2022-02-25 11:52:59.064487
2022-02-25 11:52:59.064487
2022-02-25 11:52:59.064487
E.g: basically a = b = 10 means both a and b are pointing to 10 in the memory, you can test by id(a) and id(b) which comes out exactly equal to a is b as True.
is matches the memory location but not its value, however == matches the value.
let's suppose, you want to update the value of a from 10 to 5, since the memory location was pointing to the same memory location you will experience the value of b will also be pointing to 5 because of the initial declaration.
The conclusion is to use this only if you know the consequences otherwise simply use , separated assignment like a, b = 10, 10 and won't face the above-explained consequences on updating any of the values because of different memory locations.
The behavior is correct. However, all the variables will share the same reference. Please note the behavior below:
>>> a = b = c = [0,1,2]
>>> a
[0, 1, 2]
>>> b
[0, 1, 2]
>>> c
[0, 1, 2]
>>> a[0]=1000
>>> a
[1000, 1, 2]
>>> b
[1000, 1, 2]
>>> c
[1000, 1, 2]
So, yes, it is different in the sense that if you assign a, b and c differently on a separate line, changing one will not change the others.
Here are two codes for you to choose one:
a = b = c = [0, 3, 5]
a = [1, 3, 5]
print(a)
print(b)
print(c)
or
a = b = c = [0, 3, 5]
a = [1] + a[1:]
print(a)
print(b)
print(c)

Assigning a value to an element of a slice in Python

This is a simple question about how Python handles data and variables. I've done a lot of experimenting and have Python mostly figured out, except this keeps tripping me up:
[edit: I separated and rearranged the examples for clarity]
Example 1:
>>> a = [[1], 2]
>>> a[0:1]
[[1]]
>>> a[0:1] = [[5]]
>>> a
[[5], 2] # The assignment worked.
Example 2:
>>> a = [[1], 2]
>>> a[0:1][0]
[1]
>>> a[0:1][0] = [5]
>>> a
[[1], 2] # No change?
Example 3:
>>> a = [[1], 2]
>>> a[0:1][0][0]
1
>>> a[0:1][0][0] = 5
>>> a
[[5], 2] # Why now?
Can anybody explain to me what's going on here?
So far the answers seem to claim that a[0:1] returns a new list containing a reference to the first element of a. But I don't see how that explains Example 1.
a[0:1] is returning a new array which contains a reference to the array [1], thus you end up modifying the inner array via a reference call.
The reason the first case doesn't modify the [1] array is that you're assigning the copied outer array a new inner array value.
Bottom line - a[0:1] returns a copy of the data, but the inner data is not copied.
My understanding is slicing returns a new object. That is it's return value is a new list.
Hence you can not use an assignment operator to changes the values of the original list
>>> a = [[1], 2, 3]
>>> k = a[0:2]
>>> id(a)
4299352904
>>> id(k)
4299353552
>>>
>>> id(a)
4299352904
>>> id(a[0:2])
4299352832
some more plays along the lines
>>> k = 5
>>>
>>> id(k)
4298182344
>>> a[0] = [1,2]
>>> a
[[1, 2], 2, 3]
>>> id(a)
4299352904
>>>
[Edit: on second part of question]
>>> a[0:1] = [[5]]
The following notation is also called commonly as slice assignment
The behavior for builtin lists is atomic (delete + insert) happens in one go. My understanding is that this is not allowed for custom sequence.
There are three distinct operations with indices, all are translated to method calls:
a[i] = b => a.__setitem__(i, b)
del a[i] => a.__delitem__(i)
a[i] used as an expression => a.__getitem__(i)
Here a, b and i are expressions, and i can contain slice objects created using the colon shorthand syntax. E.g.:
>>> class C(object):
... def __setitem__(self, *a):
... print a
...
>>> C()[1] = 0
(1, 0)
>>> C()['foo'] = 0
('foo', 0)
>>> C()['foo':'bar'] = 0
(slice('foo', 'bar', None), 0)
>>> C()['foo':'bar',5] = 0
((slice('foo', 'bar', None), 5), 0)
So what's happening in your third example is this:
a[0:1][0][0] = 5
becomes
a.__getitem__(slice(0,1)).__getitem__(0).__setitem__(0, 5)
The first __getitem__ returns a copy of part of the list, but the second __getitem__ returns the actual list inside that, which is then modified using __setitem__.
Your second example on the other hand becomes
a.__getitem__(slice(0,1)).__setitem__(0, 5)
So __setitem__ is being called on the sliced copy, leaving the original list intact.

Categories