为什么变量=对象不像变量=数字那样工作

Gun*_*nnm 19 python variables object python-2.7 python-3.x

这些变量赋值按我的预期工作:

>>> a = 3
>>> b = a
>>> print(a, b)
(3, 3)
>>> b=4
>>> print(a, b)
(3, 4)
Run Code Online (Sandbox Code Playgroud)

但是,这些分配的行为有所不同:

>>> 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)
Run Code Online (Sandbox Code Playgroud)

与示例不同,为什么是c相同的?如何在类示例中执行类似的操作?也就是说,复制对象,然后更改它的一部分(这不会影响我借用属性的对象)?d(a, b)(a, b)(c, d)

Mak*_*oto 16

这些线:

c = number("one", 1)
d = c
Run Code Online (Sandbox Code Playgroud)

......有效:

  • 创建一个新实例number并将其分配给c
  • 将现有的引用分配c给新变量d

您没有更改或修改任何内容c; d是另一个指向同一实例的名称.

在不克隆实例或创建新实例的情况下,您无法执行与原始int的行为类似的任何操作.


为了纠正一些信息,上面的解释相当简单,其性质有点不完整,尽管它主要描述10,000英尺处发生的事情.

仔细看看,我们必须了解Python的变量或"名称",以及它们如何与此程序交互.

如上所述,您有"名称"和"绑定"的概念,这些概念非常简单:

a = 3
b = a
Run Code Online (Sandbox Code Playgroud)

在这种情况下,a是一个名称,并且b是一个绑定a.我们没有修改或改变任何事情a.

如前所述,Python中有两种类型的数据:可变和不可变.指向不可变数据的名称(例如基元和元组)可以重新分配,而不会对其上存在的任何其他绑定产生任何不良影响,因为没有状态在绑定方面发生变化.

这就是为什么这种重新分配能够实现我们所期望的:

print(a, b)
b = 4
print(a, b)
Run Code Online (Sandbox Code Playgroud)

的结果b = 4是,b现在指向的整数的一个新的副本,则该值4.

回想一下,我确实提到了元组作为不可变数据.您无法更改元组中特定实体的绑定...

t = ('foo', 'bar')
t[0] = 'baz' # illegal
Run Code Online (Sandbox Code Playgroud)

...但您可以将可变数据结构作为这些绑定的一部分.

t = ([1, 2, 3], 'bar')
t[0].append([4, 5, 6]) # ([1, 2, 3, [4, 5, 6]], 'bar')
Run Code Online (Sandbox Code Playgroud)

那么这就离开了我们的榜样?

c = number("one", 1)
d = c
Run Code Online (Sandbox Code Playgroud)

number是一个名为的可变类型c,它的值可以在多个不同的绑定之间随意更改c.

实际上,我们有一个名字和一个名字的绑定:

  • 我们有一个新的实例,number并通过名称引用它c.
  • 将引用绑定c到另一个名称d.

再一次,没有任何改变c,但它可以通过另一个名称引用.

与不可变数据不同,当我们重新分配值时d.number,我们将重新分配与之相关的相同绑定c:

>>> id(d.number)
36696408
>>> id(c.number)
36696408
Run Code Online (Sandbox Code Playgroud)

这就是您需要新实例或副本的原因.你必须引用一个不同的实例number.通过这种简单的绑定,您无法实现这一目标.

from copy import copy
c = number("one", 1)
d = copy(c)
id(c) # 140539175695784
id(d) # 140539175695856
Run Code Online (Sandbox Code Playgroud)

  • 这是正确的答案,但我想补充一点,Python并没有真正的变量.它们更像是标签.在第一种情况(a,b)中,a => 3,b => 3,然后b => 4,因此您要修改2个不同的标签.对于案例2(c,d),c和d是指向同一对象的两个不同标签,因此Object.number只是一个标签. (5认同)

200*_*ess 10

一张价值千言万语的图片

a = 3
b = a
c = number("one", 1)
d = c
Run Code Online (Sandbox Code Playgroud)

之前的块和指针图


第2步…

b = 4
d.number = 2
Run Code Online (Sandbox Code Playgroud)

块和指针图,之后

您可以看到为什么更改d.number也会影响c.


如果,在第2步之前,你做了

import copy
d = copy.copy(c)
Run Code Online (Sandbox Code Playgroud)

......然后cd是独立的.改变d.number不会影响c.

复制后的块和指针图


小智 9

我没有看到有人通过复制对象而不是仅仅为同一个对象分配新的引用来提供有关如何使这两个案例工作相同的细节.

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)
Run Code Online (Sandbox Code Playgroud)

这会给你:

1 1 1
2 2 5
Run Code Online (Sandbox Code Playgroud)


Bre*_*arn 7

你关注的是这两对线是相同的(都使用普通的=):

# one
a = 3
b = a

#two
c = number("one", 1)
d = c
Run Code Online (Sandbox Code Playgroud)

你缺少的是这两行不同:

# one
b = 4

# two
d.number = 2
Run Code Online (Sandbox Code Playgroud)

他们不一样的原因是它d.number有一个点,b但没有.

设置d = c 确实有作为settign同样的效果b = a.所不同的是,这样做d.number = 2一样的做b = 4.执行此操作时b = 4,将为该名称指定一个新对象b.执行此操作时d.number = 2,可以修改名称已引用的对象d,而无需指定新对象.如果将第二个示例更改为d = 2,使用普通赋值而不是属性赋值,您将看到它c不受影响,就像a在第一个示例中不受影响一样.

尽管它可能令人困惑,=但在Python的所有上下文中并不总是意味着同样的事情.分配裸名称(blah = ...)与分配属性(blah.attr = ...)或项目(blah[item] = ...)不同.要理解什么是=手段,您需要查看左侧(分配目标)以查看它是裸名还是某种表达式.

至于如何在c/d示例中获得a/b示例的效果,它取决于您想要的确切效果.如果您想结束dc指向具有不同number属性的不同对象,您可以执行以下操作:

d = number("one", 2)
Run Code Online (Sandbox Code Playgroud)

请注意,这现在与之并行b = 4(因为它使用了对裸名称的赋值).还有其他更复杂的解决方案涉及制作现有d对象的副本; 看看标准库中的复制模块.究竟该做什么取决于您尝试使用代码完成的任务.

  • @ kirbyfan64sos:不,不是.`a = b`总是将名称绑定到引用; 对象`a`无法控制此操作,事实上`a`甚至可能不存在.`a.attr = b`调用`a .__ setattr __('attr',b)`,这意味着`a`必须已经存在,整个操作由`a`控制.`a.attr = b`可以复制b,或变异b,或指定一个完全不同的值,或什么都不做,或删除你的硬盘,如果这是'a`决定做的.`a = b`只能将b绑定到名称`a`. (3认同)

kir*_*sos 5

在Python中,一切都是参考.以此为例,例如:

a = '1221398210p3'
b = a
print(id(a) == id(b)) # True
Run Code Online (Sandbox Code Playgroud)

现在,当你这样做:

b = 4
Run Code Online (Sandbox Code Playgroud)

你只是改变了数字b的引用.不是数字本身.现在,在后面的例子中:

c = number("one", 1)
d = c
...
d.number = 2
Run Code Online (Sandbox Code Playgroud)

d = c设置d以引用c.现在,当您设置时d.number,您将在c和d引用的对象上设置属性.就像我说的,一切都是参考.

在Python中,变量更像是标签(或者,在C术语中,指针):它们不是一个值.他们只是指出真正的价值.