魔术方法(Magic Method)
在 python 中,所有以__
包裹的方法统称为 魔术方法,如:__init__
,__str__
等等
1. 构造(__new__
)和初始化(__init__
)
__new__
: 用来创建类并返回这个类的实例__init__
: 只是将传入的参数来初始化该实例- 创建实例时不一定会被调用, 比如通过
pickle.load
的方式反序列化实例时, __init__
不会被调用
def __new__(cls)
是在 def __init__(self)
之前调用的,作用是返回一个实例对象.__new__
方法总是需要返回该类的一个实例,而__init__
不能返回除了None
的任何值.
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20
| class User: def __new__(cls, *args, **kwargs): print('调用了 def __new__ 方法') print(args) return super(User, cls).__new__(cls)
def __init__(self, name, age): print('调用了 def __init__ 方法') self.name = name self.age = age
if __name__ == '__main__': user = User('小明', 18)
调用了 def __new__ 方法 ('小明', 18) 调用了 def __init__ 方法
|
通过打印的结果来看,我们就可以知道一个类创建的过程是怎样的了,先是调用了 __new__
方法来创建一个对象,把参数传给 __init__
方法进行实例化。
在实际开发中,很少会用到 __new__
方法,除非希望能够控制类的创建。通常讲到 __new__
,都是牵扯到 metaclass(元类)
的。
2. 属性的访问控制
Python 定义私有属性,然后提供公共的set
、get
方法时,可以通过魔术方法
来实现封装.
方法 | 说明 |
---|
__getattr__(self, name) | 该方法定义了你试图访问一个不存在的属性时的行为。因此,重载该方法可以实现捕获错误拼写然后进行重定向, 或者对一些废弃的属性进行警告。 |
__setattr__(self, name, value) | 定义了对属性进行赋值和修改操作时的行为。不管对象的某个属性是否存在,都允许为该属性进行赋值.有一点需要注意,实现 __setattr__ 时要避免”无限递归”的错误, |
__delattr__(self, name) | __delattr__ 与 __setattr__ 很像,只是它定义的是你删除属性时的行为。实现 __delattr__ 是同时要避免”无限递归”的错误 |
__getattribute__(self, name) | __getattribute__ 定义了你的属性被访问时的行为,相比较,__getattr__ 只有该属性不存在时才会起作用。因此,在支持 __getattribute__ 的 Python 版本,调用__getattr__ 前必定会调用 __getattribute__``__getattribute__ 同样要避免”无限递归”的错误。 |
通过上面的方法表可以知道,在进行属性访问控制定义的时候你可能会很容易的引起一个错误,可以看看下面的示例:
1 2 3 4 5 6 7 8 9
| def __setattr__(self, name, value): self.name = value
def __setattr__(self, name, value): self.__dict__[name] = value
|
上面方法的调用案例:
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35
| class User: def __getattr__(self, name): print('调用了 __getattr__ 方法') return super(User, self).__getattr__(name)
def __setattr__(self, name, value): print('调用了 __setattr__ 方法') return super(User, self).__setattr__(name, value)
def __delattr__(self, name): print('调用了 __delattr__ 方法') return super(User, self).__delattr__(name)
def __getattribute__(self, name): print('调用了 __getattribute__ 方法') return super(User, self).__getattribute__(name)
if __name__ == '__main__': user = User() user.attr1 = True user.attr1 try: user.attr2 except AttributeError: pass del user.attr1
调用了 __setattr__ 方法 调用了 __getattribute__ 方法 调用了 __getattribute__ 方法 调用了 __getattr__ 方法 调用了 __delattr__ 方法
|
3. 对象的描述器
描述器: 有 __get__
、__set__
、__delete__
方法的对象
默认对属性的访问控制是从对象的字典里面(__dict__
)中获取(get),设置(set)和删除(delete)
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37
| class User: def __init__(self, name='Pupper', sex='男'): self.name = name self.sex = sex
def __get__(self, obj, objtype): print('获取 name 值') return self.name
def __set__(self, obj, value): print('设置 name 值') self.name = value
class MyClass: x = User() y = 5
if __name__ == '__main__': m = MyClass() print(m.x) print("------------------------------") m.x = 'Pupper Cheng' print(m.x) print("------------------------------") print(m.y)
获取 name 值 Pupper ------------------------------ 设置 name 值 获取 name 值 Pupper Cheng ------------------------------ 5
|
距离既可以用单位”米”表示,也可以用单位”英尺”表示。 现在我们定义一个类来表示距离,它有两个属性: 米和英尺
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56
| class Meter: def __init__(self, value=0.0): self.value = float(value)
def __get__(self, instance, owner): print('调用 meter 的 __get__ 方法') return self.value
def __set__(self, instance, value): print('调用 meter 的 __set__ 方法') self.value = float(value)
class Foot: def __get__(self, instance, owner): print('调用 foot 的 __get__ 方法') return instance.meter * 3.2808
def __set__(self, instance, value): print('调用 foot 的 __set__ 方法') instance.meter = float(value) / 3.2808
class Distance: meter = Meter() foot = Foot()
if __name__ == '__main__': d = Distance() print(d.meter, d.foot) print('----------------------------------------') d.meter = 1 print(d.meter, d.foot) print('----------------------------------------') d.meter = 2 print(d.meter, d.foot)
调用 meter 的 __get__ 方法 调用 foot 的 __get__ 方法 调用 meter 的 __get__ 方法 0.0 0.0 ---------------------------------------- 调用 meter 的 __set__ 方法 调用 meter 的 __get__ 方法 调用 foot 的 __get__ 方法 调用 meter 的 __get__ 方法 1.0 3.2808 ---------------------------------------- 调用 meter 的 __set__ 方法 调用 meter 的 __get__ 方法 调用 foot 的 __get__ 方法 调用 meter 的 __get__ 方法 2.0 6.5616
|
在上面例子中,在还没有对 Distance 的实例赋值前, 我们认为 meter 和 foot 应该是各自类的实例对象, 但是输出却是数值。这是因为 get 发挥了作用.
我们只是修改了 meter ,并且将其赋值成为 int ,但 foot 也修改了。这是 set 发挥了作用.
描述器对象 (Meter、Foot) 不能独立存在, 它需要被另一个所有者类 (Distance) 所持有。描述器对象可以访问到其拥有者实例的属性,比如例子中 Foot 的 instance.meter 。
4. 自定义容器(Container)
- 常见的容器类型:
dict
、tuple
、list
、string
- 不可变容器:
tuple
、string
- 可变容器:
dict
、list
功能 | 说明 |
---|
自定义不可变容器类型 | 需要定义 __len__ 和 __getitem__ 方法 |
自定义可变类型容器 | 在不可变容器类型的基础上增加定义 __setitem__ 和 __delitem__ |
自定义的数据类型需要迭代 | 需定义 __iter__ |
返回自定义容器的长度 | 需实现 __len__(self) |
自定义容器可以调用 self[key] ,如果 key 类型错误,抛出 TypeError ,如果没法返回 key 对应的数值时,该方法应该抛出 ValueError | 需要实现 __getitem__(self, key) |
当执行 self[key] = value 时 | 调用是 __setitem__(self, key, value) 这个方法 |
当执行 del self[key] 方法 | 其实调用的方法是 __delitem__(self, key) |
当你想你的容器可以执行 for x in container: 或者使用 iter(container) 时 | 需要实现 __iter__(self) ,该方法返回的是一个迭代器 |
使用上面魔术方法实现 Haskell 语言中的一个数据结构:
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52
| class FunctionalList: """实现了内置类型 list 的功能,并丰富了一些其他方法: head, tail, init, last, drop, take""" def __init__(self, values=None): if values is None: self.values = [] else: self.values = values
def __len__(self): return len(self.values)
def __getitem__(self, key): return self.values[key]
def __setitem__(self, key, value): self.values[key] = value
def __delitem__(self, key): del self.values[key]
def __iter__(self): return iter(self.values)
def __reversed__(self): return FunctionalList(reversed(self.values))
def append(self, value): self.values.append(value)
def head(self): return self.values[0]
def tail(self): return self.values[1:]
def init(self): return self.values[:-1]
def last(self): return self.values[-1]
def drop(self, n): return self.values[n:]
def take(self, n): return self.values[:n]
|
5. 运算符相关的魔术方法
5.1 比较运算符
魔术方法 | 说明 |
---|
__cmp__(self, other) | 如果该方法返回负数,说明 self < other ; 返回正数,说明 self > other ; 返回 0 说明 self == other 。强烈不推荐来定义 __cmp__ , 取而代之, 最好分别定义 __lt__ , __eq__ 等方法从而实现比较功能。 __cmp__ 在 Python3 中被废弃了。 |
__eq__(self, other) | 定义了比较操作符 == 的行为 |
__ne__(self, other) | 定义了比较操作符 != 的行为 |
__lt__(self, other) | 定义了比较操作符 < 的行为 |
__gt__(self, other) | 定义了比较操作符 > 的行为 |
__le__(self, other) | 定义了比较操作符 <= 的行为 |
__ge__(self, other) | 定义了比较操作符 >= 的行为 |
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53
| class Number(object): def __init__(self, value): self.value = value
def __eq__(self, other): print('__eq__') return self.value == other.value
def __ne__(self, other): print('__ne__') return self.value != other.value
def __lt__(self, other): print('__lt__') return self.value < other.value
def __gt__(self, other): print('__gt__') return self.value > other.value
def __le__(self, other): print('__le__') return self.value <= other.value
def __ge__(self, other): print('__ge__') return self.value >= other.value
if __name__ == '__main__': num1 = Number(2) num2 = Number(3) print('num1 == num2 ? --------> {} \n'.format(num1 == num2)) print('num1 != num2 ? --------> {} \n'.format(num1 == num2)) print('num1 < num2 ? --------> {} \n'.format(num1 < num2)) print('num1 > num2 ? --------> {} \n'.format(num1 > num2)) print('num1 <= num2 ? --------> {} \n'.format(num1 <= num2)) print('num1 >= num2 ? --------> {} \n'.format(num1 >= num2))
__eq__ num1 == num2 ? --------> False __eq__ num1 != num2 ? --------> False __lt__ num1 < num2 ? --------> True __gt__ num1 > num2 ? --------> False __le__ num1 <= num2 ? --------> True __ge__ num1 >= num2 ? --------> False
|
5.2 算数运算符
魔术方法 | 说明 |
---|
__add__(self, other) | 实现了加号运算 |
__sub__(self, other) | 实现了减号运算 |
__mul__(self, other) | 实现了乘法运算 |
__floordiv__(self, other) | 实现了 // 运算符 |
___div__(self, other) | 实现了/运算符. 该方法在 Python3 中废弃. 原因是 Python3 中,division 默认就是 true division |
__truediv__(self, other) | 实现了 true division. 只有你声明了 from __future__ import division 该方法才会生效 |
__mod__(self, other) | 实现了 % 运算符, 取余运算 |
__divmod__(self, other) | 实现了 divmod() 內建函数 |
__pow__(self, other) | 实现了 ** 操作. N 次方操作 |
__lshift__(self, other) | 实现了位操作 << |
__rshift__(self, other) | 实现了位操作 >> |
__and__(self, other) | 实现了位操作 & |
__or__(self, other) | 实现了位操作 ` | ` |
__xor__(self, other) | 实现了位操作 ^ |