python cookbook第三版学习笔记十三:类和对象代理类以及内存回收
Posted
tags:
篇首语:本文由小常识网(cha138.com)小编为大家整理,主要介绍了python cookbook第三版学习笔记十三:类和对象代理类以及内存回收相关的知识,希望对你有一定的参考价值。
代理类:
代理类的作用其实有继承有些类似,如果你想将某个实例的属性访问代理到内部另外一个实例中去,可以用继承也可以用代理。来看下代理的应用:
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):
pass
if __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 d
if __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=val
if __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,内存被清空
以上是关于python cookbook第三版学习笔记十三:类和对象代理类以及内存回收的主要内容,如果未能解决你的问题,请参考以下文章
python cookbook第三版学习笔记五:datetime
python cookbook第三版学习笔记三:列表以及字符串