python cookbook第三版学习笔记十四:类和对象(五)代理类以及内存回收

mac2022-06-30  54

代理类: 代理类的作用其实有继承有些类似,如果你想将某个实例的属性访问代理到内部另外一个实例中去,可以用继承也可以用代理。来看下代理的应用: class A:     def spam(self,x):         print 'in Class A x=%d' % x     def foo(self):         print 'in Class A:foo()' class B1:     def __init__(self):         self._a=A()          ⑴     def spam(self,x):         return self._a.spam(x)      ⑵     def foo(self):         return self._a.foo()    ⑶     def bar(self):         passif __name__ == "__main__":     b=B1()     b.spam(42)     b.foo() E:\python2.7.11\python.exe E:/py_prj/python_cookbook/chapter8.py in Class A x=42 in Class A:foo() 在(1)中,将self._a赋值为A的实例。在(2)中,self._a.spam(x),self._a.foo其实等于A().spam(x)和A().foo().通过这样的方式,在B1中能够访问到A中的实现。这也就实现了spam()和foo()的代理 如果仅仅是两个函数需要代理,那么代码量还不算很大,但是如果有很多函数需要代理,每个函数在B1中都需要写一个对应的代理函数就很麻烦了。这种情况下使用__getattr__就方便很多 class A:     def spam(self,x):         print 'in Class A x=%d' % x     def foo(self):         print 'in Class A:foo()' class B:     def __init__(self):         self._a=A()     def bar(self):         pass     def __getattr__(self, item):         return getattr(self._a,item) if __name__ == "__main__":     b=B()     b.spam(42)     b.foo() E:\python2.7.11\python.exe E:/py_prj/python_cookbook/chapter8.py {'name': 'x'} in Class A x=42 in Class A:foo()   在实例以及类中找不到对应的属性的时候会调用__getattr__。上面的实现在调用b.spam(42)以及b.foo()的时候将会调用__getattr__。getattr(self._a,item)等价于self._a.item     创建不调用init方法的实例。Python中的__init__方法其实是一个构造函数,在调用对应的类实例的时候,首先调用__init__方法。但是有没有方法可以绕开执行__init__呢? __new__就可以达到这个目的,来看下面的例子。 class Date(object):     def __init__(self,year,month,day):         self.year=year         self.month=month         self.day=day if __name__ == "__main__":     d=Date.__new__(Date)     print d.year E:\python2.7.11\python.exe E:/py_prj/python_cookbook/chapter8.py Traceback (most recent call last):   File "E:/py_prj/python_cookbook/chapter8.py", line 236, in <module>     print d.year AttributeError: 'Date' object has no attribute 'year' 首先通过Date.__new__(Date)创建一个实例,当执行d.year的时候提示没有year这个属性,这是因为没有执行__init__.如果添加下面的代码则不会有问题 print d.__init__(1,2,3)print d.year 注意: 只有在新式类中才会有__new__的方法,经典类中是没有的,因此要在python2.7中使用__new__方法,必须要继承自object。而python3.0中都是新式类。可以直接使用__new__ 我们还可以将这个__new__写到类中去: class Date(object):     def __init__(self,year,month,day):         self.year=year         self.month=month         self.day=day     def __new__(cls, *args, **kwargs):         print 'new'         print object.__new__(cls,*args, **kwargs)         return object.__new__(cls,*args, **kwargs)if __name__ == "__main__":     d=Date(2017,7,16) E:\python2.7.11\python.exe E:/py_prj/python_cookbook/chapter8.py new <__main__.Date object at 0x017DD3D0> 通过上面的实现可以看到__new__其实反馈的就是__init__中的self.在实例创建过程中__new__方法优于__init__方法。另外在__new__中必须要有返回值,这个返回值就是生成的实例。所以__new__和__init__就像这么一个关系,__init__提供生产的原料self(但并不保证这个原料来源正宗,像上面那样它用的是另一个不相关的类的__new__方法类得到这个实例),而__init__就用__new__给的原料来完善这个对象(尽管它不知道这些原料是不是正宗的) 如果我们完全不想用__init__来初始化实例,可以用下面的方法: class Date(object):     @classmethod     def today(cls,year,month,day):         d=cls.__new__(cls)         d.year=2017         d.month=7         d.day=16         return dif __name__ == "__main__":     d=Date.today(2017,7,16)     print d.year 通过classmethod修饰today方法,通过调用today方法也可以实现__init__方法   垃圾内存回收 一:垃圾内存回收: 在内部,创建一个对象时,Python总是在对象的C结构体里保存一个整数,称为 引用数。期初,Python将这个值设置为1:当引用数为0 的时候,就会销毁这个对象,然后释放内存

导致引用计数+1的情况:

对象被创建,例如n=1对象被引用,例如n1=n对象被作为参数,传入到一个函数中,例如func(n)对象作为一个元素,存储在容器中,例如list1=[n,n1]

导致引用计数-1的情况

对象的别名被显式销毁,例如del a对象的别名被赋予新的对象,例如a=2一个对象离开它的作用域,例如f函数执行完毕时,func函数中的局部变量(全局变量不会)对象所在的容器被销毁,或从容器中删除对象

 

参考下面的代码:

class Node(object):     def __init__(self,val):         self.value=valif __name__ == "__main__":     n1=Node('abc')     n2=Node('def')     n3=Node('ghi')     print sys.getrefcount(n1)     print sys.getrefcount(n2)     print sys.getrefcount(n3)

E:\python2.7.11\python.exe E:/py_prj/python_cookbook/chapter8.py

2

2

2

由于n1,n2,n3都被传入了getrefcount,按照上面计数的规则,因此最终的引用计数为2. 对应的关系如下图。

如果改变下引用,让n2引用n1.n2=n1.”ghi”被n1和n2同时引用,因此引用数变成2.而abc由于没有任何引用,所以引用计数为0,内存被清空

 

 

转载于:https://www.cnblogs.com/zhanghongfeng/p/7352686.html

最新回复(0)