zoukankan      html  css  js  c++  java
  • python magic_method

    总的来说python的 magic method 主要是围绕一些类中形如 __xx__ 的样子的方法。

      1 构造对象和初始化对象 __new__, __init__ 等

      2 控制属性访问 __getattribute__, __setattr__ 等

      3 创建对象描述符 __get__, __set__, __del__

      4 可调用对象 __call__

      5 上下文管理 __enter__, __exit__

      6 自定义容器 __getitem__, __iter__ 等

      7 反射 __instancecheck__ 等

      8 迭代器 __iter__, __next__

    1 构造对象和初始化对象 

    1.1 __new__ 用来创建类并返回这个类的实例(创建)。

    1.2 __init__ 将传入的参数来初始化该实例(初始化)。

    1.3 __del__ 在对象生命周期结束的时候,该方法会被调用。

    __new__ 与 __init__ 的示例

    class B(object):
        def __init__(self):
            print "init"
    
        def __new__(cls, *args, **kwargs):  # 一定要写传入cls,因为要实例化类
            print "new %s" % cls
            return object.__new__(cls, *args, **kwargs)
    
    b = B()
    
    
    [out:]
    new <class '__main__.B'>
    init
    class A(object):
        pass
    
    
    class B(A):
        def __init__(self):
            print "init"
    
        def __new__(cls, *args, **kwargs):
            print "new %s" % cls
            # 当实例化父类的时候,本类的init将不会被调用
            return object.__new__(A, *args, **kwargs) 
    
    b = B()
    print type(b)
    
    
    [out:]
    new <class '__main__.B'>
    <class '__main__.A'>

    1.4 __str__函数用于处理打印实例本身的时候的输出内容, 如果覆写该函数,则默认输出函数名称和内存地址

    class Foo(object):
    
        def __init__(self):
            self.name = 'wwww'
    
        def __str__(self):
            return self.name
    
    
    foo = Foo()
    print foo
    
    # out:
    # wwww
    
    # 如果没有__str__则打印 <__main__.Foo object at 0x000000000253DA90>

    2 控制属性访问  

    2.1 __getattr__(self,  item) 当获取一个不存在的字段时候会被调用。你也可以定义调用时候的处理方式 如下:

    class Desc(object):
        def __init__(self, name):
            self.name = name
    
        def __getattr__(self, item):
            if item == 'mmc':
                return 'dunk'
            return 'nothing'

    2.2 __setattr__(self, key, value)  关于__setattr__有两点需要说明:

    第一,使用它时必须小心,不能写成类似self.name = “Tom”这样的形式,因为这样的赋值语句会调用__setattr__方法,这样会让其陷入无限递归;

    第二,你必须区分 对象属性 和 类属性 这两个概念。

      实例对象的__setattr__方法可以定义属性的赋值行为,不管属性是否存在。当属性存在时,它会改变其值;当属性不存在时,它会添加一个对象属性信息到对象的__dict__中,然而这并不改变类的属性。

    class Foo(object):
    
        def __init__(self, name):
            self.name = name
    
        def __getattr__(self, item):
            return 'is nothing'
    
        def __setattr__(self, key, value):
            return super(Foo, self).__setattr__(key, value)
    
    
    
    f = Foo('wwe')
    print f.name
    print f.ufc
    f.ufc = 123
    print f.ufc
    
    
    [out:]
    wwe
    is nothing

    2.3 __delattr__ 删除一个属性

    和__setattr__方法要注意无限递归的问题,重写该方法时不要有类似del self.name的写法。

    class Desc(object):
        def __init__(self, name):
            self.name = name
    
        def __getattr__(self, item):
            return 'nothing'
    
        def __delattr__(self, item):
            print 'process del'
            return super(Desc, self).__delattr__(item)
    
    
    de = Desc('zhww')
    print de.name
    del de.name
    print de.name
    
    
    [out:]
    zhww
    process del
    nothing

    2.4 __getattribute__(self, item) 当访问字段的时候会被无条件调用。访问字段首先调用 __getattribute__ 方法,如果该方法未能处理字段才调用 __getattr__方法 ,

    注意我们使用了super()方法来避免无限循环问题。 如下:

    class Desc(object):
        def __init__(self, name):
            self.name = name
    
        def __getattr__(self, item):
            return 'nothing'
    
        def __getattribute__(self, item):
            try:
                print 'hahha'
                return super(Desc, self).__getattribute__(item)
            except AttributeError:
                return 'default'
    
    desc = Desc('wwt')
    print desc.name
    print desc.wwt
    
    [out:]
    hahha #每次调用都会访问 __getattribute__ 方法
    wwt
    hahha
    default # 如果 __getattribute__ 可以处理, 就不会调用__getattr__ 方法

    3 创建对象描述符  

    我认为如果说 __getattribute__, __getattr__, __setattr__, __delattr__ 等方法用来实现对象中属性查找、设置、删除的逻辑。

    那么描述符就是把某个具体属性当成对象进行查找,设置,删除的逻辑(很重要)

    3.1 为了成为一个描述符,一个类必须至少有__get__,__set__,__delete__方法被实现:

       __get__(self, instance, owner): 定义了当描述器的值被取得的时候的行为。instance是拥有该描述器对象的一个实例(obj)。owner是拥有者本身(cls)

       __set__(self, instance, value): 定义了当描述器的值被改变的时候的行为。instance是拥有该描述器类的一个实例。value是要设置的值。

      __del__(self, instance): 定义了当描述器的值被删除的时候的行为。instance是拥有该描述器对象的一个实例。

    3.2 数据描述符与非数据描述符 

      非数据描述符:只定义了__get__ 属性

      数据描述符:定义了__get__, __set__, __delete__ 属性

    class Describer(object):
    
        def __init__(self):
            self._func = None
    
        # 用__call__实现类的装饰器
        def __call__(self, func):
            print 'im Describer call'
            print func
            self._func = func
            return self
    
        def __get__(self, instance, owner):
            self._func(instance)
            instance.foo2()
            return 123
    
    
    class Foo(object):
    
        def __init__(self):
            pass
    
        @Describer()
        def foo(self):
            print 'im Foo instance foo'
    
        def foo2(self):
            print 'im Foo instance foo2'
    
    
    f = Foo()
    # 注意,因为使用了描述符,所以类方法会被封装在描述符中,而这个方法会被描述符作为属性返回
    # 所以foo作为描述符的属性方法存在
    print f.foo
    '''
    out:
    im Describer call
    <function foo at 0x0000000002756668>
    im Foo instance foo
    im Foo instance foo2
    '''

    3.3 注意

    3.3.1 方法会变成属性的调用方式

      注意,因为使用了描述符,所以类方法会被封装在描述符中,而这个方法会被描述符作为属性返回

      所以foo作为描述符的属性方法存在

    3.3.2 描述符如果在作为一个类的属性,一定要是类属性, 实例中是不允许使用描述符的

    class Describer(object):
    
        def __init__(self):
            self.name = 'www'
    
        def __get__(self, instance, owner):
            print 'im __get__'
            return self.name
    
    
    class Foo(object):
    
        def __init__(self):
            self.des = Describer()
    
    
    foo = Foo()
    print Foo.__dict__
    foo.des  # 并没有返回值
    
    # out:
    # {'__dict__': <attribute '__dict__' of 'Foo' objects>, '__module__': '__main__', '__weakref__': <attribute '__weakref__' of 'Foo' objects>, '__doc__': None, '__init__': <function __init__ at 0x00000000025F65F8>}

    因为调用 foo.des 时刻,首先会去调用Foo(即Owner)的 __getattribute__() 方法,该方法将 foo.des 转化为Foo.__dict__['des'].__get__(t, Foo), 但是呢,实际上 Foo并没有 des 这个属性,des 是属于实例对象的,所以,只能忽略了。

    4 可调用的对象  

    4.1 __call__(self, [args...]) 允许一个类的实例像函数一样被调用。实质上说,这意味着 x() 与 x.__call__() 是相同的。这会让类的实例在实现方法上非常优美

    5 上下文管理  

    5.1 __enter__(): 在使用with语句时调用,会话管理器在代码块开始前调用,返回值与as后的参数绑定

    5.2 __exit__(): 会话管理器在代码块执行完成好后调用,在with语句完成时,对象销毁之前调用

    6 创建自定义容器  

    首先,实现不可变容器的话,你只能定义 __len__ 和 __getitem__ (下面会讲更多)。可变容器协议则需要所有不可变容器的所有,另外还需要 __setitem__ 和 __delitem__。如果你希望你的对象是可迭代的话,你需要定义 __iter__ 会返回一个迭代器。迭代器必须遵循迭代器协议,需要有 __iter__(返回它本身) 和 next。  ---- j_hao104

    6.1 _len__(self) 返回容器的长度。对于可变和不可变容器的协议,这都是其中的一部分。

    6.2 __getitem__(self, item) 定义当某一项被访问时,使用self[key]所产生的行为。这也是不可变容器和可变容器协议的一部分。如果键的类型错误将产生TypeError;如果key没有合适的值则产生KeyError。

    6.3 __setitem__(self, key, value) 当你执行self[key] = value时,调用的是该方法。

    6.4 __delitem__(self, key) 定义当一个项目被删除时的行为(比如 del self[key])。这只是可变容器协议中的一部分。当使用一个无效的键时应该抛出适当的异常。

    6.5 __iter__(self) 返回一个容器迭代器,很多情况下会返回迭代器,尤其是当内置的iter()方法被调用的时候,以及当使用for x in container:方式循环的时候。迭代器是它们本身的对象,它们必须定义返回self的__iter__方法。

    6.6 __reversed__(self) 实现当reversed()被调用时的行为。应该返回序列反转后的版本。仅当序列可以是有序的时候实现它,例如对于列表或者元组。

    6.7 __contains__(self, item) 定义了调用in和not in来测试成员是否存在的时候所产生的行为。你可能会问为什么这个不是序列协议的一部分?因为当__contains__没有被定义的时候,如果没有定义,那么Python会迭代容器中的元素来一个一个比较,从而决定返回True或者False。

    6.8 __missing__(self, key) dict字典类型会有该方法,所以一定要是dict类的子类,它定义了key如果在容器中找不到时触发的行为。比如d = {‘a’: 1}, 当你执行d[notexist]时,d.__missing__[‘notexist’]就会被调用。但注意它一定是在__getitem__(self, item)处理后执行,所以一般将__missing__(self, key)定义在__getitem__() 异常处理中。

    下面是自定义一个MyDict()类

    #!/usr/bin/python
    # -*- coding: utf-8 -*-
    
    
    class MyDict(dict):
        def __init__(self):
            self.kind = {}
    
        def __len__(self):
            return len(self.kind)
    
        def __getitem__(self, item):
            try:
                return self.kind[item]
            except KeyError:
                return self.__missing__(item)  # 首先,__getitem__()方法需要在访问键失败时,调用__missing__()方法,而不是item不存在时直接调用 missing
    
        def __missing__(self, key):
            return 'There is not item.'
    
        def __setitem__(self, key, value):
            self.kind[key] = value
    
        def __delitem__(self, key):
            del self.kind[key]
    
        def __iter__(self):
            return iter(self.kind)
    
        def __reversed__(self):
            return reversed(self.kind)  # 但类型必须是有序类型,本例中的dict属于无序,所以无法调用reversed方法
    
        def __contains__(self, item):
            if item in self.kind:
                print 'MyTrue'
                return True
            else:
                print 'MyFalse'
                return False
    
    
    
    md = MyDict()
    
    md['name'] = 'wwt'
    md['age'] = 23
    md['del'] = 'nothing'
    print type(md)
    print len(md)
    print
    del md['del']
    for item in md:
        print item
    print
    print md['haha']
    print
    print 'name' in md
    
    
    [out:]
    <class '__main__.MyDict'>
    
    age
    name
    
    There is not item.
    
    MyTrue
    True

    7 反射  

    7.1 __instancecheck__(self, instance) 检查一个实例是不是你定义的类的实例

    7.2 __subclasscheck__(self, subclass) 检查一个类是不是你定义的类的子类

    这两个方法自己没试过,试过够再细说。

    isinstance(mc, MyClass)
    issubclass(SubClass, MyClass) 这俩个个方法可以实现同样功能

    8 迭代器 

    8.1 __iter__(self) 一般 return self 本身,也就是说返回对象自身

    8.2 __next__(self) 在每次调用 __next__ 方法 或 调用 next 方法时, 会执行该函数

    仿写内置 的range方法

    class MyRange(list):
    
        def __init__(self):
            super(MyRange, self).__init__()
    
        def __iter__(self):
            return self
    
        def __next__(self):
            if self._i:
                self._s += self._i
            else:
                self._s += 1
            return
    
        def __call__(self, s, t, interval=None):
            self._s = s
            self._t = t
            self._i = interval
            while self._s <= self._t:
                self.append(self._s)
                self.__next__()
            return self
    
    mr = MyRange()
    print mr(0, 5, 2)  # [0, 2, 4]
    print range(0, 4)  # [0, 2, 4]
    View Code

    参考文献

    my.oschina.net/jhao104/blog/779743

  • 相关阅读:
    gain 基尼系数
    luogu P5826 【模板】子序列自动机 主席树 vector 二分
    牛客挑战赛39 树与异或 离线 树上莫队 树状数组 约数
    4.22 省选模拟赛 三元组 manacher 回文自动机
    4.22 省选模拟赛 最优价值 网络流 最大权闭合子图
    4.18 省选模拟赛 消息传递 树剖 倍增 线段树维护等比数列
    luogu P4008 [NOI2003]文本编辑器 splay 块状链表
    牛客挑战赛39 密码系统 后缀数组
    luogu P1526 [NOI2003]智破连环阵 搜索+最大匹配+剪枝
    luogu P4095 [HEOI2013]Eden 的新背包问题 多重背包 背包的合并
  • 原文地址:https://www.cnblogs.com/fuzzier/p/7512955.html
Copyright © 2011-2022 走看看