1 #《流畅的Python》读书笔记
  2 # 第四部分 面向对象惯用法
  3 # 第8章 对象引用、可变性和垃圾回收
  4
  5 # 8.1 变量不是盒子
  6 # Python 变量类似于 Java 中的引用式变量,因此最好把它们理解为附加在对象上的标注。
  7
  8 # 示例 8-1 变量 a 和 b 引用同一个列表,而不是那个列表的副本
  9 # 对引用式变量来说,说把变量分配给对象更合理,反过来说就有问题。
 10 # >>> a=[1,2,3]
 11 # >>> b=a
 12 # >>> b.append(4)
 13 # >>> a
 14 # [1, 2, 3, 4]
 15
 16 # 示例 8-2 创建对象之后才会把变量分配给对象
 17 # 为了理解 Python 中的赋值语句,应该始终先读右边。
 18 #     对象在右边创建或获取,在此之后左边的变量才会绑定到对象上,这就像为对象贴上标注。
 19 # 因为变量只不过是标注,所以无法阻止为对象贴上多个标注。贴的多个标注,就是别名。
 20 # >>> x=Gizmo()
 21 # ❶ 输出的 Gizmo id: ... 是创建 Gizmo 实例的副作用。
 22 # Gizmo id: 34079216
 23 # ❷ 在乘法运算中使用 Gizmo 实例会抛出异常。
 24 # >>> y=Gizmo() * 10
 25 # ❸ 这里表明,在尝试求积之前其实会创建一个新的 Gizmo 实例。
 26 # Gizmo id: 35171312
 27 # ❹ 但是,肯定不会创建变量 y,因为在对赋值语句的右边进行求值时抛出了异常。
 28 # Traceback (most recent call last):
 29 #   File "<pyshell#9>", line 1, in <module>
 30 #     y=Gizmo() * 10
 31 # TypeError: unsupported operand type(s) for *: 'Gizmo' and 'int'
 32 # >>> dir()
 33 # ['Gizmo', '__annotations__', '__builtins__', '__doc__', '__loader__', '__name__', '__package__', '__spec__', 'a', 'b', 'x']
 34
 35 # 8.2 标识、相等性和别名
 36 # Lewis Carroll 是 Charles Lutwidge Dodgson 教授的笔名。Carroll 先生指的就是 Dodgson 教授,二者是同一个人。
 37
 38 # 示例 8-3 charles 和 lewis 指代同一个对象
 39 # >>> charles={'name':'Charles L. Dodgson','born':1832}
 40 # >>> lewis=charles
 41 # >>> lewis is charles
 42 # True
 43 # >>> id(charles),id(lewis)
 44 # (34927120, 34927120)
 45 # >>> lewis['balance']=950
 46 # >>> charles
 47 # {'name': 'Charles L. Dodgson', 'born': 1832, 'balance': 950}
 48
 49 # 示例 8-4 alex 与 charles 比较的结果是相等,但 alex 不是charles
 50 # 在那段代码中,lewis 和 charles 是别名,即两个变量绑定同一个对象。
 51 #     而 alex 不是 charles 的别名,因为二者绑定的是不同的对象。
 52 #     alex 和 charles 绑定的对象具有相同的值(== 比较的就是值),但是它们的标识不同。
 53 # 每个变量都有标识、类型和值。
 54 #     对象一旦创建,它的标识绝不会变;你可以把标识理解为对象在内存中的地址。
 55 #     is 运算符比较两个对象的标识;id() 函数返回对象标识的整数表示。
 56 # >>> alex={'name': 'Charles L. Dodgson', 'born': 1832, 'balance': 950}
 57 # ❷ 比较两个对象,结果相等,这是因为 dict 类的 __eq__ 方法就是这样实现的。
 58 # >>> alex  == charles
 59 # True
 60 # ❸ 但它们是不同的对象。这是 Python 说明标识不同的方式:a is notb。
 61 # >>> alex is not charles
 62 # True
 63
 64 # 8.2.1 在==和is之间选择
 65 # == 运算符比较两个对象的值(对象中保存的数据),而 is 比较对象的标识。
 66 # 通常,我们关注的是值,而不是标识,因此 Python 代码中 == 出现的频率比 is 高。
 67
 68 # 8.2.2 元组的相对不可变性
 69 # 元组与多数 Python 集合(列表、字典、集,等等)一样,保存的是对象的引用。
 70 # 如果引用的元素是可变的,即便元组本身不可变,元素依然、可变。
 71
 72 # 示例 8-5 一开始,t1 和 t2 相等,但是修改 t1 中的一个可变元素后,二者不相等了
 73 # ❶ t1 不可变,但是 t1[-1] 可变。
 74 # >>> t1=(1,2,[30,40])
 75 # ❷ 构建元组 t2,它的元素与 t1 一样。
 76 # >>> t2=(1,2,[30,40])
 77 # ❸ 虽然 t1 和 t2 是不同的对象,但是二者相等——与预期相符。
 78 # >>> t1 == t2
 79 # True
 80 # ❹ 查看 t1[-1] 列表的标识。
 81 # >>> id(t1[-1])
 82 # 33289600
 83 # ❺ 就地修改 t1[-1] 列表。
 84 # >>> t1[-1].append(99)
 85 # >>> t1
 86 # (1, 2, [30, 40, 99])
 87 # ❻ t1[-1] 的标识没变,只是值变了。
 88 # >>> id(t1[-1])
 89 # 33289600
 90 # ❼ 现在,t1 和 t2 不相等。
 91 # >>> t1 == t2
 92 # False
 93
 94 # 8.3 默认做浅复制
 95 # 复制列表(或多数内置的可变集合)最简单的方式是使用内置的类型构造方法。
 96 # >>> l1=[3,[55,44],(7,8,9)]
 97 # ❶ list(l1) 创建 l1 的副本。
 98 # >>> l2=list(l1)
 99 # >>> l2
100 # [3, [55, 44], (7, 8, 9)]
101 # ❷ 副本与源列表相等。
102 # >>> l2 == l1
103 # True
104 # ❸ 但是二者指代不同的对象。对列表和其他可变序列来说,还能使用简洁的 l2 = l1[:] 语句创建副本。
105 # >>> l1 is l2
106 # False
107
108 # 示例 8-6 为一个包含另一个列表的列表做浅复制;把这段代码复制粘贴到 Python Tutor 网站中,看看动画效果
109 # >>> l1=[3,[66,55,44],(7,8,9)]
110 # ❶ l2 是 l1 的浅复制副本。
111 # >>> l2=list(l1)
112 # ❷ 把 100 追加到 l1 中,对 l2 没有影响。
113 # >>> l1.append(100)
114 # ❸ 把内部列表 l1[1] 中的 55 删除。这对 l2 有影响,因为 l2[1] 绑定的列表与 l1[1] 是同一个。
115 # >>> l1[1].remove(55)
116 # >>> print('li',l1)
117 # li [3, [66, 44], (7, 8, 9), 100]
118 # >>> print('l2',l2)
119 # l2 [3, [66, 44], (7, 8, 9)]
120 # >>> l2[1] += [33,22]
121 # >>> l2[2] += (10,11)
122 # >>> print('l1',l1)
123 # l1 [3, [66, 44, 33, 22], (7, 8, 9), 100]
124 # >>> print('l2',l2)
125 # l2 [3, [66, 44, 33, 22], (7, 8, 9, 10, 11)]
126
127 # 示例 8-8 校车乘客在途中上车和下车
128 class Bus:
129     def __init__(self,passengers):
130         if passengers is None:
131             self.passengers=[]
132         else:
133             self.passengers=list(passengers)
134     def pick(self,name):
135         self.passengers.append(name)
136     def drop(self,name):
137         self.passengers.remove(name)
138
139 # 示例 8-9 使用 copy 和 deepcopy 产生的影响
140 # >>> import copy
141 # >>> bus1=Bus(['Alice','Bill','Claire','David'])
142 # >>> bus2=copy.copy(bus1)
143 # >>> bus3=copy.deepcopy(bus1)
144 # >>> id(bus1),id(bsu2),id(bus3)
145 # Traceback (most recent call last):
146 #   File "<pyshell#44>", line 1, in <module>
147 #     id(bus1),id(bsu2),id(bus3)
148 # NameError: name 'bsu2' is not defined
149 # >>> id(bus1),id(bus2),id(bus3)
150 # (35175888, 35175856, 37550768)
151 # ❷ bus1中的'Bill'下车后,bus2中也没有他了。
152 # >>> bus2.passengers
153 # ['Alice', 'Claire', 'David']
154 # >>> id(bus1.passengers),id(bus2.passengers),id(bus3.passengers)
155 # ❸ 审查passengers属性后发现,bus1和bus2共享同一个列表对象,因为bus2是bus1的浅复制副本。
156 # (37562848, 37562848, 37609760)
157 # ❹ bus3是bus1的深复制副本,因此它的passengers属性指代另一个列表。
158 # >>> bus3.passengers
159 # ['Alice', 'Bill', 'Claire', 'David']
160
161 # 示例 8-10 循环引用:b 引用 a,然后追加到 a 中;deepcopy 会想办法复制 a
162 # >>> a=[10,20]
163 # >>> b=[a,30]
164 # >>> a.append(b)
165 # >>> a
166 # [10, 20, [[...], 30]]
167 # >>> from copy import deepcopy
168 # >>> c=deepcopy(a)
169 # >>> c
170 # [10, 20, [[...], 30]]
171
172 # 8.4 函数的参数作为引用时
173 # Python 唯一支持的参数传递模式是共享传参(call by sharing)。
174 # 共享传参指函数的各个形式参数获得实参中各个引用的副本。
175 #     也就是说,函数内部的形参是实参的别名。
176 # >>> def f(a,b):
177 #     a += b
178 #     return a
179 # >>> x=1
180 # >>> y=2
181 # >>> f(x,y)
182 # 3
183 # ❶ 数字x没变。
184 # >>> x,y
185 # (1, 2)
186 # >>> a=[1,2]
187 # >>> b=[3,4]
188 # >>> f(a,b)
189 # [1, 2, 3, 4]
190 # ❷ 列表a变了。
191 # >>> a,b
192 # ([1, 2, 3, 4], [3, 4])
193 # >>> t=(10,20)
194 # >>> u=(30,40)
195 # >>> f(t,u)
196 # (10, 20, 30, 40)
197 # >>> t,u
198 # ❸ 元组t没变。
199 # ((10, 20), (30, 40))
200
201 # 8.4.1 不要使用可变类型作为参数的默认值
202 # 可选参数可以有默认值,这是 Python 函数定义的一个很棒的特性,这样我们的 API 在进化的同时能保证向后兼容。
203 #     然而,我们应该避免使用可变的对象作为参数的默认值。
204
205 # 示例 8-12 一个简单的类,说明可变默认值的危险
206 class HauntedBus:
207     """备受幽灵乘客折磨的校车"""
208     # ❶ 如果没传入passengers参数,使用默认绑定的列表对象,一开始是空列表。
209     def __init__(self,passengers=[]):
210         self.passengers = passengers
211     def pick(self,name):
212         self.passengers.append(name)
213     def drop(self,name):
214         self.passengers.remove(name)
215
216 # 示例 8-13 备受幽灵乘客折磨的校车
217 # >>> bus1=HauntedBus(['Alice','Bill'])
218 # >>> bus1.passengers
219 # ['Alice', 'Bill']
220 # >>> bus1.pick('Charles')
221 # >>> bus1.drop('Alice')
222 # ❶ 目前没什么问题,bus1 没有出现异常。
223 # >>> bus1.passengers
224 # ['Bill', 'Charles']
225 # ❷ 一开始,bus2 是空的,因此把默认的空列表赋值给self.passengers。
226 # >>> bus2=HauntedBus()
227 # >>> bus2.pick('Carrie')
228 # >>> bus2.passengers
229 # ['Carrie']
230 # ❸ bus3 一开始也是空的,因此还是赋值默认的列表。
231 # >>> bus3=HauntedBus()
232 # ❹ 但是默认列表不为空!
233 # >>> bus3.passengers
234 # ['Carrie']
235 # ❺ 登上 bus3 的 Dave 出现在 bus2 中。
236 # >>> bus3.pick('Dave')
237 # >>> bus2.passengers
238 # ['Carrie', 'Dave']
239 # ❻ 问题是,bus2.passengers 和 bus3.passengers 指代同一个列表。
240 # >>> bus2.passengers is bus3.passengers
241 # True
242 # ❼ 但 bus1.passengers 是不同的列表。
243 # >>> bus1.passengers
244 # ['Bill', 'Charles']
245
246 # 可以审查 HauntedBus.__init__ 对象,看看它的 __defaults__ 属性中的那些幽灵学生:
247 # >>> dir(HauntedBus.__init__)
248 # ['__annotations__', '__call__', ...,'__defaults__',...]
249 # >>> HauntedBus.__init__.__defaults__
250 # (['Carrie', 'Dave'],)
251 # 最后,我们可以验证 bus2.passengers 是一个别名,它绑定到HauntedBus.__init__.__defaults__ 属性的第一个元素上:
252 # >>> HauntedBus.__init__.__defaults__[0] is bus2.passengers
253 # True
254
255 # 8.4.2 防御可变参数
256 # 如果定义的函数接收可变参数,应该谨慎考虑调用方是否期望修改传入的参数。
257
258 # 示例 8-15 一个简单的类,说明接受可变参数的风险
259 class TwilightBus:
260     """让乘客销声匿迹的校车"""
261     def __init__(self,passengers=None):
262         if passengers is None:
263             # ❶ 这里谨慎处理,当passengers为None时,创建一个新的空列表。
264             self.passengers = []
265         else:
266             self.passengers = passengers
267     def pick(self,name):
268         self.passengers.append(name)
269     def drop(self,name):
270         self.passengers.remove(name)
271
272 # 示例8 - 14 从TwilightBus下车后,乘客消失了
273 # ❶ basketball_team 中有 5 个学生的名字。
274 # >>> basketball_team=['Sue','Tina','Maya','Diana','pat']
275 # ❷ 使用这队学生实例化 TwilightBus。
276 # >>> bus=TwilightBus(basketball_team)
277 # ❸ 一个学生从 bus 下车了,接着又有一个学生下车了。
278 # >>> bus.drop('Tina')
279 # >>> bus.drop('pat')
280 # ❹ 下车的学生从篮球队中消失了!
281 # >>> basketball_team
282 # ['Sue', 'Maya', 'Diana']
283
284 # 8.5 del和垃圾回收
285 # 对象绝不会自行销毁;然而,无法得到对象时,可能会被当作垃圾回收。
286 # del 语句删除名称,而不是对象。
287 #     del 命令可能会导致对象被当作垃圾回收,但是仅当删除的变量保存的是对象的最后一个引用,或者无法得到对象时。
288 #     重新绑定也可能会导致对象的引用数量归零,导致对象被销毁。
289
290 # 示例 8-16 没有指向对象的引用时,监视对象生命结束时的情形
291 # >>> import weakref
292 # >>> s1={1,2,3}
293 # ❶ s1 和 s2 是别名,指向同一个集合,{1, 2, 3}。
294 # >>> s2=s1
295 # ❷ 这个函数一定不能是要销毁的对象的绑定方法,否则会有一个指向对象的引用。
296 # >>> def bye():
297 #     print('Gone    with the wind...')
298 # ❸ 在 s1 引用的对象上注册 bye 回调。
299 # >>> ender=weakref.finalize(s1,bye)
300 # ❹ 调用 finalize 对象之前,.alive 属性的值为 True。
301 # >>> ender.alive
302 # True
303 # >>> del s1
304 # ❺ 如前所述,del 不删除对象,而是删除对象的引用。
305 # >>> ender.alive
306 # True
307 # ❻ 重新绑定最后一个引用 s2,让 {1, 2, 3} 无法获取。对象被销毁了,调用了 bye 回调,ender.alive 的值变成了 False。
308 # >>> s2='spam'
309 # Gone    with the wind...
310 # >>> ender.alive
311 # False
312
313 # 8.6 弱引用
314 # 正是因为有引用,对象才会在内存中存在。当对象的引用数量归零后,垃圾回收程序会把对象销毁。
315 #     但是,有时需要引用对象,而不让对象存在的时间超过所需时间。这经常用在缓存中。
316
317 # 示例 8-17 弱引用是可调用的对象,返回的是被引用的对象;如果所指对象不存在了,返回 None
318 # >>> import weakref
319 # >>> a_set={0,1}
320 # ❶ 创建弱引用对象 wref,下一行审查它。
321 # >>> wref=weakref.ref(a_set)
322 # >>> wref
323 # <weakref at 0x023D19C0; to 'set' at 0x02185558>
324 # ❷ 调用wref()返回的是被引用的对象,{0, 1}。因为这是控制台会话,所以{0, 1}会绑定给_变量。
325 # >>> wref()
326 # {0, 1}
327 # ❸ a_set 不再指代 {0, 1} 集合,因此集合的引用数量减少了。但是 _变量仍然指代它。
328 # >>> a_set={2,3,4}
329 # ❹ 调用 wref() 依旧返回 {0, 1}。
330 # >>> wref()
331 # {0, 1}
332 # ❺ 计算这个表达式时,{0, 1} 存在,因此 wref() 不是 None。
333 #     但是,随后 _ 绑定到结果值 False。现在 {0, 1} 没有强引用了。
334 # >>> wref() is None
335 # ❻ 因为 {0, 1} 对象不存在了,所以 wref() 返回 None。
336 # False
337 # >>> wref() is None
338 # True
339
340 # 8.6.1 WeakValueDictionary简介
341 # WeakValueDictionary 类实现的是一种可变映射,里面的值是对象的弱引用。
342 #     被引用的对象在程序中的其他地方被当作垃圾回收后,对应的键会自动从 WeakValueDictionary 中删除。
343 #     因此,WeakValueDictionary 经常用于缓存。
344 class Cheese:
345     def __init__(self,kind):
346         self.kind=kind
347     def __repr__(self):
348         return 'Cheese(%r)'%self.kind
349
350 # 示例 8-19 顾客:“你们店里到底有没有奶酪?”
351 >>> import weakref
352 >>> stock = weakref.WeakValueDictionary()
353 >>> catalog=[Cheese('red Leicester'),Cheese('Tilsit'),Cheese('Brie'),Cheese('Parmesan')]
354 >>> for cheese in catalog:
355     stock[cheese.kind]=cheese
356 >>> sorted(stock.keys())
357 ['Brie', 'Parmesan', 'Tilsit', 'red Leicester']
358 >>> del catalog
359 >>> sorted(stock.keys())
360 ['Parmesan']
361 >>> del cheese
362 >>> sorted(stock.keys())
363 []
364
365 # 8.6.2 弱引用的局限
366 # 不是每个 Python 对象都可以作为弱引用的目标(或称所指对象)。基本的 list 和 dict 实例不能作为所指对象,但是它们的子类可以轻松地解决这个问题:
367
368 # 8.7 Python对不可变类型施加的把戏
369 # 你可以放心跳过本节。这里讨论的是 Python 的实现细节,对Python 用户来说没那么重要。
370
371 # 8.8 本章小结
372 # 每个 Python 对象都有标识、类型和值。只有对象的值会不时变化。
373
374 # 8.9 延伸阅读

转载于:https://www.cnblogs.com/larken/p/10575844.html

第8章 对象引用、可变性和垃圾回收相关推荐

  1. 流畅的python 对象引用 可变性和垃圾回收

    对象引用.可变性和垃圾回收 变量不是盒子 人们经常使用"变量是盒子"这样的比喻,但是这有碍于理解面向对象语言中的引用式变量.Python 变量类似于 Java 中的引用式变量,因此 ...

  2. 《Fluent Python》学习笔记:第 8 章 对象引用、可变性和垃圾回收

    本文主要是 Fluent Python 第 8 章的学习笔记.这部分主要是介绍了变量.引用.对象.深拷贝.浅拷贝.垃圾回收等.本章虽然枯燥,但是非常有用. <Fluent Python>学 ...

  3. 第八章《对象引用、可变性和垃圾回收》(下)

    对<流畅的python>的读书笔记以及个人理解 9-20 接着上一篇的内容,上一篇的链接:https://blog.csdn.net/scrence/article/details/100 ...

  4. Fluent_Python_Part4面向对象,08-ob-ref,对象引用、可变性和垃圾回收

    第四部分第8章,对象引用.可变性和垃圾回收 1. 创建对象之后才会把变量分配给对象 变量是对象的标注,是对象的别名,是对象的引用,并不是对象存储的地方. 例子1. 证明赋值语句的右边先执行 class ...

  5. [流畅的Python][8][对象引用、可变性和垃圾回收]

    第8章 对象引用.可变性和垃圾回收 "你不开心,"白骑士用一种忧虑的声调说,"让我给你唱一首歌安 慰你吧--这首歌的曲名叫作 :<黑线鳕的眼睛>." ...

  6. 流畅的Python读书笔记-第八章-对象引用、可变性和垃圾回收

    第8章:对象引用,可变性和垃圾回收 在Python里面变量不是盒子,而是便利贴,类似于Java中的引用变量,因此最好把它们理解为附加在对象上的标注. 因为变量不过是标注,因此无法阻止为对象贴上多个标注 ...

  7. CHAR.VIII 对象引用、可变性和垃圾回收

    CHAR.VIII 对象引用.可变性和垃圾回收 本章讨论对象标识.值和别名等概念.随后,本章会揭露元组的一个神奇特性:元组是不可变的,但是其中的值是可变的,之后就引申到浅复制和深复制.接下来的话题是引 ...

  8. 记录学习《流畅的python》的一些知识-----对象引用,可变性和垃圾回收

    记录我学习<流畅的python>的过程--对象引用,可变性和垃圾回收 2021.9.22 1.变量不是盒子 2.标识.相等性和别名 3.默认做浅复制 4.函数的参数作为引用时 5.del和 ...

  9. 对象引用、可变性和垃圾回收

    对象引用.可变性和垃圾回收 变量不是盒子 变量不是盒子,而是便利贴,python是先创建对象然后再将变量赋值给对象,当创建对象之后,可以通过id查看对象的内存地址. 例如: class Gizmo:d ...

最新文章

  1. 深度学习Pytorch框架Tensor张量
  2. linux insight 使用教程,Insight API开源项目分析
  3. 记忆模糊的知识点5-17
  4. Linux下I/O多路转接之select --fd_set
  5. python 高阶函数之 reduce
  6. Spring Bean 后置处理器
  7. 【LDPC系列1】基于MATLAB中LDPC编译码器对象的图像传输通信系统仿真
  8. Jupyter Tool
  9. [SCOI2016]背单词
  10. 成语答题小程序 开源的成语答题小程序
  11. 顺序结构—— 华氏温度转摄氏温度
  12. 微生物组-扩增子16S分析和可视化(2022.10)
  13. windows保护无法启动修复服务器,win10系统使用“sfc /scannow”修复系统提示Windows资源保护无法启动修复服务怎么办...
  14. linux奶瓶U盘使用方法,如何用U盘启动盘奶瓶解除无线WPA加密
  15. Powershell / windows终端 无法加载文件 因为在此系统上禁止运行脚本。有关详细信息,请参阅 https:/go.microsoft.com/fwlink/?LinkID=135170
  16. 关于this.name=name的理解
  17. Java图片处理 - 创建工具类
  18. win7/win8卸载matlab时提示 bummer -uninstller error exeption calling main
  19. GRE写作重要词汇应用技巧
  20. 【Java】判断是否是IE浏览器

热门文章

  1. 以太坊区块链入门之实现简单DApp开发
  2. Android驱动-霍尔摁键hall_sensor的实现
  3. 82---Python 动态绘制阻尼振荡
  4. 用计算机弹童年,你的童年电脑记忆里有它们吗?瑞星小狮子、Office曲别针、三维弹球……...
  5. win10计算机服务打不开,windows10设置打不开怎么办_win10设置功能打不开解决方法...
  6. 当你的工作==你热爱的事情,是一种什么体验?
  7. 项目管理中的三大误区
  8. 2014 WOT全球软件技术峰会深圳站
  9. 深入理解操作系统实验——bomb lab(phase_1)
  10. Spring+SpringMVC+Mybatis分布式敏捷开发系统架构(附源码)