На этом шаге мы дадим ряд советов по использованию выражений-генераторов.
Как и включения в список, выражения-генераторы оставляют место для большей сложности, чем та, которую мы рассмотрели на данный момент. Посредством вложенных циклов for и состыкованных в цепочки формул фильтрации они могут охватывать более широкий диапазон вариантов использования:
(expr for x in xs if cond1 for y in ys if cond2 for z in zs if condN)
Образец выше переводится в следующую ниже логику функции-генератора:
for x in xs: if cond1: for y in ys: if cond2: for z in zs: if condN: yield expr
И вот здесь хотелось бы разместить большое предостережение.
Пожалуйста, не пишите такие глубоко вложенные выражения-генераторы. В дальнейшем окажется, что их будет очень трудно сопровождать.
Это одна из тех ситуаций, о которых говорят, что "вещество становится ядом, начиная с определенной дозы", где злоупотребление красивым и простым инструментом может создать плохо воспринимаемую и трудно отлаживаемую программу.
Точно так же, как и с включениями в список, стремитесь избегать любого выражения-генератора, которое содержит более двух уровней вложенности.
Выражения-генераторы являются полезным и питоновским инструментом в вашем наборе, но это не значит, что они должны использоваться для решения каждой задачи, с которой вы сталкиваетесь. В случае составных итераторов часто лучше написать функцию-генератор или даже итератор на основе класса.
Если у вас есть потребность использовать вложенные генераторы и составные условия фильтрации, обычно лучше вынести их в подгенераторы (чтобы им можно было назначить имя) и затем состыковать их в цепочку еще раз, на верхнем уровне. Вы увидите, как это делается, в следующем далее разделе, посвященном цепочкам итераторов (iterator chains).
Если вы до сих пор не определились, то попробуйте другие реализации, а затем выберите ту, которая кажется самой удобочитаемой. Поверьте, в итоге это сэкономит вам время.
На следующем шаге мы подитожим изученный материал.