翼度科技»论坛 编程开发 python 查看内容

Python中的弱引用与基础类型支持情况探究

9

主题

9

帖子

27

积分

新手上路

Rank: 1

积分
27
背景

最近有一个业务场景需要用Python自行实现一个简单的LRU cache,不可避免的接触到了弱引用这一概念,这里记录一下。
强引用

Python内存回收由垃圾回收器自动管理,当一个对象的引用计数归0时,其内存就可能被回收掉,而引用计数器的数值其实就是代表有多少个强引用指向该对象,我们日常写的Python代码如果没有使用到weakref模块一般都只会涉及到强引用。
可以通过sys.getrefcount查看对象的引用计数,如以下代码:
  1. import sys
  2. alist = [1, 2, 3] # alist引用计数=1
  3. print(sys.getrefcount(alist)) # 包括getrefcount本身新增的强引用,输出2
  4. blist = alist
  5. print(sys.getrefcount(alist)) # 新增blist强引用,输出3
  6. print(blist) # 输出[1, 2, 3]
  7. del blist
  8. print(sys.getrefcount(alist)) # 删除了blist,强引用-1, 输出2
复制代码
弱引用

与强引用相对,弱引用并不会影响对象的引用计数,也就是说其不影响对象是否被回收的判定,如以下代码:
  1. import sys
  2. import weakref
  3. class tlist(list): # list本身不支持弱引用,但其子类支持
  4.     pass
  5. alist = tlist([1, 2, 3]) # alist引用计数=1
  6. print(sys.getrefcount(alist)) # 输出2
  7. bref = weakref.ref(alist) # bref为对alist对象的弱引用
  8. print(bref()) # 返回弱引用对象,输出: [1, 2, 3]
  9. print(sys.getrefcount(alist)) # 由于弱引用不影响引用计数,依然输出2
  10. del alist # 删除alist,对象引用计数变为0
  11. print(bref()) # 由于bref指向的对象已无任何强引用,返回None
复制代码
如上代码所示弱引用不会影响对象的引用计数,亦即不会影响对象内存的回收,但是这里碰到一个引人疑惑的点,就是Python中的基本数据类型对弱引用的支持分了三种情况。
基础类型对于弱引用支持情况

基础类型int、list、dict、tuple、str不支持弱引用,对其执行弱引用会报错:
  1. ---------------------------------------------------------------------------
  2. TypeError                                 Traceback (most recent call last)
  3. <ipython-input-3-9daeb515714d> in <module>
  4. ----> 1 weakref.ref(alist)
  5. TypeError: cannot create weak reference to 'list' object
复制代码
可以通过__weakrefoffset__查看类型是否支持弱引用,该变量表示弱引用指针相对对象起始地址的偏移量,>0表示支持弱引用:
  1. In [1]: int.__weakrefoffset__
  2. Out[1]: 0
  3. In [2]: str.__weakrefoffset__
  4. Out[2]: 0
  5. In [3]: tuple.__weakrefoffset__
  6. Out[3]: 0
  7. In [4]: list.__weakrefoffset__
  8. Out[4]: 0
  9. In [5]: dict.__weakrefoffset__
  10. Out[5]: 0
  11. In [6]: set.__weakrefoffset__
  12. Out[6]: 192
复制代码
官方文档中介绍:
  1. Several built-in types such as list and dict do not directly support weak references but can add support through subclassing:
  2. CPython implementation detail: Other built-in types such as tuple and int do not support weak references even when subclassed.
复制代码
总结基础类型对弱引用的支持分为以下三种情况(for python3.8):

  • 对于list、dict、str本身不支持弱引用,但可以通过创建子类的方式对其进行弱引用
  • 对于int、tuple本身及其子类均不支持弱引用
  • set直接支持弱引用
这又是出于什么考虑?通过一番探究得出以下可能原因:

  • 绝大部分场景下,基础类型使用并不涉及到弱引用,所以基础类型不支持弱引用可以有效避免相应的overhead。
  • 弱引用添加于Python2.1,所以对于之后添加的类型(包括object、type、set等)默认都是支持弱引用的,除非有明确的理由不这么做。
  • 对于list、dict、int、str、tuple这些2.1之前的基础类型为了兼容性考虑均默认不支持弱引用,而set添加与2.3,因此其直接支持弱引用。
  • int、str、tuple这些不可变对象,在CPython解释器中会有特殊的处理逻辑:
    4.1 如[-5, 256]范围的小整数池一开始就被创建好了,在程序的整个生命周期无论是否被实际引用都不会被回收。
    4.2 又如对于同一个compilation unit的tuple对象,如果取值相同,编译器会将独立的多个相同的tuple对象处理为指向同一个对象的多个强引用。
    在这些情况下使用弱引用并没有什么明显的好处,反而额外引入了overhead,综合考虑直接对其不支持弱引用。
  • 出于CPython的具体实现细节,对于int、tuple的子类也不支持弱引用。
转载请注明出处,原文地址:https://www.cnblogs.com/AcAc-t/p/python_weakref_study.html
参考

https://docs.python.org/3.8/library/weakref.html
https://www.cnblogs.com/marsggbo/p/14831456.html
https://www.cnblogs.com/AcAc-t/p/python_weakref_study.html
https://stackoverflow.com/questions/52011430/python-which-types-support-weak-references

来源:https://www.cnblogs.com/AcAc-t/p/python_weakref_study.html
免责声明:由于采集信息均来自互联网,如果侵犯了您的权益,请联系我们【E-Mail:cb@itdo.tech】 我们会及时删除侵权内容,谢谢合作!

举报 回复 使用道具