【问题标题】:Difference between len() and .__len__()?len() 和 .__len__() 之间的区别?
【发布时间】:2010-03-20 00:53:24
【问题描述】:

调用len([1,2,3])[1,2,3].__len__()有什么区别吗?

如果没有明显的区别,那么在幕后做了什么不同的事情?

【问题讨论】:

标签: python


【解决方案1】:

len 是一个获取集合长度的函数。它通过调用对象的__len__ 方法来工作。 __something__ 属性比较特殊,通常不那么顺眼,一般不应该直接调用。

在很久以前的某个时候决定获取某个东西的长度应该是一个函数而不是一个方法代码,理由是len(a) 的含义对初学者来说很清楚,但a.len() 的含义就不那么清楚了。当 Python 开始时,__len__ 甚至还不存在,len 是一种特殊的东西,可以处理几种类型的对象。无论这给我们留下的情况是否完全合理,它都将继续存在。

【讨论】:

    【解决方案2】:

    通常情况下,内置或运算符的“典型”行为是在所涉及的对象上调用(使用不同且更好的语法)合适的魔术方法(名称如__whatever__)。内置或运算符通常具有“附加值”(它能够根据所涉及的对象采用不同的路径)——在 len__len__ 的情况下,这只是对内置的一些健全性检查——魔术方法中缺少的那一点:

    >>> class bah(object):
    ...   def __len__(self): return "an inch"
    ... 
    >>> bah().__len__()
    'an inch'
    >>> len(bah())
    Traceback (most recent call last):
      File "<stdin>", line 1, in <module>
    TypeError: 'str' object cannot be interpreted as an integer
    

    当您看到对内置函数 len 的调用时,您确定如果程序在此之后继续而不是引发异常,则该调用返回一个整数,非- 否定和&lt;= sys.maxsize -- 当你看到对xxx.__len__() 的调用时,你并不确定(除非代码的作者不熟悉 Python 或不熟悉 Python;-)。

    除了简单的健全性检查和可读性之外,其他内置程序还提供更多附加值。通过将所有 Python 统一设计为通过调用内置函数和使用运算符来工作,而不是通过调用魔术方法,程序员可以免于记住哪种情况是哪种情况的负担。 (有时会出现错误:在 2.5 之前,您必须调用 foo.next() -- 在 2.6 中,虽然这仍然适用于向后兼容,但您应该调用 next(foo),而在 3.* 中,魔术方法被正确命名为 @ 987654331@ 而不是“oops-ey”next!-)。

    所以一般规则应该是永远不要直接调用魔法方法(但总是通过内置函数间接调用),除非你确切知道为什么需要这样做(例如,当你在子类中重写这样的方法时,如果子类需要推迟到必须通过显式调用魔术方法来完成的超类)。

    【讨论】:

    • 我是 Python 初学者用户(不是初学者所想的),我不确定“当你看到对内置 len 的调用时,如果程序在而不是引发异常”。我试过这个:def len(x): return "I am a string." print(len(42)) print(len([1,2,3])),它打印了两次I am string。你能再解释一下吗?
    • @DarekNędza 这和上面的无关,都是关于内置len的。你刚刚定义了你的 len 函数,它当然可以返回你想要的任何东西。 OP 谈到了内置 len,它在所考虑的对象上调用 __len__ 特殊 方法(不是函数)。
    • @Veky 我如何确定我调用的是内置函数len 而不是碰巧具有相同名称的其他函数(如我的示例) - len。没有像“您正在重新定义内置函数 len”或类似的警告。在我看来,我不能确定亚历克斯在他的回答中所说的内容。
    • Alex 明确表示如果你调用的是内置函数,那么你确定..._。他没有说要确保你调用的是内置的。但如果你想知道,你可以:len in vars(__builtins__).values().
    • 可悲的是,这是 Python 中缺少对象通用基类的另一个例子。语法上下文切换一直很疯狂。在某些情况下,使用下划线方法是常见的习惯用法,在其他情况下,应该使用类似函数的东西来执行许多对象共有的事情。这也很奇怪,因为许多对象对 len 没有语义用途。有时对象模型更像 C++,厨房下沉..
    【解决方案3】:

    你可以认为 len() 大致相当于

    def len(x):
        return x.__len__()
    

    一个优点是它允许您编写类似的东西

    somelist = [[1], [2, 3], [4, 5, 6]]
    map(len, somelist) 
    

    而不是

    map(list.__len__, somelist)
    

    map(operator.methodcaller('__len__'), somelist)
    

    但行为略有不同。 例如在整数的情况下

    >>> (1).__len__()
    Traceback (most recent call last):
      File "<stdin>", line 1, in <module>
    AttributeError: 'int' object has no attribute '__len__'
    >>> len(1)
    Traceback (most recent call last):
      File "<stdin>", line 1, in <module>
    TypeError: object of type 'int' has no len()
    

    【讨论】:

    • 我假设您的意思是 operator.methodcaller 而不是 operator.attrgetter
    【解决方案4】:

    您可以查看Pythond docs:

    >>> class Meta(type):
    ...    def __getattribute__(*args):
    ...       print "Metaclass getattribute invoked"
    ...       return type.__getattribute__(*args)
    ...
    >>> class C(object):
    ...     __metaclass__ = Meta
    ...     def __len__(self):
    ...         return 10
    ...     def __getattribute__(*args):
    ...         print "Class getattribute invoked"
    ...         return object.__getattribute__(*args)
    ...
    >>> c = C()
    >>> c.__len__()                 # Explicit lookup via instance
    Class getattribute invoked
    10
    >>> type(c).__len__(c)          # Explicit lookup via type
    Metaclass getattribute invoked
    10
    >>> len(c)                      # Implicit lookup
    10
    

    【讨论】:

      【解决方案5】:

      嗯,len(s) 是一个内置的 Python 方法,它返回对象的长度。现在__len__()是一个特殊的方法,被len(s)方法内部调用,返回一个对象的长度。

      所以,当我们调用len(s) 方法时,s.__len__() 是在幕后实际发生的事情来计算长度。

      Python的len()函数可以解释为:

      def len(s):
          return s.__len__()
      

      reference

      【讨论】:

        猜你喜欢
        • 2012-09-27
        • 2021-07-23
        • 1970-01-01
        • 2013-02-13
        • 2021-12-25
        • 2020-05-10
        • 2014-09-20
        • 2010-10-28
        • 2015-10-04
        相关资源
        最近更新 更多