繁体   English   中英

发电机的python发电机?

[英]python generator of generators?

我写了一个读取txt文件的类。 该文件由非空行块组成(让我们称之为“部分”),由空行分隔:

line1.1
line1.2
line1.3

line2.1
line2.2

我的第一个实现是读取整个文件并返回列表列表,这是一个列表列表,其中每个部分都是一个行列表。 这显然是非常可怕的记忆。

所以我重新将它作为列表生成器实现,即在每个循环中,我的类将内存中的整个部分作为列表读取并生成它。

这样更好,但在大型部分的情况下仍然存在问题。 所以我想知道我是否可以重新实现它作为发电机的发电机? 问题是这个类非常通用,它应该能够满足这两个用例:

  1. 读取一个非常大的文件,包含非常大的部分,并只循环一次。 发电机的发电机是完美的。
  2. 将一个小文件读入内存,以便多次循环。 列表生成器工作正常,因为用户可以只调用

    列表(MyClass的(file_handle))

但是,生成器的生成器在情况2中不起作用,因为内部对象不会转换为列表。

有没有比实现一个显式的to_list()方法更优雅的东西,它会将生成器的生成器转换为列表列表?

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
  • 或者使用黑暗的,秘密的黑客方法,我将在mo'(我需要写它),但不要这样做!

正如所承诺的,黑客攻击(对于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 ,我不_to_list_hack ,改变了__class__属性。

由于布局存在冲突,我们必须使用UserList类并为其所有方法设置阴影,这只是另一层crud。

基本上,请不要使用此hack。 不过,你可以像幽默一样享受它。

一种相当实用的方法是在创建时告诉“生成器的生成器”是否生成生成器或列表。 虽然这不像让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))

暂无
暂无

声明:本站的技术帖子网页,遵循CC BY-SA 4.0协议,如果您需要转载,请注明本站网址或者原文地址。任何问题请咨询:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM