第四章 面向对象编程--对象引用

第零章 学前准备
第一章 数据结构 – 基本数据类型
第一章 数据结构 – 字符串
第一章 数据结构 – 列表、元组和切片
第一章 数据结构 – 字典
第一章 数据结构 – 集合
第一章 – 数组、队列、枚举
第一章 数据结构 – 序列分类
第二章 控制流程
第三章 函数也是对象 – 函数定义以及参数
第三章 函数也是对象 – 高阶函数以及装饰器
第三章 函数也是对象 – 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 模块提供的 deepcopycopy 函数能为任意对象做深复制和浅复制。

# 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_twobus_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

猜你喜欢

转载自blog.csdn.net/qq_31654025/article/details/132781031