代码之家  ›  专栏  ›  技术社区  ›  Greg Beech

为什么Python使用“魔术方法”?

  •  95
  • Greg Beech  · 技术社区  · 15 年前

    我最近一直在玩Python,我发现有一点奇怪,那就是“magic methods”的广泛使用,例如,为了使其长度可用,一个对象实现了一个方法, def __len__(self) ,然后在编写时调用它 len(obj) .

    我只是想知道为什么对象不能简单地定义 len(self) obj.len() ? 我相信Python这样做肯定有充分的理由,但作为一个新手,我还没有弄清楚它们是什么。

    7 回复  |  直到 6 年前
        1
  •  64
  •   Eli Bendersky    15 年前

    就我所知 len

    from the FAQ :

    功能(例如list.index()),但是 其他函数(例如len(list))?

    主要原因是历史。功能 对于一组类型和 甚至是为了 全部(例如元组)。它也是 方便有一个功能,可以 易于应用于无定形的 Python的函数特性(map(), apply()等人)。

    min()作为内置函数 将它们作为每种类型的方法。一个可以 它是Python的一部分,也是Python的一部分 做这种根本性的改变太晚了 现在。这些功能必须保留到 避免大量代码破坏。

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

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

    等等。。。

        2
  •  21
  •   AndiDog    15 年前

    来自Python的禅宗:


    应该有一个——最好只有一个——显而易见的方法。

    getLength() , length() , getlength() 或者别的什么。Python强制执行严格的命名,以便公共函数 len() 可以使用。

    对于许多类型的对象来说,所有常见的操作都被放入魔术方法中,比如 __nonzero__ , __len__ __repr__ . 不过,它们大多是可选的。

    运算符重载也可以通过魔术方法完成(例如。 __le__

        3
  •  15
  •   Peter Mortensen icecrime    6 年前

    Python使用这个词 “神奇的方法” ,因为这些方法对你的程序来说真的很神奇。使用Python的magic方法的最大优点之一是,它们提供了一种简单的方法,使对象的行为类似于内置类型。这意味着您可以避免执行基本运算符的丑陋、反直觉和非标准方式。

    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'
    

    这会产生一个错误,因为字典类型不支持加法。现在,让我们扩展dictionary类并添加 “添加” 魔术法:

    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'}
    

    因此,通过添加此方法,突然魔法发生了,您之前得到的错误消失了。

    A Guide to Python's Magic Methods (Rafe Ketter,2012年)

        4
  •  9
  •   flornquake    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
    

    你也可以100%确定 总是会返回真或假;如果你依靠一种方法,你不能完全确定你会得到什么。

    其他一些实现相对复杂的函数(比底层的magic方法可能更复杂)是 iter() cmp() getattr , setattr delattr ). 比如 int __int__ )但作为一种类型,你要承担双重责任。 len(obj) obj.__len__() .

        5
  •  4
  •   Stefano Borini    15 年前

    它们不是真正的“神奇名字”。它只是一个对象为提供给定服务而必须实现的接口。从这个意义上说,它们并不比您必须重新实现的任何预定义接口定义更神奇。

        6
  •  1
  •   Rosh Oxymoron    14 年前

    虽然原因主要是历史原因,但Python的 len 使用函数而不是方法是合适的。

    例如,Python中的一些操作是作为方法实现的 list.index dict.append ,而其他的则实现为可调用和魔术方法 str iter reversed . 这两个组的差异很大,因此不同的方法是合理的:

    1. 它们很常见。
    2. str公司 , int
    3. iter公司 可能会打电话 __getitem__ __iter__ 不可用,并且支持不适合方法调用的其他参数。出于同样的原因 it.next() next(it)
    4. 其中一些是运营商的近亲。调用有语法 __next__ -它被称为 for 循环。为了一致性,函数更好。它使某些优化变得更好。
    5. 有些函数在某些方面与其他函数过于相似- repr 表现得像 做。有 str(x) x.repr()
    6. 例如,其中一些很少使用实际的实现方法 isinstance
    7. getattr(x, 'a') 是另一种方式 x.a getattr 具有上述许多品质。

    说到这里, 伦恩 不完全适合第二组。它更接近于第一个操作,唯一的区别是它比几乎任何一个操作都更常见。但它唯一能做的就是打电话 __len__ ,非常接近 L.index __伦__ bool ,如果调用了方法 伦恩 bool(x) 有习惯的 伦恩 做完全不同事情的方法。

    简言之,在对象构造期间,类可以实现一组非常常见的特性,这些特性可以通过操作符、通过特殊函数(通常比操作符的实现做得更多)进行访问,所有这些特性都有一些共同的特性。剩下的都是方法。以及 伦恩

        7
  •  0
  •   Peter Mortensen icecrime    6 年前

    以上两个帖子没有太多的补充,但所有的“神奇”功能其实一点也不神奇。它们是内置模块的一部分,该模块在解释器启动时隐式/自动导入。即。:

    from __builtins__ import *
    

    每次程序启动前都会发生。

    我一直认为,如果Python只对交互式shell执行此操作,并且需要脚本从所需的内置程序中导入各个部分,那就更正确了。也可能不同的主处理方式在shell和交互式中会更好。不管怎样,请查看所有函数,看看没有它们会是什么样子:

    dir (__builtins__)
    ...
    del __builtins__