为什么Python使用“魔法方法”?
我对Python中广泛使用的“魔法方法”感到有点惊讶。
举个例子,如果一个类想要声明它的实例有“长度”,它就需要实现一个叫做 __len__
的方法。当你写 len(obj)
时,这个方法会被调用。那为什么不直接定义一个 len
方法,让它可以像对象的成员一样直接调用,比如 obj.len()
呢?
7 个回答
Python中有个词叫“魔法方法”,因为这些方法真的能为你的程序带来一些神奇的效果。使用Python的魔法方法最大的好处之一就是,它们能让对象像内置类型那样工作。这意味着你可以避免使用那些丑陋、不直观和不标准的方式来执行基本操作。
我们来看一个例子:
dict1 = {1 : "ABC"}
dict2 = {2 : "EFG"}
dict1 + dict2
Traceback (most recent call last):
File "python", line 1, in <module>
TypeError: unsupported operand type(s) for +: 'dict' and 'dict'
这个例子会报错,因为字典类型不支持加法。现在,我们来扩展字典类,添加“__add__”这个魔法方法:
class AddableDict(dict):
def __add__(self, otherObj):
self.update(otherObj)
return AddableDict(self)
dict1 = AddableDict({1 : "ABC"})
dict2 = AddableDict({2 : "EFG"})
print (dict1 + dict2)
现在,它会输出以下内容。
{1: 'ABC', 2: 'EFG'}
通过添加这个方法,突然间就像发生了魔法一样,之前的错误就消失了。
希望这样能让你更清楚。如果想了解更多信息,可以参考:
Python魔法方法指南(Rafe Kettler,2012)
来自Python的哲学:
面对不确定的情况,不要轻易猜测。
应该有一种——最好只有一种——明显的方法来解决问题。
这就是为什么开发者在自定义方法时,可以自由选择不同的方法名称,比如 getLength()
、length()
、getlength()
等等。Python要求严格的命名规则,这样大家就可以使用一个通用的函数 len()
。
所有对许多对象类型都常用的操作都放在了魔法方法里,比如 __nonzero__
、__len__
或 __repr__
。不过,这些方法大多数是可选的。
运算符重载也是通过魔法方法来实现的(例如 __le__
),所以用它们来处理其他常见操作也是很合理的。
据我所知,len
在这方面是特别的,而且它的使用有历史原因。
这里有一句来自常见问题解答的引用:
为什么Python对某些功能使用方法(比如list.index()),而对其他功能使用函数(比如len(list))?
主要原因是历史原因。函数被用来处理那些对一组类型通用的操作,这些操作甚至可以用于没有任何方法的对象(比如元组)。而且,当你使用Python的函数特性(比如map()、apply()等)时,使用一个可以直接应用于各种对象的函数会更方便。
实际上,把len()、max()、min()实现为内置函数,写的代码比把它们作为每种类型的方法实现要少。虽然可以对个别情况进行争论,但这就是Python的一部分,现在要做这样的根本性改变已经太晚了。这些函数必须保留,以避免大规模的代码崩溃。
其他的“魔法方法”(在Python的说法中其实叫做特殊方法)是非常有意义的,类似的功能在其他语言中也存在。它们主要用于在使用特殊语法时被隐式调用的代码。
例如:
- 重载运算符(在C++等语言中存在)
- 构造函数/析构函数
- 访问属性的钩子
- 元编程的工具
等等……