文章目录
魔法方法构造和析构算数运算算数操作符反运算增量赋值运算一元操作符 属性访问
魔法方法
前面在介绍类和对象时,已经接触过Python常用的魔法方法,那么什么是魔法方法呢?
魔法方法总是被下划线包围,例如_ _init_ _()。魔法方法是面向对象的Python的一切,如果不知道魔法方法,那么说明你还没意识到面向对象的Python的强大。魔法方法的魔力体现在它们总能够在适当的时候被调用。构造和析构
这里主要是_ _init_ _()、_ _new_ _()和_ _del_ _()方法,这些方法均在前面的Python基础——类和对象中详细讲解过,此处便不再赘述。
算数运算
现在来讲一个新的名词——工厂函数 。Python2.2以后,对类和类型进行了统一,做法就是将int()、float()、str()、list()、tuple()这些BIF转换为工厂函数。
如上图所示,普通的BIF应该是<class ‘builtin_function_or_method’>,而工厂函数则是<class ‘type’>。有没有觉得<class ‘type’>很眼熟?通过我们前面的学习,我们知道,如果定义一个类:
其类型也是type类型,也就是类对象。其实所谓的工厂函数,其实就是一个类对象。当你调用它们的时候,事实上就是创建一个相应的实例对象:
对象是可以计算的!Python中无处不对象,当在求a+b等于多少的时候,事实上Python就是将两个对象进行相加操作。Python的魔法方法还提供了自定义对象的数值处理,通过下面这些魔法方法的重写,可以自定义任何对象间的算数运算。
算数操作符
下表列举了算数运算相关的魔法方法。
魔法方法 | 含义 |
---|---|
_ _add_ _(self, other) | 定义加法的行为:+ |
_ _sub_ _(self, other) | 定义减法的行为:- |
_ _mul_ _(self, other) | 定义乘法的行为:* |
_ _truediv_ _(self, other) | 定义真除法的行为:/ |
_ _floordiv_ _(self, other) | 定义整数除法的行为:// |
_ _mod_ _(self, other) | 定义取模算法的行为:% |
_ _divmod_ _(self, other) | 定义当被divmod()调用时的行为 |
_ _pow_ _(self, other[, modulo]) | 定义当被power()调用或**运算时的行为 |
_ _lshift_ _(self, other) | 定义按位左移位的行为:<< |
_ _rshift_ _(self, other) | 定义按位右移位的行为:>> |
_ _and_ _(self, other) | 定义按位与操作的行为:& |
_ _xor_ _(self, other) | 定义按位异或操作的行为:^ |
_ _or_ _(self, other) | 定义按位或操作的行为:| |
举个例子,下面定义一个比较特立独行的类:
有些读者可能会问,我想自己写代码,不想通过Python的默认方案行不行?答案是可以的,但是要小心。
有些人可能会像上图那样写,这样的结果就是会陷入无限递归。为什么会这样呢?
这是因为当对象涉及加法操作时,会自动调用魔法方法_ _add_ _(),但是看看上面的魔法方法写的是return self + other,也就是返回对象本身加另外一个对象,这就又自动触发调用_ _add_ _()方法了,这样就形成了无限递归。所以需要修改成下面这样:
上面介绍了很多有关算数运算的魔法方法,意思是当对象进行了相关的算数运算,自然就会触发对应的魔法方法。
反运算
下表列举了反运算相关的魔法方法。
魔法方法 | 含义 |
---|---|
_ _radd_ _(self, other) | 定义加法的行为:+(当左操作数不支持相应的操作时被调用) |
_ _rsub_ _(self, other) | 定义减法的行为:-(当左操作数不支持相应的操作时被调用) |
_ _rmul_ _(self, other) | 定义乘法的行为:*(当左操作数不支持相应的操作时被调用) |
_ _rtruediv_ _(self, other) | 定义真除法的行为:/(当左操作数不支持相应的操作时被调用) |
_ _rfloordiv_ _(self, other) | 定义整数除法的行为://(当左操作数不支持相应的操作时被调用) |
_ _rmod_ _(self, other) | 定义取模算法的行为:%(当左操作数不支持相应的操作时被调用) |
_ _rdivmod_ _(self, other) | 定义当被divmod()调用时的行为(当左操作数不支持相应的操作时被调用) |
_ _rpow_ _(self, other[, modulo]) | 定义当被power()调用或**运算时的行为(当左操作数不支持相应的操作时被调用) |
_ _rlshift_ _(self, other) | 定义按位左移位的行为:<<(当左操作数不支持相应的操作时被调用) |
_ _rrshift_ _(self, other) | 定义按位右移位的行为:>>(当左操作数不支持相应的操作时被调用) |
_ _rand_ _(self, other) | 定义按位与操作的行为:&(当左操作数不支持相应的操作时被调用) |
_ _rxor_ _(self, other) | 定义按位异或操作的行为:^(当左操作数不支持相应的操作时被调用) |
_ _ror_ _(self, other) | 定义按位或操作的行为:|(当左操作数不支持相应的操作时被调用) |
这里的反运算魔法方法跟上节介绍的算数运算符保持一一对应,不同之处就是反运算的魔法方法多了一个r,举个例子:
尝试一下:
关于反运算,这里还需要注意一点:对于a + b,b的_ _radd_ _(self, other)的self是b对象,other是a对象,所以不能这么写:
所以对于注重操作数顺序的运算符(例如减法、除法、移位),在重写反运算魔法方法的时候,就一定要注意顺序问题。
增量赋值运算
Python也有大量的魔法方法可以定制增量赋值语句,增量赋值其实就是一种偷懒的形式,它将操作符与赋值结合起来,例如:
一元操作符
一元操作符就是只有一个操作数的意思,像 a + b 这样,加号左右有a、b两个操作数,叫作二元操作数。一元操作符如-,其作为负号的时候,表示负数,其就是一元操作符,如-1。
Python支持的一元操作符主要有_ _neg_ _()(表示正号行为),_ _pos_ _()(定义负号行为),_ _abs_ _()(定义当被abs()调用时的行为,就是取绝对值的意思),还有_ _invert_ _()(定义按位取反的行为)。
属性访问
通常可以通过点(.)操作符的形式去访问对象的属性,例如:
此外,关于属性访问,也有相应的魔法方法来管理,下表列举了属性相关的魔法方法。
魔法方法 | 含义 |
---|---|
_ _getattr_ _(self, name) | 定义当用户试图获取一个不存在的属性时的行为 |
_ _getattribute_ _(self, name) | 定义当该类的属性被访问时的行为 |
_ _setattr_ _(self, name, value) | 定义当一个属性被设置时的行为 |
_ _delattr_ _(self, name) | 定义当一个属性被删除时的行为 |
举个例子:
这几个魔法方法在使用上需要注意的是,有一个死循环陷阱,初学者比较容易中招,通过下面这个实例来讲解一下。
写一个矩形类(Rectangle),默认有宽(width)和高(height)两个属性,如果为一个叫square的属性赋值,那么说明这是一个正方形,其值就是正方形的边长,此时宽和高都等于边长。
这是为什么呢?
分析一下:实例化对象,调用_ _init_ _()方法,在这里给self.width和self.height分别初始化赋值。一发生赋值操作,就会自动触发_ _setattr_ _()魔法方法,width和height两个属性被赋值,于是执行else的下边的语句,就又变成了self.width=value,那么就相当于又触发了_ _setattr_ _()魔法方法了,死循环陷阱就是这么来的。
那怎么解决呢?
一种方法就是跟刚才一样,用super()来调用基类的_ _setattr_ _(),那么这样就依赖基类的方法来实现赋值:
另一种方法就是给特殊属性_ _dict_ _赋值。 对象有一个特殊的属性,叫作_ _dict_ _,它的作用就是以字典的形式显示出当前对象的所有属性以及相对应的值: