自定义迭代器类与生成器?
Posted
tags:
篇首语:本文由小常识网(cha138.com)小编为大家整理,主要介绍了自定义迭代器类与生成器?相关的知识,希望对你有一定的参考价值。
以下是说明。我想知道在可以定义生成器函数时是否有理由定义自定义迭代器类。
我需要迭代一个将每个元素转换为int的序列,例如
# seq is a sequence of strings or in general anything convertible to int
def f(seq):
# ...
g(int_iter(seq))
# iseq is a numeric sequence
def g(iseq):
it = iter(iseq)
# ...
我可以使用自定义迭代器类:
# iterator converting elements it iterates over to int
class int_iter:
def __init__(self, iterable):
self.it = iter(iterable)
def __iter__(self):
return self
def __next__(self):
return int(next(self.it))
或发电机功能:
def int_iter(seq):
return (int(i) for i in seq)
这些解决方案总是可以互换吗? 它们是否等效(时间和空间)? 在风格上,他们中的任何一个被认为更好吗?
谢谢!
如果我打算将其作为答案编写,那么让我们添加一些示例来演示差异。假设我们有一个简单的迭代:
source_list = list(range(10)) # [0, 1, 2, 3, 4, 5, 6, 7, 8, 9]
如果你想把它变成一个字符串列表,除了已经提到的为此目的设计的map()
之外还有很多方法 - 你可以做一个简单的生成器:
def gen_str(iterable): # this is equivalent to returning an in-line generator
for element in iterable:
yield str(element)
test_gen = gen_str(source_list)
for element in test_gen:
print(repr(element), end=" ")
# '0' '1' '2' '3' '4' '5' '6' '7' '8' '9'
或者你可以编写一个完整的迭代器类:
class iter_str(object):
def __init__(self, iterable):
self._iterable = iterable
self._iterator = self._get_iter(self._iterable)
def __iter__(self):
return self
def __next__(self):
return str(next(self._iterator))
@staticmethod
def _get_iter(iterable): # a generator for forward iteration
for element in iterable:
yield element
test_iter = iter_str(source_list)
for element in test_iter:
print(repr(element), end=" ")
# '0' '1' '2' '3' '4' '5' '6' '7' '8' '9'
到目前为止,它们是相同的 - 但是如果你想在迭代时跳过一些元素会发生什么?你不能指示生成器这样做,并且为了跳过你需要在迭代代码本身中添加跳过耗尽逻辑的元素,即:
test_gen = gen_str(source_list)
for element in test_gen:
if element == "5":
for _ in range(3):
next(test_gen)
continue
print(repr(element), end=" ")
# '0' '1' '2' '3' '4' '9'
另一方面,使用迭代器类,您可以通过添加一个简单的skip()
方法来封装您的控件,如:
def skip(self, elements=1):
for _ in range(elements):
next(self._iterator)
然后你可以优雅地做同样的事情:
test_iter = iter_str(source_list)
for element in test_iter:
if element == "5":
element = test_iter.skip(3)
continue
print(repr(element), end=" ")
# '0' '1' '2' '3' '4' '9'
但这只是冰山一角 - 如果你想在迭代中途停止生成字符串并使用原始数据会发生什么?没有办法通知生成器这样做(除非你通过传递一些外部控制变量来构建它),而对迭代器类的简单更改允许你这样做:
class iter_str(object):
def __init__(self, iterable, string_mode=True):
self._iterable = iterable
self.string_mode = string_mode
self._iterator = self._get_iter(self._iterable)
def __iter__(self):
return self
def __next__(self):
element = next(self._iterator)
if self.string_mode:
return str(element)
return element
@staticmethod
def _get_iter(iterable): # a generator for forward iteration
for element in iterable:
yield element
test_iter = iter_str(source_list)
for element in test_iter:
if element == "4":
test_iter.string_mode = False
print(repr(element), end=" ")
# '0' '1' '2' '3' '4' 5 6 7 8 9
通过这种方式,您可以对迭代添加任意控制,包括反转,重复迭代,甚至在迭代中途切换迭代器源等。简单的生成器不允许您在没有重大麻烦的情况下执行任何操作。
至于效率,从这个例子中可以明显看出,发电机效率更高,因为我们仍然依赖于内部发电机,但如果你需要控制你所需要的迭代的生命周期,性能损失将很快消失。添加更复杂的检查,通常会让您的生活变得悲惨,试图解决发电机限制问题。
我不会评论风格,但我声称,一般来说,最好使用最好的工具 - 如果你不需要生命周期控制你的iterable,继续使用生成器,如果你这样做 - 迭代器类是一种方法。
这一切都取决于您期望从对象中获得的功能。如果你只想要一次性迭代作为结果,这是在内存方面优化而不是像列表,元组等容器,最好的方法是使用生成器表达式。如果您希望您的对象可以多次迭代,您应该使用列表推导或其他等价物(设置理解等)。
如果您想要容器或生成器无法满足的更多功能,您应该使用自定义对象并将您期望的功能添加为类的不同方法。
以上是关于自定义迭代器类与生成器?的主要内容,如果未能解决你的问题,请参考以下文章
如何从片段中调用 getSupportFragmentManager()?