Why variable = object doesn't work like variable = number

旧巷老猫 提交于 2019-12-28 16:37:52

问题


These variable assignments work as I expect:

>>> a = 3
>>> b = a
>>> print(a, b)
(3, 3)
>>> b=4
>>> print(a, b)
(3, 4)

However, these assignments behave differently:

>>> class number():
...     def __init__(self, name, number):
...         self.name = name
...         self.number = number
... 
>>> c = number("one", 1)
>>> d = c
>>> print(c.number, d.number)
(1, 1)
>>> d.number = 2
>>> print(c.number, d.number)
(2, 2)

Why is c is same as d, unlike in (a, b) example? How can I do something like in (a, b) in (c, d) classes example? That is, copy the object and then change one part of it (that won't affect the object that I borrowed properties from)?


回答1:


I didn't see that anyone provided details on how to make these two cases work the same by copying the object instead of just assigning a new reference to the same object.

import copy
c = number("one", 1)
d = c
e = copy.copy(c)

print(c.number, d.number, e.number)
d.number = 2
e.number = 5
print(c.number, d.number, e.number)

This will give you:

1 1 1
2 2 5



回答2:


These lines:

c = number("one", 1)
d = c

...are effectively:

  • Create a new instance of number and assign it to c
  • Assign the existing reference called c to a new variable d

You haven't changed or modified anything about c; d is another name that points to the same instance.

Without cloning the instance or creating a new instance, you can't do anything similar to how the primitive int is behaving.


To correct a bit of information, the explanation above is rather simplified and a bit incomplete in its nature, although it mostly describes what's going on at 10,000 feet.

For a closer look, we have to realize a few things about Python's variables, or "names", and how they interact with this program.

As mentioned above, you have the notion of "names" and "bindings", which are pretty straightforward to reason at:

a = 3
b = a

In this context, a is a name, and b is a binding to a. We haven't modified or changed anything about a.

As noted before, there are two types of data in Python: mutable and immutable. A name that points to immutable data, such as primitives and tuples, can be reassigned without any ill effect to any other bindings present on it, because no state is changing with respect to the binding.

This is why this reassignment does what we would expect it to:

print(a, b)
b = 4
print(a, b)

The result of b = 4 is that b is now pointing at a new copy of an integer, the value 4.

Recall that I did mention tuples as immutable data. You can't change the binding of a particular entity in your tuple...

t = ('foo', 'bar')
t[0] = 'baz' # illegal

...but you can have mutable data structures as part of those bindings.

t = ([1, 2, 3], 'bar')
t[0].append([4, 5, 6]) # ([1, 2, 3, [4, 5, 6]], 'bar')

So where does that leave our example?

c = number("one", 1)
d = c

number is a mutable type which is named as c, and its values can be changed at will between multiple different bindings to c.

Effectively, we've got a name and a binding to a name:

  • We have a new instance of number and refer to it by the name c.
  • Bind the reference c to another name d.

Again, nothing's changed about c, but it can be referenced through another name.

Unlike with the immutable data, when we reassign the value of d.number, we're reassigning the same binding that c is aware of:

>>> id(d.number)
36696408
>>> id(c.number)
36696408

This is why you require either a new instance or a copy. You have to refer to a different instance of number. With this simple binding, you're not going to accomplish that.

from copy import copy
c = number("one", 1)
d = copy(c)
id(c) # 140539175695784
id(d) # 140539175695856



回答3:


A picture worth a thousand words

a = 3
b = a
c = number("one", 1)
d = c


Step 2…

b = 4
d.number = 2

You can see why changing d.number would also affect c.


If, before Step 2, you do

import copy
d = copy.copy(c)

… then c and d are independent. Changing d.number will not affect c.




回答4:


You are focusing on the fact that these two pairs of lines are the same (both use plain =):

# one
a = 3
b = a

#two
c = number("one", 1)
d = c

What you're missing is that these two lines are different:

# one
b = 4

# two
d.number = 2

The reason they aren't the same is that d.number has a dot in it, and b does not.

Setting d = c does have the same effect as settign b = a. The difference is that doing d.number = 2 is not the same as doing b = 4. When you do b = 4, you assign a new object to the name b. When you do d.number = 2, you modify the object that is already referred to by the name d, without assigning a new object. If you change your second example to d = 2, using plain assignment instead of attribute assignment, you will see that c is unaffected, just as a is unaffected in your first example.

Although it can be confusing, = does not always mean the same thing in all contexts in Python. Assigning to a bare name (blah = ...) is not the same as assigning to an attribute (blah.attr = ...) or an item (blah[item] = ...). To understand what an = means, you need to look at the left-hand side (the assignment target) to see whether it is a bare name or some sort of expression.

As for how to get the effect of your a/b example in your c/d example, it depends on exactly what effect you want. If you want to wind up with d and c pointing to different objects that have different number attributes, you could do:

d = number("one", 2)

Notice that this is now parallel to b = 4 (because it uses assignment to a bare name). There are also other more complex solutions involving making a copy of the existing d object; have a look at the copy module in the standard library. Exactly what to do depends on what you're trying to accomplish with the code.




回答5:


In Python, everything is a reference. Take this, for example:

a = '1221398210p3'
b = a
print(id(a) == id(b)) # True

Now, when you do this:

b = 4

you're just changing the number b references. Not the number itself. Now, in your later example:

c = number("one", 1)
d = c
...
d.number = 2

d = c sets d to reference c. Now, when you set d.number, you're setting the attribute on the object that c and d reference. Like I said, everything is a reference.

In Python, variables are more like labels (or, in C terms, pointers): they're not a value. They just point to the real value.




回答6:


The reason for this difference is immutable v. immutable objects - an int is an immutable object, your class is a mutable object.

In this example (similar to your first example), both x and y point the same immutable object (the integer, 1). That object can never change, so when you assign y = 2, y now points to a different immutable object (int(2)).

>>> x = 1  # x points to the object int(1).
>>> y = x  # So does y.
>>> assert x is y
>>> y = 2
>>> assert x is not y
>>>    

Your class is a mutable object, similar to a list. In this second example (similar to your second example), both x and y point to the same mutable object ([]). When you append 0 to that, it places it in the list that they both reference. When you assign [0] to y, you are assigning a different list to y, but that list happens to be equal to x, even though it's a different object.

>>> x = []
>>> y = x
>>> assert x is y  # x and y reference the same object.
>>> y.append(0)  # We append 0 to the list that both x and y reference.
>>> x
[0]
>>> y
[0]
>>>
>>> y = [0]  # Creating a new list object.
>>> assert x is not y  # x and y are different objects...
>>> assert x == y  # ...but they are equal.
>>>    



回答7:


"What's in a name? that which we call a rose

By any other name would smell as sweet;"

--- Act II, Scene II Romeo Juliet

In Python,

  • a variable is just a symbolic reference to an object or literal
  • an object is an instance of a type or class
  • unless explicitly enforced, all variables are copied by referenced (assignments, parameter passing and function return value)

Now let's understand your example

a and b refers to two different integer literals

a = 3
b = a

print(a, b)

b now refers to a new integer literal (4)

b=4

c refers to an object which is an instance of class number

c = number("one", 1)

d refers to the same object that c points to

d = c

Change the number attribute of the object referred by d. As d and c refers the same object, this also affects c.

d.number = 2



回答8:


Why is c same as d, unlike in (a, b) example?

In python the variable is not an object.
“Hamlet was not written by Shakespeare; it was merely written by a man named Shakespeare.” Python make a crucial distinction between a thing, and the label we use to refer to that thing. “The man named Shakespeare” is a man. “Shakespeare” is just a name. If we do:

l = []

then [] is the empty list. l is a variable that points to the empty list, but l itself is not the empty list.

So, in python variables work more like a tag. When you do an assignment in Python, it tags the value with the variable name.

a = 1

and if you change the value of the variable, it just changes the tag to the new value in memory.

Assigning one variable to another makes a new tag bound to the same value as show below.

b = a  

If you assign a new value 3 to b, then it will bound to that new value and a will still be bound to 2.

In second snippet, d = c will make both c and d to point to the same object. Modifying any object member will cause that member to point to the new value and not the name a and b. They will still point to the same object.

How can I do something like in (a, b) in (c, d) classes example? That is, copy the object and then change one part of it (that won't affect the object that I borrowed properties from)?

Python 2.7.10: 8.17. copy — Shallow and deep copy operations:

Assignment statements in Python do not copy objects, they create bindings between a target and an object. For collections that are mutable or contain mutable items, a copy is sometimes needed so one can change one copy without changing the other. This module provides generic shallow and deep copy operations (explained below).

Interface summary:

copy.copy(x)

Return a shallow copy of x.

copy.deepcopy(x)

Return a deep copy of x.

>>> import copy
>>> c = number("one", 1)
>>> d = copy.copy(c)        # Make a copy of object c  
>>> id(d), id(c)
(42898856, 43279384)  

Modifying any one of c or d object will not affect one another.


References:

  • Is Python pass-by-reference or pass-by-value?.
  • Understanding Python variables and Memory Management.



回答9:


Assigning an object reference to a variable is exactly like assigning a number to a variable. NEITHER of them involves copying anything. It's precisely the same in Python's intellectual ancestors Lisp and Smalltalk. It just happens that numbers cannot be mutated.

>>> x = 10**50
>>> y = 10**50
>>> z = x
>>> x is y
False
>>> x is z
True

We see here that Python discriminates between making a copy (x and y are equal but distinct) and not making a copy (x is z), and that assigning a number doesn't make a copy.




回答10:


This wasn't clarified in other answers, but if I recall correctly, integers in python are not actually primitives. They are immutable integer objects. So when you have multiple variables all holding the value 3, you actually have multiple references to the single instance of Integer that holds the value 3.

So with this in mind;

a = 3
b = a

Results in variables a and b each pointing to the Integer holding the value 3. Likewise,

c = number("one", 1)
d = c

Results in variables c and d each pointing to the same instance of number.

This same thing occurs with

b = 4

Now b is a reference to the Integer 4.

However, when d.number is set

d.number = 2

The member variable "number" of the instance of the class "number", which is pointed to by both c and d is updated to reference the Integer 2. The important concept here is that you are modifying a member variable of an object that is referenced by two separate variables.

Sources: http://www.laurentluce.com/posts/python-integer-objects-implementation/



来源:https://stackoverflow.com/questions/29926485/why-variable-object-doesnt-work-like-variable-number

易学教程内所有资源均来自网络或用户发布的内容,如有违反法律规定的内容欢迎反馈
该文章没有解决你所遇到的问题?点击提问,说说你的问题,让更多的人一起探讨吧!