26

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

def foo(): return 3
if foo > 8:
    launch_the_nukes()

该死的,我不小心把月亮炸了。

我的理解是,对于表现良好的类(例如内置函数)来说,E > F它等价于.(E).__gt__(F)(F).__lt__(E)

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

但是,这些方法都不适用于function对象,而<and>运算符确实有效。导致这种情况发生的幕后原因是什么?

>>> 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
4

3 回答 3

17

但是,这些方法都不适用于函数对象,而 < 和 > 运算符确实有效。导致这种情况发生的幕后原因是什么?

默认情况下,2.x 系列中的 CPython 基于类型名称进行比较。(这被记录为实现细节,尽管有一些有趣的异常只能在源代码中找到。)在 3.x 系列中,这将导致异常。

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

于 2013-08-22T18:19:12.263 回答
4

对于未来的读者,我发布这个答案是因为@wim对这个问题给予了赏金,断言@Marcin的答案function < int与将评估为的推理是错误的False,而不是 True按照类型名称按字典顺序排序时所预期的那样。

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

比较算法

Python的比较算法非常复杂;当使用类型的内置比较函数进行比较时两种类型不兼容时,它在内部默认使用几个不同的函数以尝试找到一致的顺序;这个问题的相关问题是default_3way_compare(PyObject *v, PyObject *w)

for 的实现对default_3way_compare类型的对象名称而不是它们的实际值执行比较(使用字典顺序)(例如,如果类型ab在 中不兼容,它在 C 代码内部a < b类似地执行)。type(a).__name__ < type(b).__name__

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

  • None:总是被认为小于(即小于)任何其他值(None当然不包括其他值,因为它们都是相同的实例)。

  • 数字类型(例如int,float等):任何返回非零值的类型PyNumber_Check(也记录在这里)将其类型的名称解析为空字符串"",而不是它们的实际类型名称(例如“int”、“float”、 ETC)。这需要在任何其他类型(不包括)之前NoneType对数字类型进行排序。这似乎不适用于该complex类型。

    例如,当使用语句比较数值类型与函数时3 < foo(),比较内部会解析为 的字符串比较"" < "function",即True,尽管预期的一般情况解析"int" < "function"实际上是False因为字典顺序。这种额外的行为是促成上述赏金的原因,因为它违背了预期的类型名称的字典顺序。

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

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

更多示例(在 Python 2.7.17 中)

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]
    )
))

输出:

[(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')]

额外的见解

Python 的比较算法在Object/object.c的源代码中实现,并为正在比较的两个对象调用do_cmp(PyObject *v, PyObject *w)。每个PyObject实例都有一个对其内置PyTypeObject类型的引用py_object->ob_typePyTypeObject"instances" 能够指定一个tp_compare比较函数来评估相同给定的两个对象的排序PyTypeObject;例如,int的比较函数在这里注册在这里实现。但是,此比较系统不支持在各种不兼容类型之间定义附加行为。

Python 通过实现自己的用于不兼容对象类型的比较算法来弥补这一差距,实现于do_cmp(PyObject *v, PyObject *w). 有三种不同的尝试来比较类型而不是使用对象的tp_compare实现:try_rich_to_3way_comparetry_3way_compare和 finally default_3way_compare(我们在这个问题中看到这种有趣行为的实现)。

于 2020-10-13T07:07:56.440 回答
-2

注意:这只适用于 Python 2.x。在 Python 3.x 中,它提供:

TypeError:“function”和“int”的实例之间不支持“(比较运算符)”。

在 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 的无穷大。

于 2020-10-19T00:29:49.003 回答