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

posted @ 2017-09-13 00:02  fuzzier  阅读(605)  评论(0编辑  收藏  举报