我写了一个读取txt文件的类。该文件由非空行块组成(让我们称之为“部分”),由空行分隔:
line1.1
line1.2
line1.3
line2.1
line2.2
我的第一个实现是读取整个文件并返回列表列表,这是一个列表列表,其中每个部分都是一个行列表。 这显然是非常糟糕的记忆。
所以我将它重新实现为列表的生成器,即在每个循环中,我的类将内存中的整个部分作为列表读取并生成它。
这是更好的,但在大型部分的情况下仍然存在问题。所以我想知道我是否可以重新实现它作为发电机的发电机?问题是这个类非常通用,它应该能够满足这两个用例:
将一个小文件读入内存,以便多次循环。列表生成器工作正常,因为用户可以只调用
列表(MyClass的(file_handle))
但是,生成器的生成器在情况2中不起作用,因为内部对象不会转换为列表。
有没有比实现一个显式的to_list()方法更优雅的方法,它会将生成器的生成器转换为列表列表?
答案 0 :(得分:6)
Python 2:
map(list, generator_of_generators)
Python 3:
list(map(list, generator_of_generators))
或两者兼有:
[list(gen) for gen in generator_of_generators]
由于生成的对象是generator functions
,而不仅仅是生成器,您需要执行
[list(gen()) for gen in generator_of_generator_functions]
如果这不起作用,我不知道你在问什么。另外,为什么它会返回生成器函数而不是生成器本身呢?
因为在评论中你说你想避免list(generator_of_generator_functions)
神秘崩溃,这取决于你真正想要的东西。
以这种方式覆盖list
的行为不:要么存储子生成元素,要么
如果你确实遇到了崩溃,我建议每次主发电机迭代时用主发电机回路耗尽子发电机。这是标准做法,正是itertools.groupby
所做的,stdlib生成器生成器。
例如
def metagen():
def innergen():
yield 1
yield 2
yield 3
for i in range(3):
r = innergen()
yield r
for _ in r: pass
正如所承诺的,黑客攻击(对于Python 3,这次是'轮次):
from collections import UserList
from functools import partial
def objectitemcaller(key):
def inner(*args, **kwargs):
try:
return getattr(object, key)(*args, **kwargs)
except AttributeError:
return NotImplemented
return inner
class Listable(UserList):
def __init__(self, iterator):
self.iterator = iterator
self.iterated = False
def __iter__(self):
return self
def __next__(self):
self.iterated = True
return next(self.iterator)
def _to_list_hack(self):
self.data = list(self)
del self.iterated
del self.iterator
self.__class__ = UserList
for key in UserList.__dict__.keys() - Listable.__dict__.keys():
if key not in ["__class__", "__dict__", "__module__", "__subclasshook__"]:
setattr(Listable, key, objectitemcaller(key))
def metagen():
def innergen():
yield 1
yield 2
yield 3
for i in range(3):
r = Listable(innergen())
yield r
if not r.iterated:
r._to_list_hack()
else:
for item in r: pass
for item in metagen():
print(item)
print(list(item))
#>>> <Listable object at 0x7f46e4a4b850>
#>>> [1, 2, 3]
#>>> <Listable object at 0x7f46e4a4b950>
#>>> [1, 2, 3]
#>>> <Listable object at 0x7f46e4a4b990>
#>>> [1, 2, 3]
list(metagen())
#>>> [[1, 2, 3], [1, 2, 3], [1, 2, 3]]
这太糟糕了,我甚至不想解释它。
关键是你有一个可以检测它是否已被迭代的包装器,如果没有,你运行_to_list_hack
那个,我没想你,改变了__class__
属性。
由于布局存在冲突,我们必须使用UserList
类并隐藏其所有方法,这只是另一层。
基本上,请不要使用此黑客。不过,你可以像幽默一样享受它。
答案 1 :(得分:0)
一种相当实用的方法是在创建时告诉“生成器生成器”是否生成生成器或列表。虽然这不像让list
神奇地知道该怎么做一样方便,但它似乎比拥有一个特殊的to_list
函数更舒服。
def gengen(n, listmode=False):
for i in range(n):
def gen():
for k in range(i+1):
yield k
yield list(gen()) if listmode else gen()
根据listmode
参数,这可以用于生成生成器或列表。
for gg in gengen(5, False):
print gg, list(gg)
print list(gengen(5, True))