Python 2何时认为一个函数"大于"或"小于"另一个函数?

5 投票
4 回答
809 浏览
提问于 2025-04-17 21:41

我刚发现,在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 个回答

1

我比较确定它使用了内置函数 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的实现来说

2

首先,要注意的是,这种行为在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是如何比较函数的?

4

关键在于:

随意但一致

Python通过 id 来进行比较,所以如果

str > list

那么

id(str) > id(list)

(而且我得到的结果是 False,这是你的额外内容!)

4

在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

撰写回答