繁体   English   中英

表示Python中的值范围

[英]Representing a Range of values in Python

我有兴趣在Python中表示一个类似于Guava的Range类型的Range 具体来说,它应该有一个起点和终点,并且代表两者之间的所有值(作为第一步,我很好,只代表标准的开闭范围,即[5,10) ,但是任何开路的正确表示/ closed range是一个合理的功能)。

我知道内置的range() ,但我的目的是支持任意类型(对于我的用例,具体来说是日期)。

从Python的类型层次结构看 ,似乎从逻辑上看,范围可以是SequenceSet类型,但我不确定哪个更有意义,如果最好是不将我的类放到该层次结构中并简单地实现我的行为,想。

Sequence

  • 非常适合该规范,这是一个“有限有序集”。
  • 可以对范围进行计数,切片和迭代。
  • 但是我可能希望支持无界范围,例如[0,+∞) ,所以上面的可能不正确。

作为一Set

  • 由于要明确指定范围,因此规格较少
  • 从概念上讲,它更像一个范围,因为像交集和联合这样的集合理论运算更有意义
  • 正确表示包含支票是有效的

作为单独的结构:

  • 我们失去了遵循上述类型的模式的好处(例如,我们必须定义一个单独的range.slice()方法)
  • 但是我们更加明确地指出,该结构也不应与这些类型混淆。 Guava的Range不能从Collection API扩展的事实似乎支持了这一论点。

我很好奇这里似乎是最Pythonic的东西,以及是否有人自己制作了这样的数据结构。

这是到目前为止我提出的实现。 Range对象表示任意的openClosed范围,并且可以哈希,包含和迭代,但是既不是序列也不是集合。 DateRange子类表示日期范围,主要是只需timedelta(days=1)增量参数定义为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)]

暂无
暂无

声明:本站的技术帖子网页,遵循CC BY-SA 4.0协议,如果您需要转载,请注明本站网址或者原文地址。任何问题请咨询:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM