我开始编写一个名为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__
这消除了许多关于传递对象的假设。
有什么建议吗?
答案 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的行为就好像格兰奇包含止损,但你希望它排除止损给出意想不到的结果。