我想学习Iterable和Iterators,因此我想把我的手弄脏,而不是读理论而忘记它。
我很好奇如何提高代码的可读性和错误处理。我还想练习并包括测试用例。
class foo_range(object):
""" Custom range iterator which mimics native xrange. """
def __init__(self, start, stop, step=1):
try:
self.current = int(start)
self.limit = int(stop)
self.step = int(step)
except ValueError:
raise
if step == 0:
raise ValueError("Step can't be 0")
def __iter__(self):
return self
def next(self):
if self.step > 0 and self.current >= self.limit:
raise StopIteration()
if self.step < 0 and self.current <= self.limit:
raise StopIteration()
oldvalue = self.current
self.current += self.step
return oldvalue
import unittest
class TestXRange(unittest.TestCase):
def test_invalid_range(self):
with self.assertRaises(ValueError) as ve:
foo_range(0, 5, 0)
def test_valid_range(self):
expected = [0, 1, 2]
actual = []
for _ in foo_range(0, 3):
actual.append(_)
self.assertEqual(actual, expected)
expected = [0, -1, -2, -3]
actual = []
for _ in foo_range(0, -4, -1):
actual.append(_)
self.assertEqual(actual, expected)
expected = []
actual = []
for _ in foo_range(0, 5, -1):
actual.append(_)
self.assertEqual(actual, expected)
expected = []
actual = []
for _ in foo_range(0, -5, 1):
actual.append(_)
self.assertEqual(actual, expected)
if __name__ == '__main__':
unittest.main()发布于 2015-10-15 09:49:15
请注意,xrange是可迭代的。可以在单个xrange实例上多次迭代。相反,foo_range是一个迭代器,因此只能迭代一次。
>>> xr = xrange(5)
>>> list(xr)
[0, 1, 2, 3, 4]
>>> list(xr)
[0, 1, 2, 3, 4]
>>> fr = foo_range(0,5)
>>> list(fr)
[0, 1, 2, 3, 4]
>>> list(fr)
[]要解决这个问题,__iter__应该返回一个新的迭代器对象。实现这一目标的一个简单方法是使__iter__成为值的生成器。或者,将类重命名为foo_range_iterator,并添加一个新的foo_range类,其__iter__返回一个新的foo_range_iterator实例。
发布于 2015-10-15 08:32:49
我不知道为何要这样做:
except ValueError:
raise您有以下模式:
actual = []
for _ in foo_range(XXX):
actual.append(_)在一些地方。
它可以很容易地被以下所取代:
actual = [_ for _ in foo_range(XXX)].另外,_通常用作抛出值的变量名(不使用的值)。在您的示例中,由于实际使用了该值,因此最好将其命名为(a、e、val、i等)。
此外,我认为这相当于:
actual = list(foo_range(XXX))我让你考虑一下。
您的def test_valid_range(self):测试测试了多个方面。最好将其分成多个较小的测试。在其他方面,如果出了问题就更容易理解,但它也帮助您思考实际要测试的内容(迭代向前,向后迭代等等)。
尝试使用用yield定义的生成器来实现这一点可能很有趣。
发布于 2015-10-15 14:31:30
所有这些中缺少的一个特性可能是xrange()最常见的用法:
xrange(停止) xrange(开始,停止步进)
你错过了第一个案子!我不能写这样的东西:
for i in foo_range(10):
# stuff你应该增加对此的支持。
xrange()类型检查它的输入。例如,如果我尝试执行xrange(10.1),它会抛出一个TypeError。由于支持浮点范围的浮点比较的所有问题,您可能希望或不希望添加此内容。
对于步骤的值有0、1和-1的测试。对于类似于5的步骤值,您可能至少应该有一个测试。
这是次要的,但你有:
if self.step > 0 and self.current >= self.limit:
raise StopIteration()
if self.step < 0 and self.current <= self.limit:
raise StopIteration()这些不是独立的检查,这表明第二个至少应该是一个elif。但既然身体是一样的,我建议把它们结合起来:
if (self.step > 0 and self.current >= self.limit or
self.step < 0 and self.current <= self.limit):
raise StopIteration()https://codereview.stackexchange.com/questions/107639
复制相似问题