计算广义范围的长度

时间:2015-03-23 14:19:18

标签: python

我开始编写一个名为grange的类作为广义范围 - 创建一系列日期等。

计算整数范围的长度很简单:(stop - start)//step

len(range(0, 8, 2)) - > (8 - 0)//2 - > (8)//2 - > 4

当然,处理否定步骤并开始>停止有点棘手,但一般的想法成立。

但是当我从整数的具体领域转移到“事物”的抽象领域时,我的思想开始融化。

我们假设一些事情:

  • 开始,停止,步骤所有定义__add____sub____truediv__,可在对象之间互操作。

  • 当组合如:(start - stop + step)//step时,我们得到一个裸数(int,float,complex等)。

  • 开始,停止或步骤+ 1可能引发TypeError,即timedelta(days=1) + 1

以下是我目前的情况:

def __len__(self):
    if self.stop is None:
        # 10/10 wish float('inf') was returnable
        raise TypeError("Infinite Range!")
    try:
        # factor in self.step to compensate to include start position
        if self._has_neg_step:
            calc = self.start - self.stop - self.step
        else:
            calc = self.stop - self.start + self.step
        # counteract float and negative steps
        return int(abs(calc//self.step))
    except (TypeError, ValueError):
        # throw a hail mary!
        # seems dangerous but should be *alright* since
        # bail out on infinite sequence case
        return len(list(iter(self)))

这适用于约会!

start, stop = datetime(2015, 3, 1), datetime(2015, 3, 8)
step = timedelta(days=2)
assert len(grange(start, stop, step)) == 4

但是用整数......

assert len(grange(0,8,1)) == 8, len(grange(0, 8, 1))
AssertationError: 9

最初,我考虑过抽象而不是一个:

self._one = step//step

所以我可以这样做:

(self.stop - self.start - self.step + self.one)//self.step

分开的东西就是一个!谢谢小学!但是有一个问题:timedelta(days=2)//timedelta(days=2) == 1不是timedelta(days=1)

原来我忘记了x unit/x unit也取消单位部分的部分。至少对于timedelta。谁知道其他对象会做什么?

我不相信通过实现广义算法是可能的(或者甚至可能!)但直接诉诸:len(list(iter(self)))似乎是浪费,但这似乎也是唯一的万无一失的解决方案。

似乎妥协是使用len(list(iter(self)))计算长度并将其存放在属性中:

# works because Python will bypass __getattribute__ when len is called.
@property
def __len__(self):
    if self.stop is None:
        raise TypeError('Infinite range')
    if not hasattr(self, '__len__'):
        self.__len__ = len(list(iter(self))
    return self.__len__

这消除了许多关于传递对象的假设。

有什么建议吗?

1 个答案:

答案 0 :(得分:2)

问题与类型无关:在len(grange(0,8,1))中,8的长度也被计算在内(因此0,1,2,3,4,5,6,7,8是9个数字) )。对于您的日期示例,这种情况不会发生,因为您不能从步骤2开始从1开始。

因此,如果calc是一个倍数(乘法?英语不是我的母语)的步骤,(正常div,而不是truediv)将长度减去1。

TL; DR你的len的行为就好像格兰奇包含止损,但你希望它排除止损给出意想不到的结果。