Python 常用魔法方法
https://pyzh.readthedocs.io/en/latest/python-magic-methods-guide.html#id2
为了实现更好的可扩展性,Python 语言提供了大量的特殊方法,它们大致可分为以下几类:
特性访问(Attribute Access)这类特殊方法实现了对象的特性访问,使用方法为
object.attribute
,既可以用来赋值,也可以在 del 语句中执行删除操作。可调用对象(Callables): 这个方法的适用对象为参数,就像 Python 内部的 len() 函数
集合(Collections)这类方法提供了很多集合操作的功能。类似这类方法的使用有 sequence[index]、mapping[key] 和 some_set | another_set
数字(Numbers): 这类方法提供了大量的数学运算符和比较运算符。
上下文(Context):这类函数通常使用 with 语句来实现上下文的管理
迭代器(Iterator):可以使用这类方法定义迭代器
构造方法
__new__
在我们调用 x=SomeClass()
的时候,__init__
并不是第一个被调用的方法,事实上,第一个被调用的是 __new__
,这个方法才真正地创建了实例。当这个对象的生命周期结束的时候,__del__
会被调用。
__new__(cls, [...])
__new__
是对象实例化时第一个调用的方法,它只取下cls
参数,并将其它参数传给__init__
。__init__
很少使用,但是它也有它合适的场景,尤其是当类继承自一个像元组或者字符串这样不经常改变的类型的时候。但是其并不是很有用。
__init__
对象的初始化
__init__
方法的参数可以多种形式来完成赋值
object 类是所有类的基类,对于任何自定义类,都会隐式继承 object。
1 | class X: |
对于每个 __init__
方法,都应当显式地指定要初始化的变量。
__del__
__new__
和 __init__
是对象的构造器,__del__
是对象的销毁器。它并非实现了语句 del x
而是定义了当对象被垃圾回收时的行为。
1 | # __new__ 方法实现单例模式 |
集合、序列相关
有许多办法可以让你的 Python 类表现得像是内建序列类型(字典,元组,列表,字符串等)
在 Python 中,协议完全是非正式的,也不需要显式的声明,事实上,它们更像是一种参考标准。
在 Python 中实现自定义容器类型需要用到一些协议。首先,不可变容器类型有如下协议:想实现一个不可变容器,你需要定义__len__
和 __getitem__
。可变容器的协议除了上面提到的两个方法之外,还需要定义 __setitem__
和 __delitem__
。最后,如果你想让你的对象可迭代,你需要定义__iter__
,这个方法返回一个迭代器。迭代器必须遵守迭代器协议,需要定义 __iter__
和 next 方法。
__len__(self)
返回容器的长度,可变和不可变类型都需要实现
__getitem__(self, key)
定义对容器中某一项使用
self[key]
的方式读取操作时的行为。这也是可变和不可变容器类型都需要实现的一个方法。它应该在键的类型错误时产生 TypeError 异常,同时在没有与键值相匹配的内容时产生 KeyError 异常__setitem__(self, key)
定义对容器中某一项使用 self[key] 的方式进行赋值操作时的行为。它是可变容器类型必须实现的一个方法,同样应该在合适的时候产生 KeyError 和 TypeError 异常
__reversed__(self)
定义了对容器使用
reversed()
内建函数时的行为。它应该返回一个反转之后的序列。__contains__(self, item)
定义了使用 in 和 not in 进行成员测试时的行为。
__missing__(self, key)
__missing__
在字典的子类中使用,它定义了当试图访问一个字典中不存在的键的行为。例如 d[“george”] 中不存在 george key 时,就会调用d.__missing__
迭代相关
__iter__
它应该返回当前容器的一个迭代器。迭代器以一连串内容的形式返回,最常见的是使用
iter()
函数调用,以及在类似 for x in container 的循环中被调用。__next__
这个方法每次返回迭代的值,在没有可迭代元素的时候,抛出StopIteration
异常
实现迭代器协议的例子:1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17class A:
"""A 实现了迭代器协议 它的实例就是一个迭代器"""
def __init__(self, n):
self.idx = 0
self.n = n
def __iter__(self):
print("__iter__")
return self
def __next__(self):
if self.idx < self.n:
val = self.idx
self.idx += 1
return val
else:
raise StopIteration()可调用对象
比较操作符
使用 Python 魔法方法的一个巨大优势就是可以构建一个拥有 Python 内置类型行为的对象。这意味着你可以避免使用非标准的、丑陋的方式来表达简单的操作。
__cmp__
是所有比较魔法方法中最基础的一个,它实际上定义了所有比较操作符的行为(<, ==, != 等等)__lt__
__le__()
__eq__
__ne__
__gt__()
__get__
访问控制 && 属性相关
Python 不是通过显式定义的字段和方法修改器,而是通过魔法方法实现了一系列的封装
_getattr__(self, name)
当用户试图访问一个根本不存在的属性时,你可以通过这个魔法方法来定义类的行为。这个可以用于捕捉错误的拼写并且给出指引。只有当试图访问不存在的属性时它才会被调用。
__setattr__(self, name, value)
它可以用于真正意义上的封装。允许你自定义某个属性的赋值行为,不管这个属性存在与否,也就是说你可以对任意属性的任何变化都定义自己的规则。
__delattr__(self, name)
用于处理删除属性时的行为。和
__setattr__
一样,使用它时也需要多加小心,防止产生无限递归__getattribute__
看起来和上面那些方法很合得来,但是最好不要使用它。允许你自定义属性被访问时的行为,它也同样可能遇到无限递归问题。
__getattribute__
基本上可以替代__getattr__
只有当它被使用,并且显式地被调用,或者产生 AttributeError 时它才被调用。不推荐被使用
与 Python 无缝集成——基本特殊方法
Python 中有一些特殊方法,它们允许我们的类和 Python 更好地集成。在标准库参考中,它们被称为基本特殊方法,是与 Python 的其他特性无缝集成的基础
例如:
__repr__
__str__
标准化表示对象的值__hash__
__bool__
__byts__
转换方法__lt__
__le__()
__eq__
__ne__
__gt__()
__get__
__new__
__del__
__repr__
&& __str__
方法
对于一个对象,Python 提供了两种字符串表示方法。它们和内建函数
repr()
str()
string.format()
功能是一致的
https://stackoverflow.com/questions/1436703/what-is-the-difference-between-str-and-repr
通常,
str()
方法表示的对象对用户更加友好。这个方法是由对象的__str__
方法实现的repr()
方法的表示通常会更加技术化,甚至有可能是一个完整的 Python 表达式容器
__str__
使用包含的对象__repr__
默认情况下,二者的行为一致
1 | >> import datetime |
上下文管理器
上下文管理的概念在 Python 中并不是全新引入的(之前它作为标准库的一部分实现),直到 PEP 343 才被接受,它才成为一种一级的语言结构
类似:
1 | with. open("f.txt") as bar: |
当对象使用 with 声明创建时,上下文管理器允许类做一些设置和清理工作。上下文管理器的行为由下面两个魔法方法所定义:
__enter__
定义使用 with 声明创建的语句块最开始上下文管理器应该做些什么。
__enter__
的返回值会赋给 with 声明的目标,也就是 as 之后的东西。__exit__
1 | class MyContext: |
refs
https://pyzh.readthedocs.io/en/latest/python-magic-methods-guide.html#id5