为什么0 <()在Python中评估为True?

Lor*_*uer 0 python collections comparison logic boolean

我无意中输入time.clock<()了Python 2.7解释器响应:True.以下代码举例说明了该行为:

>>> repr(time.clock)
'<built-in function clock>'
>>> time.clock<()
True
Run Code Online (Sandbox Code Playgroud)

此外:

>>> import sys
>>> sys.maxint < ()
True

>>> map(lambda _:0<_,((),[],{}))
[True, True, True]
Run Code Online (Sandbox Code Playgroud)

相反:

>>> 1<set(())
TypeError: can only compare to a set
Run Code Online (Sandbox Code Playgroud)

问题:除了为什么,是否存在空洞的实际意义或目的list,tupledict评估好像是否大于任何数字?


更新:

  • Viktor指出默认情况下会比较内存地址:

    >>> map(lambda _:(id(0),'<',id(_)),((),[],{}, set([])))

    [(31185488L, '<', 30769224L), (31185488L, '<', 277144584L), (31185488L, '<', 279477880L), (31185488L, '<', 278789256L)]

尽管看似顺序,但这是不正确的.


  • Martijn Pieters指出:

如果没有定义明确的比较运算符,Python 2将使用Numbers和Type-names进行比较,其中数字的优先级最低.

这并未暗示正在调用的内部方法.另见这个有用但不确定的SO线程:

在IPython 2.7.5 REPL中

>>> type(type(()).__name__)
Out[15]: str

>>> type(()) < 10
Out[8]: False
>>> 10 < type(())
Out[11]: True
#as described
>>> type(()) < type(())
Out[9]: False
>>> type(()) == type(())
Out[10]: True

However:
>>> 'somestr' .__le__(10)
Out[20]: NotImplemented
>>> 'somestr' .__lt__(10)
Out[21]: NotImplemented

>>> int.__gt__
Out[25]: <method-wrapper '__gt__' of type object at 0x1E221000>
>>> int.__lt__
Out[26]: <method-wrapper '__lt__' of type object at 0x1E221000>

>>> int.__lt__(None)
Out[27]: NotImplemented
    #.....type(...), dir(...), type, dir......
#An 'int' instance does not have an < operator defined
>>> 0 .__lt__
Out[28]: AttributeError: 'int' object has no attribute '__lt__'

#int is actually a subclass of bool
>>>int.__subclasses__()
Out: [bool]
#str as the fallback type for default comparisons
>>> type(''.__subclasshook__)
Out[72]: builtin_function_or_method
>>> dir(''.__subclasshook__)
Out[73]: 
['__call__',
 '__class__',
 '__cmp__',
 '__delattr__',
 '__doc__',
 '__eq__',
 '__format__',
 '__ge__',
 '__getattribute__',
 '__gt__',
 '__hash__',
 '__init__',
 '__le__',
 '__lt__',
 '__module__',
 '__name__',
 '__ne__',
 '__new__',
 '__reduce__',
 '__reduce_ex__',
 '__repr__',
 '__self__',
 '__setattr__',
 '__sizeof__',
 '__str__',
 '__subclasshook__']
#IPython is subclassing 'str' 
>>> str.__subclasses__()
Out[84]: [IPython.utils.text.LSString]
Run Code Online (Sandbox Code Playgroud)

Mar*_*ers 5

在Python 2中,当比较不同类型时,python会在其他所有类型之前对数字类型进行排序,并在类型名称之间对其余排序类型进行排序.

因此,整数在元组之前排序,但类的实例Foo类的实例之后排序Bar.

Python 3消除了这种疯狂; 比较不同的类型会导致TypeError:

>>> 10 < ()
Traceback (most recent call last):
  File "<stdin>", line 1, in <module>
TypeError: unorderable types: int() < tuple()
Run Code Online (Sandbox Code Playgroud)

Python set()类型>通过实现__gt__或"大于时"魔术方法重载了运算符; 它被称为1 < set()表达式,因为int类型没有__lt__,低于 - 然后Python在这种情况下测试逆; 毕竟,x < y如果y > x是真的,那是真的.

set.__gt__()勾引起了TypeError当其它操作数不是set:

>>> 1 .__lt__(set())
Traceback (most recent call last):
  File "<stdin>", line 1, in <module>
AttributeError: 'int' object has no attribute '__lt__'
>>> set().__gt__(1)
Traceback (most recent call last):
  File "<stdin>", line 1, in <module>
TypeError: can only compare to a set
Run Code Online (Sandbox Code Playgroud)

>集合的重载(大于)运算符用于测试左侧操作数是否是右侧操作数的正确超集.(从技术上讲,set对象实现了C-API PyTypeObject.tp_richcompare函数,而不是__gt__直接实现了钩子,但__gt__钩子会tp_richcompare在这种情况下自动转换为调用).

当一个重载比较方法(之一.__lt__(),.__le__(),.__eq__(),. __ne__(),. __gt__(),. __ge__(),或. __cmp__())返回NotImplementedsingleton对象这预示着,比较不支持和Python回落到默认行为.这个默认行为,如Python比较运算符<和>如何使用函数名作为操作数?Python 2和3之间有所不同.

对于Python 3,返回的比较挂钩NotImplemented会导致Python引发TypeError:

>>> class Foo():
...     def __lt__(self, other): return NotImplemented
... 
>>> Foo() < Foo()
Traceback (most recent call last):
  File "<stdin>", line 1, in <module>
TypeError: unorderable types: Foo() < Foo()
Run Code Online (Sandbox Code Playgroud)

Python 2更顽固,当NotImplemented返回或没有实现钩子时,C代码最终在default_3way_compare()C函数中,其中:

  • 当两个对象的类型相同时,按内存地址排序(第768-776行)
  • 订单None前(780-783行)
  • 在其他类型之前订购数字(PyNumber_Check测试将类型名称设置为空,行786-793)
  • 按名称排序(v->ob_type->tp_namew->ob_type->tp_name第786-793行)
  • 如果类型名称相同,则按类型对象的内存地址排序(行800和801).

  • 此外,`set`示例失败,因为`<`是*not*小于集合的运算符,它被视为"is sub set"运算符,这显然对非设置操作数没有意义.(我相信某处的python文档明确声明该语言没有定义`<`,`>`等的语义,因此这种区别) (2认同)