在Python中为任何对象创建正无穷和负无穷

4 投票
3 回答
1038 浏览
提问于 2025-04-17 07:59

我正在开发一个库,这个库实现了一种可以处理任何有序数据类型的数据结构——范围集合。很多操作(比如反转)在允许正无穷和负无穷的情况下会变得很有趣。

我的一个目标是让日期时间对象能够与这个模块一起使用。在支持非数字对象的无穷大时,我创建了 INFINITY 和 NEGATIVE_INFINITY:

class _Indeterminate(object):
    def __eq__(self, other):
        return other is self

@functools.total_ordering
class _Infinity(_Indeterminate):
    def __lt__(self, other):
        return False
    def __gt__(self, other):
        return True
    def __str__(self):
        return 'inf'
    __repr__ = __str__

@functools.total_ordering
class _NegativeInfinity(_Indeterminate):
    def __lt__(self, other):
        return True
    def __gt__(self, other):
        return False
    def __str__(self):
        return '-inf'

INFINITY = _Infinity()
NEGATIVE_INFINITY = _NegativeInfinity()

不幸的是,当日期时间对象在 cmp() 操作的左边时,这个方法并不奏效:

In [1]: from rangeset import *
In [2]: from datetime import datetime
In [3]: now = datetime.now()
In [4]: cmp(INFINITY, now)
Out[4]: 1
In [5]: cmp(now, INFINITY)
---------------------------------------------------------------------------
TypeError                                 Traceback (most recent call last)
/home/axiak/Documents/rangeset/<ipython-input-5-c928d3687d92> in <module>()
----> 1 cmp(now, INFINITY)

TypeError: can't compare datetime.datetime to _Infinity

我原本希望通过使用一个 cmp 包装器来解决这个限制,确保我的对象总是被调用,但我真的想使用 .sort() 方法,这样会在这些对象之间调用 cmp。

有没有办法创建一个对象,使其在任何其他对象面前都是真正小于的,同时在任何其他对象后面都是真正大于的呢?

模块主页: https://github.com/axiak/py-rangeset

3 个回答

0

问题在于 cmp(now, INFINITY) 实际上等同于 datetime.__cmp__(INFINITY),这个比较是直接在 datetime 类里定义的。你可以通过修改 datetime 模块来绕过这个问题,但这样做其实有点像是黑客行为。

我觉得你真正想要的是一个排序函数,它能够考虑到你的类,并根据无穷大的正负号总是把它放在前面或后面。

def order(x, y):
    if isinstance(x,_Infinity):
        return -1
    if isinstance(y, _Infinity):
        return 1
    elif isinstance(x, _NegativeInfinity):
        return 1
    elif isinstance(y, _NegativeInfinity):
         return -1
    else:
        return cmp(x,y)

>>> sorted([datetime.datetime.now(), datetime.datetime.now(), INFINITY, NEGATIVE_INFINITY], cmp=order)
[ 
    NEGATIVE_INFINITY, 
    datetime.datetime(2011, 12, 8, 13, 38, 47, 428626),
    datetime.datetime(2011, 12, 8, 13, 38, 47, 428661),
    INFINITY
]
0

我不太确定,但你可以试着重写一下 __eq____ne__(或者 __cmp__),看看在你使用比较的时候它们是否会被调用。你还需要知道,cmp__cmp__ 在 Python 3 中已经被去掉了。

5

来自 文档

为了避免比较时退回到默认的方式(也就是比较对象的地址),日期比较通常会在另一个比较对象不是日期对象时引发类型错误(TypeError)。不过,如果另一个比较对象有一个叫做 timetuple() 的属性,那么就会返回 NotImplemented

所以,为了让日期时间对象可以进行比较,你需要添加一个 timetuple 方法,例如:

class _Infinity(object):

    def __lt__(self, other):
        return False

    def __gt__(self, other):
        return True

    def timetuple(self):
        return tuple()

import datetime
INF = _Infinity()
now = datetime.datetime.now()
print cmp(INF, now)
print cmp(now, INF)

输出:

1    
-1

撰写回答