问题
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 toc
- Assign the existing reference called
c
to a new variabled
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 namec
. - Bind the reference
c
to another named
.
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