zoukankan      html  css  js  c++  java
  • 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.11python.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.11python.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.11python.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.11python.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的情况:

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

    导致引用计数-1的情况

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

     

    参考下面的代码:

    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.11python.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,内存被清空

     

  • 相关阅读:
    坚持
    随笔
    C++:对象和类
    STEP7 V14 安装和激活
    c++:cout
    C 格式化字符串处理函数
    WIn:消极处理机制
    Python:requests发送json格式数据
    Python:logging日志功能的基本使用
    PLC:西门子测试
  • 原文地址:https://www.cnblogs.com/zhanghongfeng/p/7352686.html
Copyright © 2011-2022 走看看