1

今天是发电机。我今天看到一个问题,想找到一种方法来递归地展平列表而不使用循环和导入。tobias_k用以下代码回答它:

def flatten(test_list):
    if isinstance(test_list, list):
        if len(test_list) == 0:
            return []
        first, rest = test_list[0], test_list[1:]
        return flatten(first) + flatten(rest)
    else:
        return [test_list]

有没有办法创建一个生成器(保持规则:没有导入,循环)?

注意:它纯粹是教育性的。我知道这不是最好的主意,但不知道该怎么做。

4

3 回答 3

4

在 Python 3.3(开发版)中,您可以使用该yield from构造来避免显式循环:

def flatten(x):
    if isinstance(x, list):
        if x:
            first, rest = x[0], x[1:]
            yield from flatten(first)
            yield from flatten(rest)
    else:
        yield x

在当前版本中,我想不出不使用itertools.chain.

于 2012-09-18T14:16:59.887 回答
4

生成器函数是一个函数,它至少包含一条yield语句,并且不包含return采用表达式的语句。当一个生成器函数被调用时,它返回一个生成器迭代器,它在迭代时(例如,通过for循环,或显式地使用next)遍历函数体,冻结其状态并将控制权返回给每个yield语句的调用者(在 Python 3.3 中) ,yield from陈述)。

Python 函数中的流控制总是向前的;如果没有设置当前帧之类的技巧f_lineno(正如(愚人节)goto语句所做的那样),控制到达较早点的唯一方法是使用循环(forwhile)。因此,如果没有循环 or yield from,可以调用生成器迭代器的最大次数受生成器yield函数中的语句数限制。

flatten请注意,编写返回迭代器的 a 很容易;采用原始解决方案并写作return iter(flatten(first) + flatten(rest))就可以了。但这不会是生成器迭代器,函数也不会是生成器函数。

这是一个滥用f_lineno无循环迭代的实现。不幸的是,它必须使用import sys

def current_frame():
    i = None
    def gen():
        yield i.gi_frame.f_back
    i = gen()
    return next(i).f_back

class Loop(object):
    jump = False
    def __call__(self, frame, event, arg):
        if self.jump:
            frame.f_lineno = self.lineno
            self.jump = False
        return None if event == 'call' else self
    def __enter__(self):
        import sys
        sys.settrace(self)
        current_frame().f_back.f_trace = self
        self.lineno = current_frame().f_back.f_lineno
        return self
    def __exit__(self, exc_type, exc_value, traceback):
        if exc_type is None:
            self.jump = True
        else:
            import sys
            sys.settrace(None)
            current_frame().f_back.f_trace = None
            return exc_type is StopIteration

def flatten(x):
    if isinstance(x, list):
        if x:
            first, rest = flatten(x[0]), flatten(x[1:])
            with Loop():
                yield next(first)
            with Loop():
                yield next(rest)
            pass
    else:
        yield x
于 2012-09-18T14:47:19.850 回答
-1

完成单行列表理解:

def flatten (test_list):
    return [element for temp in test_list for element in flatten(temp)] if isinstance(test_list, list) else [test_list]


print(flatten([1, [2, 1, [3, 6, 7]], [1, 2, [3, 2, 3], 4, [1, 2, 3, 4, 5]]]))
#[1, 2, 1, 3, 6, 7, 1, 2, 3, 2, 3, 4, 1, 2, 3, 4, 5]
于 2012-09-18T14:46:54.177 回答