Types for which the keyword is may be equivalent to the equality operator in Python

For some types in Python, the is operator seems equivalent to the == operator. For example:

 >>> 1 is 1 True >>> "a spoon" is "a spoon" True >>> (1 == 1) is (2 == 2) True 

However, this is not always the case:

 >>> [] == [] True >>> [] is [] False 

This makes sense for mutable types such as lists. However, immutable types, such as tuples, seem to be the same:

 >>> (1, 2) == (1, 2) True >>> (1, 2) is (1, 2) False 

This raises several questions:

  • Is == / is equivalence related to immutability?
  • Are the behaviors described above or implementation details?
  • Most importantly (and mostly), how can I find out if assignment will result in a copy of an object or a link to it?

Update : If the destination is always by reference, why is it not printing 2 ?:

 >>> a = 1 >>> b = a >>> a = 2 >>> b 1 

Why is this not equivalent to the following C snippet:

 int a = 1; int *b = &a; a = 2; printf("%d\n", *b); 

I apologize for the novelty of this question, but I am a Python beginner and feel that it is important to understand. Are there any readings that you would recommend to understand such problems?

+3
python immutability variable-assignment reference identity
source share
3 answers

The is operator checks to see if two objects are physically the same, which means that they have the same address in memory. This can also be checked using the id() function:

 >>> a = 1 >>> b = 1 >>> a is b True >>> id(a) == id(b) True 

The == operator, on the other hand, checks semantic equality. You can also override this with custom classes by implementing the __eq__() function. Semantically, two different lists are equal if their elements are equal, but physically they will be different objects.

Inevitable types, such as strings and tuples, can be combined by the Python implementation, so the two string object literals are actually physically identical. But this does not mean that you can always use is to compare these types, as shown in the following example:

 >>> "foobar" is "foobar" # The interpreter knows that the string literals are True # equal and creates only one shared object. >>> a = "foobar" >>> b = "foobar" >>> a is b # "foobar" comes from the pool, so it is still the same object. True >>> b = "foo" # Here, we construct another string "foobar" dynamically that is >>> b += "bar" # physically not the same as the pooled "foobar". >>> a == b True >>> a is b False 

Assignments in Python always associate an object reference with a variable name and never imply a copy.

UPDATE

Like C, think Python variables are always pointers:

 >>> a = 1 >>> b = a >>> a = 2 >>> b 1 

Roughly equivalent to:

 const int ONE = 1; const int TWO = 2; int *a = &ONE; int *b = a; /* b points to 1 */ a = &TWO; /* a points to 2, b still points to 1 */ 
+9
source share

Is equivalence == / equivalent to immutability?

Not.

See Python '== vs' comparing strings,' sometimes it doesn't work, why? about why it works with strings, and the Python "is" operator unexpectedly works with integers about why it works with integers (thus, bools for the same reason).

Are the behaviors described above or implementation details?

Implementation Details.

How can I find out if assignment will result in a copy of an object or a link to it?

The destination is always by reference. Copying is only performed if you explicitly use copy.copy (or something like that).

Edit: "by reference" I do not mean links in C ++. A Python assignment will recheck the variable. It looks more like

 // int* a, *b; a = new int(1); b = a; a = new int(2); printf("%d\n", *b); 
+7
source share

If you come from the background of C or C ++, then it is probably easier to rationalize that all variables in Python are really pointers. So statement

  a = 1 

really looks like

  Object *a = new Integer(1); 

The is operator verifies that the pointer is equal, and the == operator instead includes a calculation that depends on the type of objects.

A slight complication of this scheme is that if objects are immutable (for example, an integer), then for reasons of efficiency, the above code is really more like

  int *a = getFromCacheOrCreateNewInteger(1); 

therefore sometimes (but this is an implementation detail) immutable objects can be the same object for is , even if they are created regardless of the logical point of view (for example, it can be 1+1 is 2-1 , but without guarantees):

 >>> 1+2 is 2+1 True >>> 99999+1 is 1+99999 False >>> 

To add a little more confusion that even if really alla variables in Python are pointers, it is pretty surprising that there is no concept of a pointer in Python, in other words, there is no way to pass a function in which of your variables something should be stored.

To do this, you need to either pass the name (if the variable is global) or pass the setter function, which will be called (if the variable is local). This is not a big annoyance, as in most cases you just need a few return values, and this is handled well by Python:

 def foo(x): return x+1, x-1 a, b = foo(12) 

Another annoyance is that if you really need to pass the setter to a local variable without a name (like a list item), this cannot be anonymous lambda , because assignment is an expression and lambda only one expression is allowed . However, you can define local functions for this ...

 def foo(x, setter): setter(x + 1) def bar(): mylocal = [1,2,3] def setFirst(value): mylocal[0] = value foo(32, setFirst) 

(OK. I lied ... it is really possible to use lambda value: mylocal.__setitem__(0, value) , but this is a more or less undesirable incident; lambda so hateful in Python that, when they find out that it is possible, they will probably be added another restriction on the language to prohibit it ;-)).

If you want to change the named local, this is simply not possible in Python 2.x (but possibly with Python 3.x and nonlocal ).

About the question of when copies are made, and instead just copying the pointer, the answer is very simple. Python never makes a copy automatically ... if you want to make a copy, you must do it yourself explicitly. This is why, for example, code is often found, for example:

 class Polygon: def __init__(pointlist): self.pointlist = pointlist[:] 

The designation [:] here means that the class instance wants to keep a copy of the transferred list, so that when you create an instance of Polygon with a list of points and then modify this list then the geometry does not change.

+1
source share

All Articles