第零章 学前准备
第一章 数据结构 – 基本数据类型
第一章 数据结构 – 字符串
第一章 数据结构 – 列表、元组和切片
第一章 数据结构 – 字典
第一章 数据结构 – 集合
第一章 – 数组、队列、枚举
第一章 数据结构 – 序列分类
第二章 控制流程
第三章 函数也是对象 – 函数定义以及参数
第三章 函数也是对象 – 高阶函数以及装饰器
第三章 函数也是对象 – lambda 表达式、可调用函数及内置函数
第四章 面向对象编程 – 自定义类、属性、方法和函数
第四章 面向对象编程–魔术方法1
第四章 面向对象编程 – 魔术方法2
第四章 面向对象编程 – 可迭代的对象、迭代器和生成器
第四章 面向对象编程 – 继承、接口
第四章 面向对象编程 – 对象引用
文章目录
第四章 面向对象编程 – 对象引用
4.9 对象引用
4.9.1 引用式变量
对于引用式变量,变量是附加在对象的标注,而不是装对象的盒子。每个变量都有标识", "类型和值。对象一旦创建,它的标识绝不会变;你可以把标识理解为对象在内存中的地址。 is
运算符比较两个对象的标识; id()
函数返回对象标识的整数表示。
==
运算符比较两个对象的值(对象中保存的数据),而 is
比较对象的标识。通常,我们关注的是值,而不是标识,因此 Python
代码中 ==
出现的频率比 is
高。
然而,在变量和单例值之间比较时,应该使用 is
。目前,最常使用 is
检查变量绑定的值是不是 None
。
test_list = [1, 2, 3]
test_list_second = [1, 2, 3]
print("test_list ==test_list_second", test_list == test_list_second)
print("test_list is test_list_second", test_list is test_list_second)
print(test_list is None)
test_list ==test_list_second True
test_list is test_list_second False
False
4.9.2 默认浅复制
在复制中,无论是使用类型构造函数还是复制运算符,默认做的都是浅复制,即复制了最外层容器,副本中的元素是源容器中元素的引用。
浅复制没什么问题,但有时我们需要的是深复制(即副本不共享内部对象的引用)。 copy
模块提供的 deepcopy
和 copy
函数能为任意对象做深复制和浅复制。
# 1", "浅复制
from copy import deepcopy
test_list = [1, 2, [1, 2]]
copy_test_list = list(test_list)
print(test_list == copy_test_list, test_list is copy_test_list,
test_list[-1] is copy_test_list[-1])
# 1.1 修改test_list中列表对象的值
test_list[-1].append(3)
# 1.2 影响了copy_test_list中的值
print(copy_test_list[-1])
# 2", "使用copy.deepcopy的深度复制
test_list = [1, 2, [1, 2]]
copy_test_list = deepcopy(test_list)
print(test_list == copy_test_list, test_list is copy_test_list,
test_list[-1] is copy_test_list[-1])
# 2.1 修改test_list中列表对象的值
test_list[-1].append(3)
# 2.2 不影响copy_test_list中的值
print(copy_test_list[-1], test_list[-1])
True False True
[1, 2, 3]
True False False
[1, 2] [1, 2, 3]
4.9.3 可变类型参数使用请注意
4.9.3.1 不要使用可变类型参数作为参数的默认值
可选参数可以有默认值,这是Python函数定义的一个很棒的特性,这样我们的API在进化的同时能保证向后兼容。然而,我们应该避免使用可变的对象作为参数的默认值。如下所示。
因为 bus_two
和 bus_three
指代同一个列表, bus_one
是不同的列表。
问题在于,没有指定初始乘客的HauntedBus实例会共享同一个乘客列表。
出现这个问题的根源是,默认值在定义函数时计算(通常在加载模块时),因此默认值变成了函数对象的属性。因此,如果默认值是可变对象,而且修改了它的值,那么后续的函数调用都会受到影响。
class HauntedBus:
def __init__(self, passengers=[]):
self.passengers = passengers
def pick(self, name):
self.passengers.append(name)
def drop(self, name):
self.passengers.remove(name)
bus_one = HauntedBus(["Alice", "Bill"])
print("bus_one.passengers", bus_one.passengers)
bus_two = HauntedBus()
bus_two.pick("Carrie")
print("bus_two.passengers", bus_two.passengers)
bus_three = HauntedBus()
print("bus_three.passengers", bus_three.passengers)
bus_three.pick("Dave")
print("bus_two.passengers", bus_two.passengers)
bus_one.passengers ['Alice', 'Bill']
bus_two.passengers ['Carrie']
bus_three.passengers ['Carrie']
bus_two.passengers ['Carrie', 'Dave']
4.9.3.2 防御可变参数
如果定义的函数接收可变参数,应该谨慎考虑调用方是否期望修改传入的参数。
class TwilightBus:
def __init__(self, passagers=None):
if passagers is None:
self.passagers = []
else:
# 这里应该修改为:self.passagers = list(passagers),让每个类维护自己的乘客列表
self.passagers = passagers
def pick(self, name):
self.passagers.append(name)
def drop(self, name):
self.passagers.remove(name)
# 篮球队有五名队员
basketball_team = ['Sue', 'Tina', 'Maya', 'Diana', 'Pat']
# 队员都上车了
bus = TwilightBus(basketball_team)
# Sue下车了
bus.drop('Sue')
# Tine下车了
bus.drop('Tina')
# 篮球队员消失了
print(basketball_team)
['Maya', 'Diana', 'Pat']
4.9.4 弱引用
TODO