Python语言特性纪要(三)——魔法方法

你在这里

Python语言特性纪要(三)——魔法方法

   (其他python纪要,请参考:

         Python语言特性纪要(一)——变量、类型、语句

         Python语言特性纪要(二)——类、对象

    )
本节内容本因属于“类、对象”一节,但因其重要性及篇幅单独一节。魔术方法使得Python成为客制化功能超强的语言,通过魔术方法,开发人员可以重新定义加减乘除、切片等功能,不愧为魔术二字。
十三、构造和析构
1. __new__(cls[, ...])
这是实例化对象时调用的首个方法,是一个静态方法,其返回值必须是一个实例对象。首个参数cls是必须的,是所要创建的对象的类。若果__new__()方法返回cls的对象,则会引发__init()的调用,且其余参数会直接传递给__init__()方法;否则,__init__()不会被调用。典型的重写中会调用其父类的__new__()方法,形如:super().__new__(cls[, ...])。_new__()方法用于创建对象,在继承自不可变类型时很重要。举例:
>>> class UpStr(str):
...     def __new__(cls, string):
...         string = string.upper()
...         return super().__new__(cls, string)
... 
>>> a=UpStr("i am ZZKOOK")
>>> a
'I AM ZZKOOK'
 
2. __init__(self[, ...])
在对象被__new__方法创建后,返回之前调用。该方法不写return语句(可视为默认return None),即使写return语句,也必须return None。__init__()方法用于初始化一些对象的客制化信息,如果父类中实现了__init__()方法,子类中进行了重写,此时若要调用父类中的__init__(),则必须显式调用,形如:super().__init__([args...])。举例:
>>> class Rect():
...     def __init__(self, x, y):
...         self.x = x
...         self.y = y
...     def getArea(self):
...         return self.x *self.y
... 
>>> r1= Rect(3,4)
>>> r1.getArea()
12
 
3. __del__(self)
当用del语句销毁对象时,__del__()方法自动被调用。如果父类中实现了__del__()方法,子类中进行了重写,此时若要调用父类中的__del__(),则必须显式调用,形如:super().__del__()。当对象有多个变量名指向时,仅当最后一个变量名被del时,才会调用__del__()。举例:
>>> class Test():
...     def __del__(self):
...         print('__del__() is called')
... 
>>> a = Test()
>>> b = a
>>> del a
>>> del b
__del__() is called
 
十四、算术运算、反运算、增量赋值运算符、一元运算符
1.算术运算魔法方法
通过Python下列魔法方法,可实现自定义对象的数值运算。下述方法可重新定制以下算术运算:(+-*@///%divmod(),pow()**<<>>&^|)
举例:
>>> class ExtraInt(int):
...     def __add__(self, other):
...         return super().__sub__(other)
... 
>>> x = ExtraInt(4)
>>> y = ExtraInt(7)
>>> x + y
-3
2.反运算魔法方法
反运算魔法方法则与算术运算魔法方法一一对应,在二元运算中如果前一个操作数类型没有实现算术运算魔法方法,而后一个操作数类型实现了对应的反运算魔法方法,则Python会调用该反运算魔法方法。
3.增量赋值运算魔法方法
此外还有增量赋值运算魔法方法,实现形如a += b这样的运算:
object.__iadd__(selfother)                  +=
object.__isub__(selfother)                  -=
object.__imul__(selfother)                  *=
object.__imatmul__(selfother)               @=
object.__itruediv__(selfother)              /=
object.__ifloordiv__(selfother)             //=
object.__imod__(selfother)                  %=
object.__ipow__(selfother[, modulo])        **=
object.__ilshift__(selfother)               <<=
object.__irshift__(selfother)               >>=
object.__iand__(selfother)                  &=
object.__ixor__(selfother)                  ^=
object.__ior__(selfother)                   |=
3.一元运算魔法方法
正负号、求绝对值、取反都是使用一元运算符,其对应魔法方法如下:
object.__neg__(self)                           +
object.__pos__(self)                           -
object.__abs__(self)                           abs()
object.__invert__(self)                        ~
十五、表现
表现类魔法方法包括:__repr__(self) 、__str__(self),其各自用途略微不同。print()打印操作会首先尝试__str__和str内置函数(print运行的内部等价形式),仅当__str__()未定义而__repr__()定义的情况下,返回__repr__()的结果。__repr__()用于所有其他的环境中:用于交互模式下提示回应以及repr函数。它通常应该返回一个编码字符串,可以用来重新创建对象,或者给开发者详细的显示。
举例:
>>> class present():
...     def __init__(self):
...         self.data = 'I like python'
...     def __repr__(self):
...         return '[repr: %s]' % self.data
...     def __str__(self):
...         return '[str: %s]' % self.data
... 
>>> ts = present()
>>> ts
[repr: I like python]
>>> print(ts)
[str: I like python]
十六、比较
比较运算符魔术方法如下:
object.__lt__(selfother)           <
object.__le__(selfother)           <=
object.__eq__(selfother)           ==
object.__ne__(selfother)           !=
object.__gt__(selfother)           >
object.__ge__(selfother)           >=
十七、属性
用于获取、设置、删除属性,属性通常表示为: obj.x
__getattr__(selfname)       当用户试图获取一个不存在的属性时的行为
__getattribute__(selfname)  当对象的属性被访问时的行为
__setattr__(selfnamevalue)当对象的属性被设置时的行为
__delattr__(selfname)       当对象的属性被删除时的行为
十八、描述符
python描述符是一个“绑定行为”的对象属性,在描述符协议中,它可以通过方法重写属性的访问,如果这些方法中的任何一个被定义在一个对象中,这个对象就是一个描述符。换句话说描述符就是将某种特殊类型的类的实例指派给另一个类的属性,该特殊类型的类中至少包含任意以下方法如下:

__get__(self, instance, owner)  用于访问属性,返回属性值

__set__(self, instance, value)  将在属性分配操作中调用,不返回任何值

__del__(self, instance)         控制删除操作,不返回任何值

其中:self是描述符类自身的实例

     instance是描述符的拥有者所在的类的实例

     owner是描述符的拥有者所在的类本身

描述符是对多数属性运用相同存取方式的一种技巧。单独的描述符没有什么意义,只有描述符实例托管在另一个类(以后统称托管类)中作为类属性才有意义。举例:

>>> class Desc(object):

...     def __init__(self,name):

...         self.name = name

...     def __get__(self, instance, owner):

...         print("call __get__ , name=", self.name)

... 

>>> class TestDesc(object):

...     x = Desc('x')

...     def __init__(self, x, y):

...         self.y = Desc('y')

... 

>>> t = TestDesc()

>>> t.x

call __get__ , name= x

>>> t.y

<__main__.Desc object at 0x7f5da9bba908>

说明:

1)t.x访问Owner的 __getattribute__() 方法(即:TestDesc.__getattribute__()),发现t没有实例属性x,然后访问类TestDesc,发现类属性x。python判断该类属性x为描述符,因此依据描述符协议,将 TestDesc.x 转化为 TestDesc.__dict__['x'].__get__(None, TestDesc) 来访问,并进入类Desc的 __get__()方法,进行相应的操作。

2)t.y访问Owner的 __getattribute__() 方法,该方法将 t.y 转化为TestDesc.__dict__['y'].__get__(t, TestDesc), 但是呢,实际上 TestDesc 并没有 y这个属性,y 是属于实例对象的,因此只能忽略了。

十九、定制容器、迭代器
Python中定制容器相关协议(协议在Python中类似其他编程语言中的接口,但更宽松,类似指南):
1.定制不可变容器,需要定义__len__()和__getitem__()方法
2.定制可变容器,需要定义__len__()、__getitem__()、__setitem__()、__delitem__()
3.定制迭代器,需要定义__iter__()、__next__()
容器相关魔法方法:
__len__(self)            定义当被len()函数调用时的行为
__getitem__(self, key)   定义获取容器指定元素的行为,self[key]
__setitem__(self, key, value)定义设置容器指定元素的行为,self[key]=value
__delitem__(self, key)   定义删除容器中指定元素的行为,del self[key]
__iter__(self)           定义当迭代器中的元素行为
__reversed__(self)       定义当被reversed()函数调用时的行为
__next__(self)           定义当被next()函数调用时的行为
__contains__(self,item)  定义当使用成员检查运算符 in 或 not in 时的行为
举例:(斐波那契数列)
>>> class Fib:
...     def __init__(self, n=20):
...         self.a = 0
...         self.b = 1
...         self.n = n
...     def __iter__(self):
...         return self
...     def __next__(self):
...         self.a, self.b = self.b, self.a + self.b
...         if self.a > self.n:
...             raise StopIteration
...         return self.a
... 
>>> f = Fib()
>>> for i in f:
...     print(i)
... 
1
1
2
3
5
8
13
 
著作权归作者所有。商业转载请联系本站作者获得授权,非商业转载请注明出处 ZZKOOK

您可能感兴趣的文章

登录以发表评论

评论

本人一定要到专页加个关注

 
110
春芽儿的头像

我一定分享

 
91
Sandy Ryza的头像

LZ实在最棒

 
101
Cay的头像

本人不得不安利

 
100
赵州的头像