Python 不为人知的对象引用机制

1."魔法"方法

众所周知, Python的对象中有一些以__双下划线开头的属性,
如调用函数的__call__属性, 和调用函数, 结果是相同的。

>>> def f(x):print('hello world',x)

>>> f(1)
hello world 1
>>> f.__call__(1)
hello world 1
>>> f.__call__
<method-wrapper '__call__' of function object at 0x02AF4348>

但函数的__call__属性和函数不是同一个对象
先来看看这段代码 :

def f():pass
old_f=f
lst=[]
while True:
    lst.append(f)
    f=f.__call__

运行一段时间后, 程序占用内存会迅速增大,
按下Ctrl+C, 查看列表lst的长度, 有几百万个对象,
调用列表的最后一个对象(lst[-1]()), 出现了RecursionError
原因分析
每次获取对象的__call__属性时, Python会自动产生一个方法包装器(method-wrapper), 会使程序占用内存增大。
调用列表的最后一个对象时, Python会调用它的前一个对象, 以此类推, 直到出现RecursionError
补充
__call__返回的方法包装器有一个__self__属性, 标识这个方法包装器的父对象。

def old_f():pass
wrapper=old_f.__call__
print(old_f)
print(wrapper.__self__)

结果输出:

<function old_f at 0x0404D108>
<function old_f at 0x0404D108>

说明old_fwrapper.__self__是同一个对象。

2.测试对象引用计数

在python中,每个对象都有存有该对象的引用总数,即引用计数。Python中的引用计数主要用于垃圾回收和内存管理机制。
Python中,当一个对象被创建时会调用该对象的__init__方法。
与之对应,在对象引用计数降为0,而被清除时,这回调用该对象的__del__方法。
比如这个示例:

import sys,gc,time,traceback

def debug(dict_):
    # 用来在交互式提示符中调试,参数dict_为命名空间
    while 1:
        s=input('>>> ').rstrip()
        if s:
            if s=='continue':break
            try:exec(compile(s,'<shell>','single'),dict_)
            except Exception:
                traceback.print_exc()
#lst=[]
class C:
    count=0
    def __init__(self):
        self.id=self.count
        C.count+=1
        print("{} was born".format(self))
    def __repr__(self):
        return object.__repr__(self)[:-1]+" id:%d>"%self.id
    def __del__(self):
        #lst.append(self)
        print("{} died".format(self),'is_finalizing',sys.is_finalizing())
        # 提示:解释器关闭过程中产生的所有异常都会被忽略,
        #      即显示Exception ignored in:...
        if sys.is_finalizing():
            debug(locals())

def test():
    while True:
        c=C()
        print()
        #time.sleep(0.1)

if __name__=="__main__":
    sys.c=C()
    test()

运行结果:

<__main__.C object at 0x01F56F30 id:684> was born
<__main__.C object at 0x01F301F0 id:683> died is_finalizing False

<__main__.C object at 0x01B208D0 id:685> was born
<__main__.C object at 0x01F56F30 id:684> died is_finalizing False
  • 2
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 打赏
    打赏
  • 1
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论 1
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包

打赏作者

qfcy_

你的鼓励将是我创作的最大动力

¥1 ¥2 ¥4 ¥6 ¥10 ¥20
扫码支付:¥1
获取中
扫码支付

您的余额不足,请更换扫码支付或充值

打赏作者

实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

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

余额充值