读书笔记:《流畅的Python》第8章 对象引用、可变性、垃圾回收

# 第八章 对象引用、可变性、垃圾回收

"""
对象名称和对象有区别:
    变量是标注,而不是盒子
内容提要:
    1.对象标识、值、别名
    2.元组是不可变的,但其中的值可以改变
    3.深浅复制
    4.引用和函数参数
        可变的默认值参数引起的问题
        如何安全地处理函数的调用者传入的可变参数
    5.垃圾回收
        del命令
        使用弱引用记住对象,而无需对象本身存在
"""

# 8.1变量不是盒子
"""Python中的变量类似于Java中的引用式变量
最好把他们理解为附加在对象上的标注
所以可以给一个对象贴上多个标注,这些标注,就是别名"""

# 例 8-1 变量a,b引用同一个列表,而不是列表的副本
"""a = [1,2,3]
b = a
a.append(4)
print(a)  # [1, 2, 3, 4]
print(b)  # [1, 2, 3, 4]"""

# 例 8-2 创建对象之后,才会把变量分配给对象
"""class Gizmo:
    def __init__(self):
        print('Gizmo id:%d'%id(self))
x = Gizmo()  # Gizmo id:1585308311504
# y = Gizmo()*10  # *之前会创建一个Gizmo对象
print(dir()) """

# 8.2标识/相等性/别名
"""
每个变量都有标识/类型/值
对象一旦创建,它的标识就不会改变,可以把标识理解为对象在内存中的地址
is运算符比较的是对象的标识
id()函数返回对象标识的整数表示
"""

# 例 8-3 charles 和 lewis指代同一个对象
"""charles = {'name' : 'charles L.Dodgson','born':1832}
lewis = charles  # lewis就是一个别名
print(charles is lewis) # True
print(id(lewis), id(charles))  # 2100165622272 2100165622272
lewis['balance'] = 950
print(charles)  # {'name': 'charles L.Dodgson', 'born': 1832, 'balance': 950}

# 例 8-4 alex 与charles 比较的结果相等,但是alex不是charles
alex = {'name': 'charles L.Dodgson', 'born': 1832, 'balance': 950}  # 内容一样
print(alex == charles)  # True
print(alex is not charles)  # True"""

# 8.2.1 == 和 is 的区别
"""
== 比较两个对象的值
is 比较两个对象的标识
最常使用is检查变量绑定的值是不是None
    x is None
    x is not None
    """

"""a = 1
b = a
print(a is b)
b = 1
print(a is b)  # 注意此处a,b是同一个对象 是因为python对-5 ~ 255?的整数所做的优化 不新建对象
print(a == b)

c = 2000
d = 2000
print(c is d) #pycharm True  但Terminal python3.9 结果是False  还有一些简单字符串也有这个特性
"""

# 8.2.2元组的相对不可变性
"""
元组和多数python集合(列表.字典.集合等等)一样
    保存的是对象的引用
str/bytes/array.array等扁平序列
    保存的是数据本身

如果元组引用的元素是可变的,即便元组本身不可变,元素依然可变
"""

# 例 8-5 元组的值会随着引用的可变对象的变化而改变,元组中不可变的是对象的标识
"""t1 = (1,2,[30,40])
t2 = (1,2,[30,40])
print(t1 is t2)  # False
print(t1 == t2)  # True
print(id(t1[-1]))  # 2464676277312
t1[-1].append(99)
print(t1)
print(id(t1[-1]))  # 2761212980288
print(t1 == t2)  # False"""

# 8.3 默认做浅复制
# 使用内置的类型构造方法浅复制
# l1 = [3,[55,44],(7,8,9)]
# l2 = list(l1)
# print(l2)
# print(l2 == l1)
# print(l2 is l1)
"""
>>>[3, [55, 44], (7, 8, 9)]
>>>True
>>>False"""

# 还可以使用[:]来做浅复制

# 例 8-6 为一个包含另一个列表的列表做浅复制
"""l1 = [3,[55,44],(7,8,9)]
l2 = list(l1)
l1.append(100)
l1[1].remove(55)
print('l1:',l1)
print('l2:',l2)
l2[1] += [33,22]
l2[2] += (10,11)
print('l1:',l1)
print('l2:',l2)"""

# 例8-8校车乘客在途中上车下车
"""class Bus:
    def __init__(self,passengers = None):
        if passengers is None:
            self.passengers = []
        else:
            self.passengers = list(passengers)
    def pick(self,name):
        self.passengers.append(name)
    def drop(self,name):
        self.passengers.remove(name)
if __name__ == '__main__':
    import copy
    bus1 = Bus(['Alice','Bill','Claire','David'])
    bus2 = copy.copy(bus1)
    bus3 = copy.deepcopy(bus1)
    print(id(bus1), id(bus2), id(bus3),sep='\n')
    bus1.drop('Bill')
    print(bus2.passengers)
    print(id(bus1.passengers))
    print(id(bus2.passengers))
    print(id(bus3.passengers))
    print(bus3.passengers)"""

"""
1583367671760
1583367671568
1583367670608
['Alice', 'Claire', 'David']
1583367678080
1583367678080
1583367657856
['Alice', 'Bill', 'Claire', 'David']
bus2 和 bus1 的passengers是同一个对象
但是bus3是作的深复制,所以不受影响"""

# 例8-10 循环引用
"""a = [10,20]
b = [a,30]
a.append(b)
print(a)
# >>>[10, 20, [[...], 30]]

from copy import deepcopy
c = deepcopy(a)
print(c)
"""

# 8.4函数的参数作为引用时
"""
Python的参数传递方式:共享传参(call by sharing)
    共享传参:函数的各个形式参数获得实参中各个引用的副本
            也就是说,函数内部的形参是实参的别名
            结果是函数可能会修改作为参数传入的可变对象
"""
# 例 8-11 函数可能会修改接收到的任何可变对象
"""def f(a,b):
    a += b
    return a

x = 1
y = 2
print(f(x,y))  # 3
print(x,y)  # 1 2

a = [1,2]
b = [3,4]
print(f(a,b))  # [1, 2, 3, 4]
print(a,b) # [1, 2, 3, 4] [3, 4]  a被修改了

t = (10,20)
u = (30,40)
print(f(t,u))  # (10, 20, 30, 40)
print(t,u)  # (10, 20) (30, 40)
"""

# 8.4.1不要使用可变类型作为参数的默认值
"""class HaunteBus:
    '''备受幽林折磨的校车'''
    def __init__(self,passengers = []):  # 如果没有传入passengers参数,使用默认绑定的列表对象
        self.passengers = passengers  # self.passengers是passengers的一个别名
    def pick(self,name):
        self.passengers.append(name)
    def drop(self,name):
        self.passengers.remove(name)

bus1 = HaunteBus(['Alice','Bill'])
print(bus1.passengers)
bus1.pick('Charles')
bus1.drop('Alice')
print(bus1.passengers)

bus2 = HaunteBus()
bus2.pick('Carrie')
print(bus2.passengers)

bus3 = HaunteBus()
print(bus3.passengers)  # ['Carrie']
bus3.pick('Dave')
print(bus2.passengers)  # ['Carrie', 'Dave']

print(bus2.passengers is bus3.passengers)  # True
print(bus1.passengers)

print(dir(HaunteBus.__init__))
print(dir(HaunteBus.__init__.__defaults__))
"""

# 8.4.2 防御可变参数

# 例 8-14,8-15
"""class TwilightBus:
    def __init__(self, passengers=None):
        if passengers is None:
            self.passengers = []
        else:
            self.passengers = passengers  # 此处把self.passengers变成passengers的一个别名

    def pick(self, name):
        self.passengers.append(name)
    def drop(self, name):
        self.passengers.remove(name)
# 从TwilightBus下车后乘客消失了

basketball_team = ['Sue','Tina','Maya','Diana','Pat']
bus = TwilightBus(basketball_team)
bus.drop('Sue')
bus.drop('Pat')
print(basketball_team)  # ['Tina', 'Maya', 'Diana']basketball_team被修改了

# TwilightBus违反了设计接口的最佳实践,"最小惊讶原则"
# 解决的方法是 校车维护自己的乘客列表 self.passengers = list(passengers)
"""

# 8.5 del和垃圾回收
"""
del语句删除名称,而不是对象
当删除的变量保存的是对象的最后一个引用或者无法得到对象时,对象会被当做垃圾回收
重新绑定也可能导致对象的引用数量归零导致对象被销毁
cpython中垃圾回收算法:引用计数"""

# 例 8-16没有指向对象的引用时,监视对象生命结束的情形
"""import weakref
s1= {1,2,3}
s2 = s1
def bye(): # 这个函数一定不能是被销毁对象的绑定方法,否则会有一个指向对象的引用
    print('Gone  with the wind...')
ender = weakref.finalize(s1,bye)  # 调用finalize前,.alive属性为True
print(ender.alive)
del s1  # 删除对对象的引用
print(ender.alive)
s2 = 'spam'  # 重新绑定s2,让{1,2,3}无法获取,对象被销毁,调用bye回调.ender.alive结果为False
print(ender.alive)
"""

# 8.6 弱引用
"""
正是因为有引用,对象才会在内存中存在
但是有时需要引用对象,而不让对象存在的时间超过所需时间,这经常用在缓存中
弱引用不会增加对象的引用数量
引用的目标对象称为所指对象
弱引用不会妨碍所指对象被当做垃圾回收
"""

# 例8-17 弱引用是可调用的对象,返回被引用的对象,如果所指对象不存在了,返回None
"""import weakref
a_set = {0,1}
wref = weakref.ref(a_set) # 创建弱引用对象
print(wref)  # <weakref at 0x0000019575AD9E00; to 'set' at 0x0000019575B362E0>

print(wref())  # {0, 1}

a_set = {2,3,4}
print(wref()) # None"""

# 8.6.1 WeakValueDictionary简介
"""WeakValueDictionary类实现的是一种可变的映射,里面的值是对象的弱引用
被引用的对象在其他地方被当做垃圾回收以后,对应的键会自动从WeakValueDictionary中删除
WeakValueDictionary经常用于缓存"""
# weakreference.py

# 8.6.2弱引用的局限
"""
1.不是每个python对象都可以作为弱引用的目标
    基本的list和dict实例不能作为所指对象
    但是他们的子类可以轻松地解决这个问题
2.set实例可以作为所指对象
3.int和tuple实例不能作为所指对象,甚至他们的子类也不行"""

import weakref
class Mylist(list):
    '''list的子类,实例可以作为弱引用的对象'''
a_list = Mylist(range(10))
# a_list可以作为弱引用的目标
wref_to_a_list = weakref.ref(a_list)

# 8.7 Python对不可变类型施加的把戏
# 对于元组t来说,t[:]和tuple(t)返回的都是同一个元组的引用
# 例 8-20 使用另一个元组构建元组,得到的其实是同一个元组

"""t1 = (1,2,3)
t2 = tuple(t1)
print(t2 is t1)  # True
t3 = t1[:]
print(t3 is t1)  # True"""

# str/byets/frozenset实例也有这种行为
# 例 8-21 字符串字面量可能会创建共享对象
t1 = (1,2,3)
t3 = (1,2,3)
print(t1 is t3)  # pycharm True
s1 = 'abc'
s2 = 'abc'
print(s1 is s2)  # True

# 这是cpython的优化措施,称为'驻留',这种情况也适用于一些整数,有的地方也称为'小数锯池'.主要是为了节省内存

# 本章小结
"""1.对象都有标识/类型/值,只有对象的值会不时变化
2.变量保存的是引用
3.简单的赋值,不创建副本
    对于+=,*=等增量赋值来说,如果左边的变量绑定的是不可变对象,会创建新对象
    否则,会就地修改
4.函数的参数是以别名的形式传递
    使用可变类型作为函数参数的默认值有危险
5.del 只删除引用,而不真正销毁对象,对象只有引用为0时才会被垃圾回收
6.弱引用"""


weakreference.py
# 若引用模块weakref的应用

# 实现一个简单的类,表示各种奶酪
# 例8-18 cheese有个kind属性和标准的字符串表示形式
class Cheese:
    def __init__(self,kind):
        self.kind = kind

    def __repr__(self):
        return 'Cheese(%r)'%self.kind

"""
在catalog中的各种奶酪载入WeakValueDictionary实现的栈stock中
删除catalog后,stock中只剩下一种奶酪了"""

# 例 8-19 顾客:你们店里到底有没有奶酪
import weakref
stock = weakref.WeakValueDictionary()
catalog = [Cheese('Red Leicester'),Cheese('Tilsit'),
           Cheese('Brie'),Cheese('Parmesan')]

for cheese in catalog:
    # stock把奶酪的名称映射到catalog中Cheese实例的弱引用上
    stock[cheese.kind] = cheese

print(sorted(stock.keys()))  # stock的内容是完整的
# >>>['Brie', 'Parmesan', 'Red Leicester', 'Tilsit']
del catalog  # 删除catalog后
print(sorted(stock.keys())) # stock中的大部分奶酪不见了,为什么不是全部呢?
# for循环中的临时变量cheese变量引用了Parmesan所以Parmesan会被保留
# >>>['Parmesan']
del cheese  # 显式删除cheese
print(sorted(stock.keys()))
# >>>[]

"""
与WeakValueDictionary对应的是WeakKeyDictionary
WeakKeyDictionary的键是弱引用
一些可能的应用:
    可以为应用中其他部分拥有的对象附加数据
    这样就无需为对象添加属性
    这对覆盖属性访问权限的对象尤其有用
WeakSet类:
    保存元素弱引用的集合类,元素没有强引用时,集合会把他们删除"""

  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值