Loading [MathJax]/jax/output/CommonHTML/config.js
前往小程序,Get更优阅读体验!
立即前往
首页
学习
活动
专区
圈层
工具
发布
首页
学习
活动
专区
圈层
工具
MCP广场
社区首页 >专栏 >Python 弱引用的使用

Python 弱引用的使用

作者头像
用户2936342
发布于 2018-08-27 06:55:32
发布于 2018-08-27 06:55:32
1.5K00
代码可运行
举报
文章被收录于专栏:nummynummy
运行总次数:0
代码可运行

和许多其它的高级语言一样,Python使用了垃圾回收器来自动销毁那些不再使用的对象。每个对象都有一个引用计数,当这个引用计数为0时Python能够安全地销毁这个对象。

引用计数会记录给定对象的引用个数,并在引用个数为零时收集该对象。由于一次仅能有一个对象被回收,引用计数无法回收循环引用的对象。

一组相互引用的对象若没有被其它对象直接引用,并且不可访问,则会永久存活下来。一个应用程序如果持续地产生这种不可访问的对象群组,就会发生内存泄漏。

在对象群组内部使用弱引用(即不会在引用计数中被计数的引用)有时能避免出现引用环,因此弱引用可用于解决循环引用的问题。

在计算机程序设计中,弱引用,与强引用相对,是指不能确保其引用的对象不会被垃圾回收器回收的引用。一个对象若只被弱引用所引用,则可能在任何时刻被回收。弱引用的主要作用就是减少循环引用,减少内存中不必要的对象存在的数量。

使用weakref模块,你可以创建到对象的弱引用,Python在对象的引用计数为0或只存在对象的弱引用时将回收这个对象。

创建弱引用

你可以通过调用weakref模块的ref(obj[,callback])来创建一个弱引用,obj是你想弱引用的对象,callback是一个可选的函数,当因没有引用导致Python要销毁这个对象时调用。回调函数callback要求单个参数(弱引用的对象)。

一旦你有了一个对象的弱引用,你就能通过调用弱引用来获取被弱引用的对象。

代码语言:javascript
代码运行次数:0
运行
AI代码解释
复制
>>>> import sys
>>> import weakref
>>> class Man:
  def __init__(self,name):
    print self.name = name
    
>>> o = Man('Jim')
>>> sys.getrefcount(o)   
2
>>> r = weakref.ref(o) # 创建一个弱引用
>>> sys.getrefcount(o) # 引用计数并没有改变
2
>>> r
<weakref at 00D3B3F0; to 'instance' at 00D37A30> # 弱引用所指向的对象信息
>>> o2 = r() # 获取弱引用所指向的对象
>>> o is o2
True
>>> sys.getrefcount(o)
3
>>> o = None
>>> o2 = None
>>> r # 当对象引用计数为零时,弱引用失效。
<weakref at 00D3B3F0; dead>de>

上面的代码中,我们使用sys包中的getrefcount()来查看某个对象的引用计数。需要注意的是,当使用某个引用作为参数,传递给getrefcount()时,参数实际上创建了一个临时的引用。因此,getrefcount()所得到的结果,会比期望的多1。

一旦没有了对这个对象的其它的引用,调用弱引用将返回None,因为Python已经销毁了这个对象。 注意:大部分的对象不能通过弱引用来访问。

weakref模块中的getweakrefcount(obj)和getweakrefs(obj)分别返回弱引用数和关于所给对象的引用列表。

弱引用对于创建对象(这些对象很费资源)的缓存是有用的。

创建代理对象

代理对象是弱引用对象,它们的行为就像它们所引用的对象,这就便于你不必首先调用弱引用来访问背后的对象。通过weakref模块的proxy(obj[,callback])函数来创建代理对象。使用代理对象就如同使用对象本身一样:

代码语言:javascript
代码运行次数:0
运行
AI代码解释
复制
import weakref
class Man:
 def __init__(self, name):
       self.name = name
 
def callback(self):
 print "callback"
 
o = Man('Jim')
p = weakref.proxy(o, callback)
p.test()
o = None

callback参数的目的和ref函数相同。在Python删除了一个引用的对象之后,使用代理将会导致一个weakref.ReferenceError错误。

循环引用

前面说过,使用弱引用,可以解决循环引用不能被垃圾回收的问题。 首先我们看下常规的循环引用,先创建一个简单的Graph类,然后创建三个Graph实例:

代码语言:javascript
代码运行次数:0
运行
AI代码解释
复制
# -*- coding:utf-8 -*-
import weakref
import gc
from pprint import pprint


class Graph(object):
    def __init__(self, name):
        self.name = name
        self.other = None

    def set_next(self, other):
        print "%s.set_next(%r)" % (self.name, other)
        self.other = other

    def all_nodes(self):
        yield self
        n = self.other
        while n and n.name !=self.name:
            yield n
            n = n.other
        if n is self:
            yield n
        return

    def __str__(self):
        return "->".join(n.name for n in self.all_nodes())

    def __repr__(self):
        return "<%s at 0x%x name=%s>" % (self.__class__.__name__, id(self), self.name)

    def __del__(self):
        print "(Deleting %s)" % self.name

def collect_and_show_garbage():
    print "Collecting..."
    n = gc.collect()
    print "unreachable objects:", n
    print "garbage:",
    pprint(gc.garbage)


def demo(graph_factory):
    print "Set up graph:"
    one = graph_factory("one")
    two = graph_factory("two")
    three = graph_factory("three")
    one.set_next(two)
    two.set_next(three)
    three.set_next(one)

    print
    print "Graph:"
    print str(one)
    collect_and_show_garbage()

    print
    three = None
    two = None
    print "After 2 references removed"
    print str(one)
    collect_and_show_garbage()

    print
    print "removeing last reference"
    one = None
    collect_and_show_garbage()


gc.set_debug(gc.DEBUG_LEAK)
print "Setting up the cycle"
print 
demo(Graph)
print
print "breaking the cycle and cleaning up garbage"
print
gc.garbage[0].set_next(None)
while gc.garbage:
    del gc.garbage[0]
print collect_and_show_garbage()

这里使用了python的gc库的几个方法, 解释如下:

  • gc.collect() 收集垃圾
  • gc.garbage 获取垃圾列表
  • gc.set_debug(gc.DBEUG_LEAK) 打印无法看到的对象信息

运行结果如下:

代码语言:javascript
代码运行次数:0
运行
AI代码解释
复制
Setting up the cycle

Set up graph:
one.set_next(<Graph at 0x25c9e70 name=two>)
two.set_next(<Graph at 0x25c9e90 name=three>)
three.set_next(<Graph at 0x25c9e50 name=one>)

Graph:
one->two->three->one
Collecting...
unreachable objects:g 0
garbage:[]

After 2 references removed
one->two->three->one
Collecting...
unreachable objects: 0
garbage:[]

removeing last reference
Collecting...
unreachable objects: 6
garbage:[<Graph at 0x25c9e50 name=one>,
 <Graph at 0x25c9e70 name=two>,
 <Graph at 0x25c9e90 name=three>,
 {'name': 'one', 'other': <Graph at 0x25c9e70 name=two>},
 {'name': 'two', 'other': <Graph at 0x25c9e90 name=three>},
 {'name': 'three', 'other': <Graph at 0x25c9e50 name=one>}]

breaking the cycle and cleaning up garbage

one.set_next(None)
(Deleting two)
(Deleting three)
(Deleting one)
Collecting...
unreachable objects: 0
garbage:[]
None
[Finished in 0.4s]c: uncollectable <Graph 025C9E50>
gc: uncollectable <Graph 025C9E70>
gc: uncollectable <Graph 025C9E90>
gc: uncollectable <dict 025D3030>
gc: uncollectable <dict 025D30C0>
gc: uncollectable <dict 025C1F60>

从结果中我们可以看出,即使我们删除了Graph实例的本地引用,它依然存在垃圾列表中,不能回收。 接下来创建使弱引用的WeakGraph类:

代码语言:javascript
代码运行次数:0
运行
AI代码解释
复制
class WeakGraph(Graph):
    def set_next(self, other):
        if other is not None:
            if self in other.all_nodes():
                other = weakref.proxy(other)
        super(WeakGraph, self).set_next(other)
        return
demo(WeakGraph)

结果如下:

代码语言:javascript
代码运行次数:0
运行
AI代码解释
复制
Setting up the cycle

Set up graph:
one.set_next(<WeakGraph at 0x23f9ef0 name=two>)
two.set_next(<WeakGraph at 0x23f9f10 name=three>)
three.set_next(<weakproxy at 023F8810 to WeakGraph at 023F9ED0>)

Graph:
one->two->three
Collecting...
unreachable objects:Traceback (most recent call last):
  File "D:\apps\platform\demo\demo.py", line 87, in <module>
    gc.garbage[0].set_next(None)
IndexError: list index out of range
 0
garbage:[]

After 2 references removed
one->two->three
Collecting...
unreachable objects: 0
garbage:[]

removeing last reference
(Deleting one)
(Deleting two)
(Deleting three)
Collecting...
unreachable objects: 0
garbage:[]

breaking the cycle and cleaning up garbage

[Finished in 0.4s with exit code 1]

上面的类中,使用代理来指示已看到的对象,随着demo()删除了对象的所有本地引用,循环会断开,这样垃圾回收期就可以将这些对象删除。

因此我们我们在实际工作中如果需要用到循环引用的话,尽量采用弱引用来实现。

缓存对象

refproxy都只可用与维护单个对象的弱引用,如果想同时创建多个对象的弱引用咋办?这时可以使用WeakKeyDictionaryWeakValueDictionary来实现。

WeakValueDictionary类,顾名思义,本质上还是个字典类型,只是它的值类型是弱引用。当这些值引用的对象不再被其他非弱引用对象引用时,那么这些引用的对象就可以通过垃圾回收器进行回收。 下面的例子说明了常规字典与WeakValueDictionary的区别。

代码语言:javascript
代码运行次数:0
运行
AI代码解释
复制
# -*- coding:utf-8 -*-
import weakref
import gc
from pprint import pprint

gc.set_debug(gc.DEBUG_LEAK)


class Man(object):
    def __init__(self, name):
        self.name = name

    def __repr__(self):
        return '<Man name=%s>' % self.name

    def __del__(self):
        print "deleting %s" % self


def demo(cache_factory):
    all_refs = {}
    print "cache type:", cache_factory
    cache = cache_factory()
    for name in ["Jim", 'Tom', 'Green']:
        man = Man(name)
        cache[name] = man
        all_refs[name] = man
        del man
    print "all_refs=",
    pprint(all_refs)
    print
    print "before, cache contains:", cache.keys()
    for name, value in cache.items():
        print "%s = %s" % (name, value)
    print "\ncleanup"
    del all_refs
    gc.collect()

    print
    print "after, cache contains:", cache.keys()
    for name, value in cache.items():
        print "%s = %s" % (name, value)
    print "demo returning"
    return

demo(dict)
print

demo(weakref.WeakValueDictionary)

结果如下所示:

代码语言:javascript
代码运行次数:0
运行
AI代码解释
复制
cache type: <type 'dict'>
all_refs={'Green': <Man name=Green>, 'Jim': <Man name=Jim>, 'Tom': <Man name=Tom>}

before, cache contains: ['Jim', 'Green', 'Tom']
Jim = <Man name=Jim>
Green = <Man name=Green>
Tom = <Man name=Tom>

cleanup

after, cache contains: ['Jim', 'Green', 'Tom']
Jim = <Man name=Jim>
Green = <Man name=Green>
Tom = <Man name=Tom>
demo returning
deleting <Man name=Jim>
deleting <Man name=Green>
deleting <Man name=Tom>

cache type: weakref.WeakValueDictionary
all_refs={'Green': <Man name=Green>, 'Jim': <Man name=Jim>, 'Tom': <Man name=Tom>}

before, cache contains: ['Jim', 'Green', 'Tom']
Jim = <Man name=Jim>
Green = <Man name=Green>
Tom = <Man name=Tom>

cleanup
deleting <Man name=Jim>
deleting <Man name=Green>

after, cache contains: []
demo returning

[Finished in 0.3s]
本文参与 腾讯云自媒体同步曝光计划,分享自作者个人站点/博客。
原始发表:2016.06.15 ,如有侵权请联系 cloudcommunity@tencent.com 删除

本文分享自 作者个人站点/博客 前往查看

如有侵权,请联系 cloudcommunity@tencent.com 删除。

本文参与 腾讯云自媒体同步曝光计划  ,欢迎热爱写作的你一起参与!

评论
登录后参与评论
暂无评论
推荐阅读
Python一切皆是对象,但这和内存管理有什么关系?
今天是Python的第15篇文章,我们来聊聊Python中内存管理机制,以及循环引用的问题。
TechFlow-承志
2020/05/25
4470
Python一切皆是对象,但这和内存管理有什么关系?
Python 弱引用 weakref
弱引用键的映射类。当不再有对键的强引用时,字典中的条目将被丢弃。这可用于将附加数据与应用程序其他部分所拥有的对象相关联,而无需向这些对象添加属性。这对于覆盖属性访问的对象特别有用。
为为为什么
2022/08/09
1.2K0
Python 弱引用 weakref
Python 进阶:浅析「垃圾回收机制」
花下猫语:近半个月里,我连续写了两篇关于 Python 中内存的话题,所关注的点都比较微小,猎奇性质比实用性质大。作为对照,今天要分享一篇长文,是跟内存相关的垃圾回收话题,一起学习进步吧! 作者:二两
Python猫
2019/09/19
2.2K0
Python 进阶:浅析「垃圾回收机制」
【Pthon100天学习笔记】Day19 面向对象基础
月薪结算系统 - 部门经理每月15000 程序员每小时200 销售员1800底薪加销售额5%提成
天道Vax的时间宝藏
2021/12/07
3630
一日一技:如何使用弱引用优化 Python 程序的内存占用?
Python 的垃圾回收机制通过引用计数来决定一个对象要不要被回收。当一个对象被引用次数为0时,它就会被作为垃圾回收从而释放 Python 内存。
青南
2020/02/19
1.1K0
Python 中的垃圾回收?如何确认垃圾?
在当今的编程世界中,内存管理是每个开发者都需要关注的重要问题。Python作为一门高级语言,其内存管理机制十分灵活,其中的垃圾回收机制更是为开发者提供了便利。在本文中,我们将深入探讨Python中的垃圾回收机制,并介绍一些判断对象是否为垃圾的方法。
疯狂的KK
2023/08/04
4700
Python 中的垃圾回收?如何确认垃圾?
【JVM】深入理解Java引用类型:强引用、软引用、弱引用和虚引用
导言: 在Java中,引用类型是内存管理的重要组成部分。本文将深入介绍强引用、软引用、弱引用和虚引用,为您解析它们的特性以及如何在实际应用中巧妙利用。
人不走空
2024/02/21
7160
【JVM】深入理解Java引用类型:强引用、软引用、弱引用和虚引用
Python weakref (弱引用 ) 教程
首先提一点:大家遇到python模块的使用问题,尽可能去 python document去找答案。
marsggbo
2021/06/10
1.2K0
Python weakref (弱引用 ) 教程
Python是如何进行内存管理的?
Python是一种高级编程语言,因其简洁易读的语法和强大的生态系统而受到广泛的欢迎。在Python中,内存管理是一个关键的主题,它决定了程序的性能和可靠性。本文将介绍Python是如何进行内存管理的,并讨论一些常见的内存管理技术和最佳实践。
疯狂的KK
2023/08/05
7930
Python是如何进行内存管理的?
python weakref
一个对象的弱引用并不足以使得对象存在。当一个对象仅仅剩下弱引用的时候,python的垃圾回收机制会回收销毁这些对象,收回内存。
用户5760343
2019/08/02
6370
面试题-python 垃圾回收机制?
简历上写着熟悉 python 面试官上来就问:说下python 垃圾回收机制?一盆冷水泼过来,瞬间感觉 python 不香了。 Python中,主要通过引用计数(Reference Counting)进行垃圾回收。
上海-悠悠
2021/03/19
9650
面试题-python 垃圾回收机制?
学习一下Python的垃圾回收
如果你的程序运行一次就退出了,你可能体会不到内存管理的重要性。如果你写的程序需要 7x24 小时持续不断地运行,那么内存管理就非常重要,尤其对于重要的服务,不能出现内存泄漏。
somenzz
2020/11/25
5550
Python变量小秘密
跟其他编程语言不同,Python的变量不是盒子,不会存储数据,它们只是引用,就像标签一样,贴在对象上面。
dongfanger
2021/06/24
3480
python 内存泄漏
程序运行时都需要在内存中申请资源用于存放变量,python 在处理内存中的变量时会调用垃圾回收机制,会留心那些永远不会被引用的变量并及时回收变量,删除并释放相关资源。
为为为什么
2022/08/09
2.9K0
python 内存泄漏
什么是Python的 “内存管理机制”
Python作为一个高层次的结合了解释性、编译性、互动性和面向对象的脚本语言,与大多数编程语言不同,Python中的变量无需事先申明,变量无需指定类型,程序员无需关心内存管理,Python解释器给你自动回收。开发人员不用过多的关心内存管理机制,这一切全部由python内存管理器承担了复杂的内存管理工作。
小灰
2020/07/29
1.7K0
什么是Python的 “内存管理机制”
python进阶(7)垃圾回收机制
现在的高级语言如java,c#等,都采用了垃圾回收机制,而不再像c,c++里,需要用户自己管理内存。自己管理内存及其自由,可以任意申请内存,但这如同一把双刃剑,可能会造成内存泄漏,空指针等bug。 python中也同java一样采用了垃圾回收机制,不过不一样的是:python采用的是引用计数机制为主,标记清除和分代回收两种机制为辅的策略
全栈程序员站长
2022/09/19
7580
解决Python中的循环引用和内存泄漏问题
在Python编程中,循环引用和内存泄漏是两个常见的问题。本文将详细介绍如何识别和解决这些问题,并提供详细的代码示例。
华科云商小徐
2023/08/29
1.3K0
学习PHP弱引用的知识
之前的文章中,我们已经学习过引用和引用传值相关的知识。我们知道,PHP 中没有纯引用(指针),不管是对象,还是用引用符号 & 赋值的变量,都是对一个符号表的引用。而今天,我们要学习的是另一种引用形式:弱引用。
硬核项目经理
2020/09/10
3.1K0
Python 库大全(下)!知道的都是大佬!(附代码讲解)
模块 reprlib 提供了一份定制的 repr(),用于简洁 地展示各种大的或者多层嵌套的容器变量:
Python知识大全
2020/02/13
7580
【python进阶】Garbage collection垃圾回收2
前言 在上一篇文章【python进阶】Garbage collection垃圾回收1,我们讲述了Garbage collection(GC垃圾回收),画说Ruby与Python垃圾回收,Python中的循环数据结构以及引⽤计数以及Python中的GC阈值,这一节我们将继续介绍GC模块的一些应用和注意事项,下面开始今天的讲解~~ 一、垃圾回收机制 Python中的垃圾回收是以引⽤计数为主,分代收集为辅。 1、导致引⽤计数+1的情况 对象被创建,例如a=23 对象被引⽤,例如b=a 对象被作为参数,传⼊到⼀个函
Angel_Kitty
2018/05/04
7850
【python进阶】Garbage	collection垃圾回收2
相关推荐
Python一切皆是对象,但这和内存管理有什么关系?
更多 >
领券
问题归档专栏文章快讯文章归档关键词归档开发者手册归档开发者手册 Section 归档
本文部分代码块支持一键运行,欢迎体验
本文部分代码块支持一键运行,欢迎体验