Python 对象引用机制的一些实现细节

本文探讨了Python中`__call__`方法的使用,展示了如何通过`__call__`将普通函数转化为可调用对象,并解释了获取`__call__`属性时导致内存增大的原因。同时,介绍了Python对象的引用计数机制,通过一个示例展示了`__init__`和`__del__`方法在对象生命周期中的作用,并展示了在对象引用计数降为0时,`__del__`方法如何被调用。
摘要由CSDN通过智能技术生成

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__属性和函数本身不是同一个对象__call__方法的类型实际上是method-wrapper,意为方法包装器。
先来看看这段代码 :

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
评论 1
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

打赏作者

qfcy_

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

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

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

打赏作者

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

抵扣说明:

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

余额充值