Понимание генератора с использованием другого генератора - PullRequest
0 голосов
/ 19 июня 2020

Я пытаюсь написать метод, который возвращает 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). Есть лучший способ сделать это? Я посмотрел на этот ответ, а также на синтаксис yield from, но я с трудом понимаю генераторы и так.

Ответы [ 2 ]

2 голосов
/ 19 июня 2020

Будет лучше, если оператор yield будет помещен внутри явного l oop, а не пытаться получить генератор.

Ваш 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 l oop, потому что вы не можете многократно повторять один и тот же итератор, который он возвращает; после итерации через него он исчерпывается, и generate_nums будет повышать StopIteration каждый раз (так что итерация дает пустую последовательность).

(Если generate_nums дорого, то вы могли конечно, сделайте nums = list(generate_nums()) за пределами flat_name l oop, а затем повторите это внутри l oop, но если для этого потребуется потенциально много памяти, то это может лишить большую часть смысла использования генератора в первое место.)

Остальная часть вашего кода не изменилась, за исключением того, что 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 голосов
/ 19 июня 2020

Вы можете использовать выражение генератора и 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
...