在 Python 中表示一系列值

问题描述 投票:0回答:2

我感兴趣的是在 Python 中表示一个范围,类似于 Guava 的

Range
类型。 具体来说,它应该有一个开始和结束,并表示两者之间的所有值(作为第一遍,我可以只表示规范的开闭范围,即
[5,10)
,但可以正确表示任何开/闭范围将是一个合理的特征)。

我知道

range()
内置,但我的目的是支持任意类型(或特别是日期,对于我的用例)。

看看Python的类型层次结构,从逻辑上看,范围似乎可以是

Sequence
Set
类型,但我不确定哪个更有意义,是否最好放弃将我的类硬塞进去层次结构并简单地实现我想要的行为。

作为

Sequence

  • 非常符合规范,它是一个“有限有序集”。
  • 可以对范围进行计数、切片和迭代。
  • 但是我可能想支持无界范围,例如
    [0,+∞)
    ,所以以上可能不是真的。

作为

Set

  • 稍微不符合规范,因为范围是明确排序的
  • 概念上更像是一个范围,因为像交集和并集这样的集合论操作更有意义
  • 正确表示包含检查是有效的

作为单独的结构:

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

我很好奇这里最Pythonic的是什么,以及是否有人自己制作了任何这样的数据结构。

python data-structures
2个回答
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)]

0
投票

pd.Interval是您要找的吗?

数字和日期时间快速演示:

import pandas as pd
import numpy as np

interval_0_1 = pd.Interval(left=0, right=1, closed='right')
print(interval_0_1)

vals = np.linspace(-.5,1.5,5)
pd.DataFrame({'value':vals, 'in interval ' + str(interval_0_1):[val in interval_0_1 for val in vals]})

output

interval_2000 = pd.Interval(left=pd.to_datetime('01-01-2000'), right=pd.to_datetime('01-01-2001'), closed='left')
print(interval_2000)

vals = pd.date_range(start=pd.to_datetime('01-01-2000'), end=pd.to_datetime('01-01-2001'), periods=3)
pd.DataFrame({'value':vals, 'in interval ' + str(interval_2000):[val in interval_2000 for val in vals]})

enter image description here

© www.soinside.com 2019 - 2024. All rights reserved.