人们经常使用“变量是盒子”这样的比喻,但是这有碍于理解面对对象语言中的引用式变量,Python 变量类似于 Java 中的引用式变量,因此最好把变量理解为附加在对象上的标注,或者是贴在对象上的便利贴。
示例 8-1 变量 a 和 b 引用同一个列表,而不是那个列表的副本
a = [1, 2, 3]
b = a
a.append(4)
print(b) # [1, 2, 3, 4]
每个变量都有标识、类型和值。对象一旦创建,它的标识绝不会变,你可以把标识理解为对象在内存中的地址, is 运算符比较两个对象的标识;内置函数 id() 可以查看对象的标识。
鲁迅是周树人的笔名,二者是同一人,示例 8-2 用 Python 来表达这个关系;
示例 8-2 鲁迅和周树人指向同一个对象
zhou = {'name': '周树人', 'born': '1881'}
lu = zhou
print(id(lu)) # 4447626584
print(id(zhou)) # 4447626584
print(lu is zhou) # True
lu['sex'] = '男'
print(zhou) # {'name': '周树人', 'born': '1881', 'sex': '男'}
lu 是 zhou 的别名, lu 增加一个元素就相当于 zhou 添加了一个元素;
然而有一个人冒充者 x,声称他是周树人,并且仿冒了周树人的证件(但实际上并不是):
zhou = {'name': '周树人', 'born': '1881', 'sex': '男'}
x = {'name': '周树人', 'born': '1881', 'sex': '男'}
print(x == zhou) # True
print(x is zhou) # False
我们用 == 检查他俩的证件发现一模一样,但是用 is 比较发现他们并不是同一个人,这是因为 == 是调用 dict 的 __eq__ 方法来比较他俩的值,很明显他们值是相等的,而 is 是比较他们的标识,在 Python 这个标识就是对象的内存地址;
元组与多数 Python 集合(列表、字典、集)一样,保存的是对象的引用。如果元组引用的元素对象是可变的,即使元组不可变,该元素可以可变的。
示例:元组的值随着引用的可变对象的变化而变,元组中不可变的是元素的标识,我们利用 list.append 就地修改列表,不更改它的标识:
t1 = (1, 2, [30, 40])
print(id(t1[-1])) # 4482544008
t1[-1].append(50)
print(id(t1[-1])) # 4482544008
print(t1) # (1, 2, [30, 40, 50])