Python比较运算符如何<和>使用函数名作为操作数?

时间:2013-08-22 18:15:40

标签: python

遇到这个问题(在Python 2.7.5中)有一点错字:

def foo(): return 3
if foo > 8:
    launch_the_nukes()
该死的,我不小心爆了月亮。

我的理解是E > F等同于(E).__gt__(F),等同于(F).__lt__(E)的表现良好的类(例如内置)。

如果没有__lt____gt__运算符,那么我认为Python使用__cmp__

但是,当function<运算符执行工作时,这些方法都不适用于>个对象。引发这种情况的是什么?

>>> foo > 9e9
True
>>> (foo).__gt__(9e9)
Traceback (most recent call last):
  File "<stdin>", line 1, in <module>
AttributeError: 'function' object has no attribute '__gt__'
>>> (9e9).__lt__(foo)
NotImplemented

3 个答案:

答案 0 :(得分:13)

  

但是,这些方法都不适用于函数对象,而&lt;和&gt;运营商做的工作。引发这种情况的是什么?

在任何其他合理比较的默认情况下,2.x系列中的CPython基于类型名称进行比较。 (这是documented as an implementation detail,虽然有一些有趣的例外情况只能找到in the source。)在3.x系列中,这将导致异常。

Python规范对2.x中的行为设置了一些特定的约束;按类型名称进行比较不是唯一允许的行为,其他实现可能会执行其他操作。这不是可以依赖的东西。

答案 1 :(得分:3)

对于未来的读者,我之所以发布此答案,是因为@wim对这个问题的悬赏是断言@Marcin's answerfunction < int会得出False的推理是错误的,以及 not True,如果按类型名称按字典顺序排序,这是可以预期的。

以下答案应阐明CPython实现的一些例外;但是,它仅与Python 2.x相关,因为此比较现在在Python 3.x +中引发异常。

比较算法

Python的比较算法非常复杂;当使用类型的内置比较功能比较两个类型不兼容时,它会在内部默认使用多个不同的功能,以试图找到一致的顺序;与该问题相关的是default_3way_compare(PyObject *v, PyObject *w)

default_3way_compare的实现对类型的对象名而不是它们的实际值(例如,如果类型ab在{{ 1}},它类似地在C代码内部执行a < b

但是,有一些不遵守此一般规则的例外情况:

  • type(a).__name__ < type(b).__name__始终被认为小于(即小于)任何其他值(当然不包括其他None,例如{{3} }。

  • 数值类型(例如Noneint等):从they are are all the same instance返回非零值的任何类型(也记录在PyNumber_Check中)将其类型名称解析为空字符串float而不是其实际类型名称(例如“ int”,“ float”等)。这要求数字类型在任何其他类型(不包括""之前,在之前排序。这似乎不适用于NoneType类型。

    例如,当将数字类型与带有语句complex的函数进行比较时,尽管预期会发生这种比较,但内部会比较为3 < foo()的字符串比较"" < "function"。由于按字典顺序,一般情况下的解析度True实际上是"int" < "function"。这种额外的行为促使了上述赏金,因为它违反了类型名称的预期字典顺序。

有关一些有趣的行为,请参见以下REPL输出:

False

更多示例(在Python 2.7.17中)

>>> sorted([3, None, foo, len, list, 3.5, 1.5])
[None, 1.5, 3, 3.5, <built-in function len>, <function foo at 0x7f07578782d0>, <type 'list'>]

输出:

from pprint import pprint
def foo(): return 3
class Bar(float): pass
bar = Bar(1.5)
pprint(map(
    lambda x: (x, type(x).__name__), 
    sorted(
        [3, None, foo, len, list, -0.5, 0.5, True, False, bar]
    )
))

其他见解

Python的比较算法在here的源代码中实现,并为要比较的两个对象调用Object/object.c。每个[(None, 'NoneType'), (-0.5, 'float'), (False, 'bool'), (0.5, 'float'), (True, 'bool'), (1.5, 'Bar'), (3, 'int'), (<built-in function len>, 'builtin_function_or_method'), (<function foo at 0x10c692e50>, 'function'), (<type 'list'>, 'type')] 实例都通过PyObject对其内置do_cmp(PyObject *v, PyObject *w)类型进行引用。 py_object->ob_type“实例”能够指定一个PyTypeObject比较函数,该函数评估给定tp_compare的两个相同对象的排序;例如,PyTypeObject的比较功能已注册PyTypeObject并已实现here。但是,此比较系统不支持定义各种不兼容类型之间的其他行为。

Python通过为here实现的不兼容对象类型实现自己的比较算法来弥合这种差距。有三种尝试比较类型而不是使用对象的int实现的尝试:do_cmp(PyObject *v, PyObject *w)try_rich_to_3way_compare,最后是try_3way_compare(在实现中,我们看到了这种有趣的行为问题)。

答案 2 :(得分:-2)

注意:这仅在Python 2.x中有效。在Python 3.x中,它给出“在'function'和'int'的实例之间不支持TypeError:'(比较运算符)'”。

在Python 2.x中,函数的大小无限大。例如,在Python 2.7.16 (repl.it)中,键入:

> def func():
...    return 0
...
> print(func)
<function func at 0xLOCATION>
> int(func)
Traceback (most recent call last):
  File "<stdin>", line 1, in <module>
TypeError: int() argument must be a string or a number, not 'function'
> id(func)
IDNUMBER
> x=id(func)
> func<x
False
> func==x
False
> inf=float("inf")
> func<inf
False

这表明'func'在Python 2.x中大于正无穷大。在Python 3.8.2 (repl.it)中尝试一下:

> def func():
...    return 0
...
> func<10
Traceback (most recent call last):
  File "<stdin>", line 1, in <module>
TypeError: '<' not supported between instances of 'function' and 'int'

这表明仅在Python 2.x中支持将函数作为操作数进行比较,并且在Python 2.x中比较函数时,它大于Python的无穷大。希望对您有帮助!