【问题标题】:Generator comprehension using another generator使用另一个生成器的生成器理解
【发布时间】:2020-10-10 02:25:00
【问题描述】:

我正在尝试编写一个返回Generator 的方法。这两种方法的最终结果是得到两个列表的组合,形式为:'A #1', 'B #1', ..., 'F #9'

FLATS = ['A', 'B', 'C', 'D', 'E', 'F']

def generate_nums() -> Generator[str, None, None]:
    prefix = '#'
    for num in range(10):
        code = ''.join([prefix, str(num)])

        yield code

def generate_room_numbers() -> Generator[str, None, None]:
    room_nums = generate_nums()

    yield (' '.join([flat_name, room_num]) for room_num in room_nums for flat_name in FLATS)

if __name__ == "__main__":
    result = generate_room_numbers()
    result = next(result) # I hate this. How do I get rid of this?
    for room in result:
        print(room)

这给了我正确的结果。虽然,我的烦恼是result = next(result)。有一个更好的方法吗?我查看了this 答案以及yield from 语法,但我几乎无法理解生成器。

【问题讨论】:

  • code = ''.join([prefix, str(num)]) 是一种非常冗长的写作方式prefix + str(num),老实说,f"{prefix}{num} 可能更具可读性
  • 我认为它比使用+f'{x}' 更有效,不是吗?
  • 为什么在generate_room_numbers 中使用yield?只需使用return,您不需要使用next
  • 不,不是,不是两个字符串。 ''.join 可以有效地使用分隔符将多个字符串连接到容器中
  • @juanpa.arrivillaga 如果我错了,OP 可以纠正我,但我假设 generate_nums 是为了代表某种东西,无论出于何种原因,它确实需要成为一个生成器,并且问题基本上是关于如何将生成器包装在另一个生成器内的循环中,而不是使用return 是否可以更有效地实现此应用程序中的特定generate_nums

标签: python generator yield


【解决方案1】:

最好将yield 语句放在显式循环中,而不是尝试生成生成器。

您的generate_room_numbers 应如下所示:

def generate_room_numbers():
    for flat_name in FLATS:
        room_nums = generate_nums()
        for room_num in room_nums:    
            yield (' '.join([flat_name, room_num]))

注意generate_nums()flat_name 循环内被调用,因为你不能重复迭代它返回的同一个迭代器;在遍历它之后,它会耗尽,generate_nums 每次都会引发StopIteration(这样迭代会产生一个空序列)。

(如果generate_nums 很昂贵,那么您当然可以在flat_name 循环之外执行nums = list(generate_nums()),然后在循环内部对其进行迭代,但是如果这可能需要大量内存,那么它首先可能会破坏使用生成器的大部分意义。)

除了删除主代码中的result = next(result)之外,其余代码没有变化,但为了方便起见,这里是全部内容:

FLATS = ['A', 'B', 'C', 'D', 'E', 'F']

def generate_nums():
    prefix = '#'
    for num in range(10):
        code = ''.join([prefix, str(num)])

        yield code

def generate_room_numbers():
    for flat_name in FLATS:
        room_nums = generate_nums()
        for room_num in room_nums:    
            yield (' '.join([flat_name, room_num]))

if __name__ == "__main__":
    result = generate_room_numbers()
    # result = next(result)  <<==== NOT NEEDED ANY MORE
    for room in result:
        print(room)

【讨论】:

    【解决方案2】:

    您可以使用生成器表达式和 f 字符串:

    
    FLATS = ['A', 'B', 'C', 'D', 'E', 'F']
    room_numbers = (f'{letter} #{i}' for i in range(1, 10) for letter in FLATS)
    
    for room in room_numbers:
        print(room)
    

    输出:

    A #1
    B #1
    C #1
    .
    .
    .
    D #9
    E #9
    F #9
    

    【讨论】:

      猜你喜欢
      • 2018-12-11
      • 1970-01-01
      • 1970-01-01
      • 2017-06-01
      • 1970-01-01
      • 2011-09-09
      • 2011-12-19
      • 1970-01-01
      • 2017-09-28
      相关资源
      最近更新 更多