代码之家  ›  专栏  ›  技术社区  ›  inspectorG4dget Dillon Benson

高级嵌套列表理解语法

  •  39
  • inspectorG4dget Dillon Benson  · 技术社区  · 14 年前

    为了更好地理解列表的理解,我一直在玩列表理解的游戏,我遇到了一些我无法解释的意外输出。我以前没有发现这个问题,但如果是重复的问题,我道歉。

    我基本上是想写一个生成生成器的生成器。使用列表理解的简单生成器如下所示:

    (x for x in range(10) if x%2==0) # generates all even integers in range(10)
    

    我要做的是写一个生成两个生成器的生成器,第一个生成范围(10)中的偶数,第二个生成范围(10)中的奇数。为此,我做到了:

    >>> (x for x in range(10) if x%2==i for i in range(2))
    <generator object <genexpr> at 0x7f6b90948f00>
    
    >>> for i in g.next(): print i
    ... 
    Traceback (most recent call last):
      File "<stdin>", line 1, in <module>
      File "<stdin>", line 1, in <genexpr>
    UnboundLocalError: local variable 'i' referenced before assignment
    >>> g.next()
    Traceback (most recent call last):
      File "<stdin>", line 1, in <module>
    StopIteration
    >>> g = (x for x in range(10) if x%2==i for i in range(2))
    >>> g
    <generator object <genexpr> at 0x7f6b90969730>
    >>> g.next()
    Traceback (most recent call last):
          File "<stdin>", line 1, in <module>
          File "<stdin>", line 1, in <genexpr>
        UnboundLocalError: local variable 'i' referenced before assignment
    

    我不明白为什么“我”在作业前会被引用。

    我想这可能与 i in range(2) 我这样做了:

    >>> g = (x for x in range(10) if x%2==i for i in [0.1])
    >>> g
    <generator object <genexpr> at 0x7f6b90948f00>
    >>> g.next()
    Traceback (most recent call last):
      File "<stdin>", line 1, in <module>
      File "<stdin>", line 1, in <genexpr>
    UnboundLocalError: local variable 'i' referenced before assignment
    

    这对我来说没有意义,所以我认为最好先尝试一些简单的方法。所以我回到列表中,尝试:

    >>> [x for x in range(10) if x%2==i for i in range(2)]
    [1, 1, 3, 3, 5, 5, 7, 7, 9, 9]
    

    我期望与以下内容相同:

    >>> l = []
    >>> for i in range(2):
    ...     for x in range(10):
    ...             if x%2==i:
    ...                     l.append(x)
    ... 
    >>> l
    [0, 2, 4, 6, 8, 1, 3, 5, 7, 9] # so where is my list comprehension malformed?
    

    但当我凭直觉尝试的时候,结果是这样的:

    >>> [[x for x in range(10) if x%2==i] for i in range(2)]
    [[0, 2, 4, 6, 8], [1, 3, 5, 7, 9]] # so nested lists in nested list comprehension somehow affect the scope of if statements? :S
    

    所以我认为这可能是一个问题,在什么程度的范围 if 语句在中操作。所以我试了一下:

    >>> [x for x in range(10) for i in range(2) if x%2==i]
    [0, 1, 2, 3, 4, 5, 6, 7, 8, 9]
    

    现在我完全困惑了。有人能解释一下这种行为吗?我不明白为什么我的列表理解看起来是畸形的,也不明白 如果 陈述有效。

    附:在校对问题的时候,我意识到这看起来有点像一个家庭作业问题,但事实并非如此。

    4 回复  |  直到 6 年前
        1
  •  39
  •   Lie Ryan Bryan    14 年前

    您需要使用一些括号:

    ((x for x in range(10) if x%2==i) for i in range(2))
    

    这对我来说没有意义,所以我 我想最好试试看 简单一点。所以我回到名单上 并尝试:

    [>>>[X代表X在范围(10)内,如果X%2==I代表I在范围(2)内] [1、1、3、3、5、5、7、7、9、9]

    这是因为以前的列表理解将i变量泄漏到封闭范围中,并成为当前范围的i。尝试启动一个新的python解释器,这将由于名称错误而失败。在python 3中,计数器的泄漏行为已被删除。

    编辑:

    等效for循环:

    (x for x in range(10) if x%2==i for i in range(2))
    

    将是:

    l = []
    for x in range(10):
        if x%2 == i:
            for i in range(2):
                l.append(x)
    

    它还提供了一个名称错误。

    编辑2:

    带圆括号的版本:

    (如果x%2==i,x代表范围(10)中的x)代表范围(2)中的i)
    

    相当于:

    li = []
    for i in range(2):
        lx = []
        for x in range(10):
            if x%2==i:
                lx.append(x)
        li.append(lx)
    
        2
  •  7
  •   Nathan Kronenfeld    12 年前

    Lie Ryan的for Loop等价物将我引向以下方面,这看起来确实很好:

    [x for i in range(2) for x in range(10) if i == x%2]
    

    输出

    [0, 2, 4, 6, 8, 1, 3, 5, 7, 9]
    
        3
  •  7
  •   Community CDub    11 年前

    稍微扩展一下Lie Ryan的答案:

    某物=(x代表x在范围(10)内,如果x%2==i代表i在范围(2)内)

    相当于:

    def _gen1():
        for x in range(10):
            if x%2 == i:
                for i in range(2):
                    yield x
    something = _gen1()
    

    而带括号的版本相当于:

    def _gen1():
        def _gen2():
            for x in range(10):
                if x%2 == i:
                    yield x
    
        for i in range(2):
            yield _gen2()
    something = _gen1()
    

    这确实产生了两个发电机:

    [<generator object <genexpr> at 0x02A0A968>, <generator object <genexpr> at 0x02A0A990>]
    

    不幸的是,它产生的发电机有些不稳定,因为输出将取决于您如何消耗它们:

    >>> gens = ((x for x in range(10) if x%2==i) for i in range(2))
    >>> for g in gens:
            print(list(g))
    
    [0, 2, 4, 6, 8]
    [1, 3, 5, 7, 9]
    >>> gens = ((x for x in range(10) if x%2==i) for i in range(2))
    >>> for g in list(gens):
            print(list(g))
    
    [1, 3, 5, 7, 9]
    [1, 3, 5, 7, 9]
    

    我的建议是完整地写出生成器函数:我想尝试在 i 不这样做几乎是不可能的。

        4
  •  3
  •   Glenn Maynard    14 年前

    谎言有句法问题的答案。建议:不要把太多东西塞进发电机的体内。函数的可读性要高得多。

    def make_generator(modulus):
        return (x for x in range(10) if x % 2 == modulus)
    g = (make_generator(i) for i in range(2))