Генераторен израз създава нещо като функция; един само с един аргумент, най-външният итерируем.
Тук това е units
и това е обвързано като аргумент към генераторния израз, когато генераторният израз е създаден.
Всички други имена са или локални (като a
и b
), глобални или затваряния. tens
се търси като глобален, така че се търси всеки път, когато напредвате в генератора.
В резултат на това units
е свързано с генератора на ред 3, tens
се търси, когато повторите израза на генератора на последния ред.
Можете да видите това, когато компилирате генератора в байткод и проверявате този байткод:
>>> import dis
>>> genexp_bytecode = compile('(a + b for a in units for b in tens)', '<file>', 'single')
>>> dis.dis(genexp_bytecode)
1 0 LOAD_CONST 0 (<code object <genexpr> at 0x10f013ae0, file "<file>", line 1>)
3 LOAD_CONST 1 ('<genexpr>')
6 MAKE_FUNCTION 0
9 LOAD_NAME 0 (units)
12 GET_ITER
13 CALL_FUNCTION 1 (1 positional, 0 keyword pair)
16 PRINT_EXPR
17 LOAD_CONST 2 (None)
20 RETURN_VALUE
Байткодът MAKE_FUNCTION
превърна кодовия обект на израза на генератора във функция и тя се извиква незабавно, предавайки iter(units)
като аргумент. Името tens
изобщо не се споменава тук.
Това е документирано в оригиналните PEP генератори:
Само най-външният for-израз се оценява незабавно, останалите изрази се отлагат, докато генераторът не бъде стартиран:
g = (tgtexp for var1 in exp1 if exp2 for var2 in exp3 if exp4)
е еквивалентно на:
def __gen(bound_exp):
for var1 in bound_exp:
if exp2:
for var2 in exp3:
if exp4:
yield tgtexp
g = __gen(iter(exp1))
del __gen
и в референция за генераторни изрази:
Променливите, използвани в израза на генератора, се оценяват лениво, когато методът __next__()
се извиква за обект на генератор (по същия начин като нормалните генератори). Най-лявата клауза for
обаче се оценява незабавно, така че генерираната от нея грешка може да се види преди всяка друга възможна грешка в кода, който обработва генераторния израз. Следващите for
клаузи не могат да бъдат оценени веднага, тъй като може да зависят от предишния for цикъл. Например: (x*y for x in range(10) for y in bar(x))
.
PEP има отличен раздел, мотивиращ защо имената (различни от най-външните итерируеми) се обвързват късно, вижте Ранно свързване срещу късно свързване.
person
Martijn Pieters
schedule
27.03.2014
units
е аргумент на генераторния израз 'функция', докатоtens
се разглежда като глобален. Така чеunits
е обвързано на ред 3,tens
не е. - person Martijn Pieters   schedule 27.03.2014