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

  • 相关阅读:
    Java 常见异常种类
    Oracle存储过程记录异常
    oracle定时器
    oracle生成单据号
    oracle计算时间秒数差
    oracle分组后取每组第一条数据
    was部分更新
    数据库分区
    JTA事务管理
    docker
  • 原文地址:https://www.cnblogs.com/fuzzier/p/7512955.html
Copyright © 2011-2022 走看看