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 __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.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__ 方法
我认为如果说 __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.1 __call__(self, [args...]) 允许一个类的实例像函数一样被调用。实质上说,这意味着 x() 与 x.__call__() 是相同的。这会让类的实例在实现方法上非常优美
5.1 __enter__(): 在使用with语句时调用,会话管理器在代码块开始前调用,返回值与as后的参数绑定
5.2 __exit__(): 会话管理器在代码块执行完成好后调用,在with语句完成时,对象销毁之前调用
首先,实现不可变容器的话,你只能定义 __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.1 __instancecheck__(self, instance) 检查一个实例是不是你定义的类的实例
7.2 __subclasscheck__(self, subclass) 检查一个类是不是你定义的类的子类
这两个方法自己没试过,试过够再细说。
isinstance(mc, MyClass)
issubclass(SubClass, MyClass) 这俩个个方法可以实现同样功能
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]
参考文献:
my.oschina.net/jhao104/blog/779743