我有兴趣在Python中表示一个范围,类似于Guava的Range
类型。具体来说,它应该有一个开始和结束,并代表两者之间的所有值(作为第一遍,我很好,仅表示规范的开放闭合范围,即[5,10)
,但任何开放的正确表示/封闭范围将是一个合理的特征)。
我知道range()
内置,但我的意图是支持任意类型(或特别是日期,对于我的用例)。
看看Python的type hierarchy,看起来范围可能是Sequence
或Set
类型的逻辑,但我不确定哪个更有意义,如果它会更好放弃把我的班级放到那个层次结构中,然后简单地实现我想要的行为。
作为Sequence
:
[0,+∞)
,所以上述情况可能不正确。作为Set
:
作为一个单独的结构:
range.slice()
方法)Range
没有从Collection API扩展的事实似乎支持了这个论点。我很好奇这里最恐怖的东西,如果有人自己制作了这样的数据结构。
答案 0 :(得分:1)
这是我到目前为止所提出的实施方案。 Range
对象表示任意openClosed范围,并且是可散列的,包含的,并且非常容易的,但既不是序列也不是集合。 DateRange
子类表示日期范围,主要只需要将增量参数定义为timedelta(days=1)
,而不仅仅是1
。
class Range:
'''
Represents a range, in the spirit of Guava's Range class.
Endpoints can be absent, and (presently) all ranges are openClosed.
There's little reason to use this class directly, as the range()
builtin provides this behavior for integers.
'''
def __init__(self, start, end, increment=1):
if start and end and end < start:
raise ValueError("End date cannot be before start date, %s:%s" % (start,end))
self.start = start
self.end = end
self.increment = increment
def __repr__(self):
return '[%s\u2025%s)' % (
self.start or '-\u221E',
self.end or '+\u221E'
)
def __eq__(self, other):
return self.start == other.start and self.end == other.end
def __hash__(self):
return 31*hash(self.start) + hash(self.end)
def __iter__(self):
cur = self.start
while cur < self.end:
yield cur
cur = cur + self.increment
def __contains__(self, elem):
ret = True
if self.start:
ret = ret and self.start <= elem
if self.end:
ret = ret and elem < self.end
return ret
class DateRange(Range):
'''A range of dates'''
one_day = timedelta(days=1)
@staticmethod
def parse(daterange):
'''Parses a string into a DateRange, useful for
parsing command line arguments and similar user input.
*Not* the inverse of str(range).'''
start, colon, end = daterange.partition(':')
if colon:
start = strToDate(start) if start else None
end = strToDate(end) if end else None
else:
start = strToDate(start)
end = start + DateRange.one_day
return DateRange(start, end)
def __init__(self, start, end):
Range.__init__(self, start, end, DateRange.one_day)
def strToDate(date_str):
'''Parses an ISO date string, such as 2014-2-20'''
return datetime.datetime.strptime(date_str, '%Y-%m-%d').date()
一些用法示例:
>>> DateRange(datetime.date(2014,2,20), None)
[2014-02-20‥+∞)
>>> DateRange(datetime.date(2014,1,1), datetime.date(2014,4,1))
[2014-01-01‥2014-04-01)
>>> DateRange.parse(':2014-2-20')
[-∞‥2014-02-20)
>>> DateRange.parse('2014-2-20:2014-3-22')
[2014-02-20‥2014-03-22)
>>> daterange = DateRange.parse('2014-2-20:2014-3-2')
>>> daterange
[2014-02-20‥2014-03-02)
>>> datetime.date(2014,1,25) in daterange
False
>>> datetime.date(2014,2,20) in daterange
True
>>> list(daterange)
[datetime.date(2014, 2, 20), datetime.date(2014, 2, 21), datetime.date(2014, 2, 22),
datetime.date(2014, 2, 23), datetime.date(2014, 2, 24), datetime.date(2014, 2, 25),
datetime.date(2014, 2, 26), datetime.date(2014, 2, 27), datetime.date(2014, 2, 28),
datetime.date(2014, 3, 1)]