有这个代码:
# assignment behaviour for integer
a = b = 0
print a, b # prints 0 0
a = 4
print a, b # prints 4 0 - different!
# assignment behaviour for class object
class Klasa:
def __init__(self, num):
self.num = num
a = Klasa(2)
b = a
print a.num, b.num # prints 2 2
a.num = 3
print a.num, b.num # prints 3 3 - the same!
Run Code Online (Sandbox Code Playgroud)
问题:
Nou*_*him 11
这是许多Python用户的绊脚石.对象引用语义与C程序员习惯的不同.
我们来看第一个案例.当你说a = b = 0,int创建一个带有值的新对象,并创建0两个对它的引用(一个是a另一个b).这两个变量指向同一个对象(我们创建的整数).现在,我们跑了a = 4.创建一个新int的有价值对象,4并a使其指向该对象.这意味着,引用的数量4是1,引用的数量0减少了一个.
将此与a = 4在C中进行比较,其中a写入"指向" 的存储区域.a = b = 4在C中意味着4写入两个内存 - 一个用于a,另一个用于b.
现在第二种情况,a = Klass(2)创建一个类型的对象Klass,将其引用计数递增1并a指向它.b = a简单地得到什么a指向,b指向同一个东西,并将事物的引用计数递增一.这和你做的一样a = b = Klass(2).由于您要取消引用相同的对象并打印属性值a.num,b.num因此尝试打印并且是相同的.您可以使用id内置函数来查看该对象是相同的(id(a)并且id(b)将返回相同的标识符).现在,您可以通过为其中一个属性赋值来更改对象.因为a并b指向同一个对象,所以当通过a或访问对象时,您希望值的变化可见b.这就是它的确切方式.
现在,回答你的问题.
引自数据模型
对象是Python的数据抽象.Python程序中的所有数据都由对象或对象之间的关系表示.(从某种意义上说,并且与Von Neumann的"存储程序计算机"模型一致,代码也由对象表示.)
从Python的角度来看,基本数据类型与C/C++有根本的不同.它用于将C/C++数据类型映射到Python.所以让我们暂时离开讨论,并考虑所有数据都是对象并且是某些类的表现.每个对象都有一个ID(有点像地址),Value和Type.
所有对象都通过引用复制.对于前者
>>> x=20
>>> y=x
>>> id(x)==id(y)
True
>>>
Run Code Online (Sandbox Code Playgroud)
拥有新实例的唯一方法是创建一个实例.
>>> x=3
>>> id(x)==id(y)
False
>>> x==y
False
Run Code Online (Sandbox Code Playgroud)
这可能听起来很复杂,但为了简化一点,Python使一些类型不可变.例如,您无法更改string.您必须将其切片并创建一个新的字符串对象.
通常通过引用复制会给出意外的结果.
x=[[0]*8]*8可能会让你感觉它创建了一个二维的0s 列表.但实际上它创建了一个相同列表对象[0]的引用列表.所以做x [1] [1]最终会同时改变所有重复的实例.
该复制模块提供了一个名为deepcopy的创建对象,而不是浅实例的新实例方法.当您打算拥有两个不同的对象并按照您在第二个示例中的预期单独操作时,这非常有用.
扩展你的例子
>>> class Klasa:
def __init__(self, num):
self.num = num
>>> a = Klasa(2)
>>> b = copy.deepcopy(a)
>>> print a.num, b.num # prints 2 2
2 2
>>> a.num = 3
>>> print a.num, b.num # prints 3 3 - different!
3 2
Run Code Online (Sandbox Code Playgroud)