Python 2何时认为一个函数"大于"或"小于"另一个函数?
我刚发现,在Python中可以用比较符号 >
、<
、>=
和 <=
来比较任意的函数。这听起来有点奇怪;我本以为这样的比较结果总是 False
(或者会抛出异常),但文档上说:“大多数其他内置类型的对象在比较时如果不是同一个对象,通常会被认为是不相等的;至于一个对象被认为比另一个对象小或大,这个选择是任意的,但在程序的一次执行中是一致的。”
所以我做了一些实验,发现函数的定义顺序可能在这里很重要:
>>> def g():
pass
>>> def y():
pass
>>> g > y
False
>>> y > g
True
>>> def r():
pass
>>> g > r
False
>>> r > g
True
>>> y > r
False
>>> r > y
True
>>> def barfoo():
pass
>>> barfoo > r > y > g
True
我试着追踪源代码(在这里要说明一下,我的C语言经验只有两个月,完全是个菜鸟)。这个回答让我找到了 Python/ceval.c,这个文件似乎是用 PyObject_RichCompare()
(第4640行)来处理这些比较操作的。我找不到这个函数的定义,只找到了一份PEP文档,现在我卡住了。
我该如何预测这些看起来毫无意义的操作的结果呢?(顺便问一下……我为什么要这么做呢?)
附加内容:
>>> unicode > super > object > type > tuple > str > basestring > slice > frozenset > set > xrange > memoryview > long > list > int > staticmethod > classmethod > float > file > reversed > enumerate > dict > property > complex > bytearray > buffer > bool > zip > vars > unichr > sum > sorted > setattr > round > repr > reload > reduce > raw_input > range > pow > ord > open > oct > next > min > max > map > locals > len > iter > issubclass > isinstance > intern > input > id > hex > hash > hasattr > globals > getattr > format > filter > execfile > eval > divmod > dir > delattr > compile > coerce > cmp > chr > callable > bin > apply > any > all > abs > __import__ > help
True
4 个回答
我比较确定它使用了内置函数 id
,根据它的说明:“返回一个对象的身份。这在同时存在的对象中是唯一的。(提示:它是对象的内存地址。)”
这和你在文档中找到的不等式运算符的性质是一致的。
在我的系统上,这似乎取决于你定义函数的顺序。举个例子:
In [1]: def g():
...: pass
...:
In [2]: def y():
...: pass
...:
In [3]: g > y
Out[3]: False
In [4]: y > g
Out[4]: True
In [5]: (id(g), id(y))
Out[5]: (171432676, 171432844)
In [6]: id(g) > id(y)
Out[6]: False
对比:
In [1]: def y():
...: pass
...:
In [2]: def g():
...: pass
...:
In [3]: g > y
Out[3]: True
In [4]: y > g
Out[4]: False
In [5]: (id(g), id(y))
Out[5]: (165088140, 165087972)
In [6]: id(g) > id(y)
Out[6]: True
这个具体情况和Python是如何在其私有堆上为动态对象分配内存密切相关,但正如文档本身所说,这是一种比较对象的任意标准,尤其是对于那些不容易排序的对象,比如函数。
†至少对于CPython的实现来说
首先,要注意的是,这种行为在Python 3中更符合逻辑:
>>> def f(): pass
...
>>> def g(): pass
...
>>> f < g
Traceback (most recent call last):
File "<stdin>", line 1, in <module>
TypeError: unorderable types: function() < function()
至于Python 2中的行为,你已经引用了相关文档:
大多数内置类型的其他对象在比较时如果不是同一个对象,通常会被认为是不相等的;一个对象被认为比另一个对象小或大,这个选择是任意的,但在程序的一次执行中是保持一致的。
这里的关键是顺序是任意的,所以来回答你的问题:
我怎么能预测这些看似无意义的操作的结果呢?
别试图去预测,文档明确指出你观察到的任何排序都是不可靠的,并且可能会因为不同的解释器、版本,甚至是同一个程序的不同执行而改变。
至于在CPython中这是如何实现的,实际上是基于id()
函数,这个函数本质上是内存中的一个地址。有关更多信息,请查看Python是如何比较函数的?
在Python 2中,这种类型的比较是根据对象的id()
值来进行的:
In [1]: def g():
...: pass
In [2]: def y():
...: pass
In [3]: g > y
Out[3]: True
In [4]: id(g)
Out[4]: 55898312
In [5]: id(y)
Out[5]: 54420736
id()
的值通常取决于函数对象的内存地址,而这个地址可能会受到一些随机因素的影响,比如垃圾回收器的历史记录。可能正因为这个原因,Python的开发者在Python 3中去掉了这种功能,所以在Python 3中比较函数会出现错误:
In [3]: g > y
---------------------------------------------------------------------------
TypeError Traceback (most recent call last)
/home/xxx/<ipython-input-3-9ebc8ff65838> in <module>()
----> 1 g > y
TypeError: unorderable types: function() > function()
在Python 3中,比较相等性仍然是合法的,因为它不再依赖于id()
的随机值:
In [4]: g == y
Out[4]: False
In [5]: g != y
Out[5]: True