为什么Python使用'魔术方法'?

Gre*_*ech 97 python magic-methods

我最近一直在玩Python,有一件事我发现有点奇怪的是广泛使用'魔术方法',例如使其长度可用,一个对象实现一个方法,def __len__(self)然后它被调用时你写的len(obj).

我只是想知道为什么对象不是简单地定义一个len(self)方法并让它直接作为对象的成员调用,例如obj.len()?我确信Python必须有充分的理由这样做,但作为一个新手,我还没有弄清楚它们到底是什么.

Eli*_*sky 63

AFAIK len在这方面很特别,具有历史根源.

这是FAQ的引用:

为什么Python使用某些功能的方法(例如list.index())但是其他功能(例如len(list))?

主要原因是历史.函数用于那些对一组类型通用的操作,这些操作甚至可以用于根本没有方法的对象(例如元组).当您使用Python的功能特性(map(),apply()等)时,拥有一个可以很容易地应用于无定形对象集合的函数也很方便.

事实上,实现len(),max(),min()作为内置函数实际上比将它们作为每种类型的方法实现它们的代码更少.人们可以对个别案例嗤之以鼻,但它是Python的一部分,现在进行这样的根本性改变为时已晚.必须保留这些功能以避免大量代码破坏.

其他"神奇的方法"(实际上称为Python民间传说中的特殊方法)很有意义,其他语言也存在类似的功能.它们主要用于在使用特殊语法时隐式调用的代码.

例如:

  • 重载运算符(存在于C++和其他)
  • 构造函数/析构函数
  • 用于访问属性的钩子
  • 元编程工具

等等...

  • [Python和最小惊讶原理](http://lucumr.pocoo.org/2011/7/9/python-and-pola/)对于这种方式的Python的一些优点是一个很好的阅读(虽然我承认英语需要工作).基本点:它允许标准库实现大量代码,这些代码变得非常,非常可重用但仍然可以覆盖. (2认同)

And*_*Dog 20

来自Python的禅宗:

面对模棱两可,拒绝猜测的诱惑.
应该有一个 - 最好只有一个 - 明显的方法来做到这一点.

这是原因之一-自定义方法,开发人员可以自由选择不同的方法的名称,如getLength(),length(),getlength()或任何责任.Python强制执行严格命名,以便len()可以使用公共函数.

对于许多类型的对象而言常见的所有操作都被放入魔术方法中,例如__nonzero__,__len____repr__.但它们大多是可选的.

运算符重载也是通过魔术方法完成的(例如__le__),因此将它们用于其他常见操作也是有意义的.


Man*_*hit 15

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'
Run Code Online (Sandbox Code Playgroud)

这会产生错误,因为字典类型不支持添加.现在,让我们扩展字典类并添加"__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)
Run Code Online (Sandbox Code Playgroud)

现在,它给出了以下输出.

{1: 'ABC', 2: 'EFG'}
Run Code Online (Sandbox Code Playgroud)

因此,通过添加这种方法,突然发生了魔法并且你早先得到的错误已经消失了.

我希望,它能让你清楚.有关更多信息,请参阅:

Python魔术方法指南(Rafe Kettler,2012)


Ian*_*ing 9

其中一些函数可以执行多个单独的方法(没有超类上的抽象方法).例如bool()行为有点像这样:

def bool(obj):
    if hasattr(obj, '__nonzero__'):
        return bool(obj.__nonzero__())
    elif hasattr(obj, '__len__'):
        if obj.__len__():
            return True
        else:
            return False
    return True
Run Code Online (Sandbox Code Playgroud)

您也可以100%确定bool()始终返回True或False; 如果你依靠一种方法,你就不能完全确定你会得到什么.

其他一些具有相对复杂实现的函数(可能比底层的魔术方法更复杂)是iter()cmp(),以及所有属性方法(getattr,setattrdelattr).喜欢的东西int做强制(可以实现时也获得神奇的方法__int__),但完成双重任务的类型. len(obj)实际上是一个我不相信它与之不同的案例obj.__len__().

  • 而不是`hasattr()`我会使用`try:`/`除了AttributeError:`而不是`if obj .__ len __():返回True else:return False`我只想说`return obj .__ len __() > 0`但这些只是风格上的东西. (2认同)