遇到这个问题(在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
答案 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 answer与
function < int
会得出False
的推理是错误的,以及 notTrue
,如果按类型名称按字典顺序排序,这是可以预期的。
以下答案应阐明CPython实现的一些例外;但是,它仅与Python
2.x
相关,因为此比较现在在Python3.x
+中引发异常。
Python的比较算法非常复杂;当使用类型的内置比较功能比较两个类型不兼容时,它会在内部默认使用多个不同的功能,以试图找到一致的顺序;与该问题相关的是default_3way_compare(PyObject *v, PyObject *w)
。
default_3way_compare
的实现对类型的对象名而不是它们的实际值(例如,如果类型a
和b
在{{ 1}},它类似地在C代码内部执行a < b
。
但是,有一些不遵守此一般规则的例外情况:
type(a).__name__ < type(b).__name__
:始终被认为小于(即小于)任何其他值(当然不包括其他None
,例如{{3} }。
数值类型(例如None
,int
等):从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的无穷大。希望对您有帮助!