簡體   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