2016-11-28 36 views
10

Myślę, że wiem, jak zmienne i generatory działają dobrze w Pythonie.
Jednak poniższy kod mnie wprawia w zakłopotanie.Zmienny zakres w generatorach w klasach

from __future__ import print_function 

class A(object): 
    x = 4 
    gen = (x for _ in range(3)) 

a = A() 
print(list(a.gen)) 

Kiedy uruchomić kod (Python 2), to mówi:

Traceback (most recent call last): 
    File "Untitled 8.py", line 10, in <module> 
    print(list(a.gen)) 
    File "Untitled 8.py", line 6, in <genexpr> 
    gen = (x for _ in range(3)) 
NameError: global name 'x' is not defined 

W Pythonie 3, to mówi NameError: name 'x' is not defined
ale, gdy uruchomię:

from __future__ import print_function 

class A(object): 
    x = 4 
    lst = [x for _ in range(3)] 

a = A() 
print(a.lst) 
Kod

Kod nie działa w Pythonie 3, ale działa w Pythonie 2 lub Funkcja jak ten

from __future__ import print_function 

def func(): 
    x = 4 
    gen = (x for _ in range(3)) 
    return gen 

print(list(func())) 

ten kod działa dobrze w Pythonie 2 i 3 lub Python na poziomie modułu

from __future__ import print_function 

x = 4 
gen = (x for _ in range(3)) 

print(list(gen)) 

Kod działa dobrze w Pythonie 2 i Python 3 też.

Dlaczego jest źle w class?

Odpowiedz

5

Jak stwierdzono w innych odpowiedzi, to True że to się dzieje, ponieważ jest zmienna statyczna. Ale to nie tylko własność ogranicza twój kod do działania. Rzeczywistą przyczyną jest zakres zmiennej i zakres, w którym jest wykonywany. Na przykład utworzyć klasę jako:

class A(object): 
    x = 999999 
    y = x +1 

Jeśli masz dostęp To właściwości klasy A.x i A.y, to będzie działać.Ponieważ w czasie inicjowania y, x jest zastępowany wartością wyrażenia x+1. Ponieważ zakres x był w klasie.

Jednak tak się nie dzieje w przypadku generatorów. to znaczy w swoim przykładzie:

class A(object): 
    x = 4 
    gen = (x for _ in range(3)) 

Kiedy robisz list(a.gen), jest on wykonywany poza klasą (jak generatory są oceniane w czasie wykonywania) i kontroli dla odniesienia x w bieżącym zakresie. Ponieważ, x nie został zainicjowany w tym zakresie, powoduje błąd.

Po jawnym zainicjowaniu x=4 działa, ponieważ teraz wyrażenie generatora ma wartość x, z której może korzystać.

W celu uczynienia pracy generatora ekspresyjny, jak stwierdzono przez innych trzeba określić to lubią:

class A(object): 
    x = 4 
    gen = (A.x for _ in range(3)) 
    # ^mentioning `A.x` is the value to access 
+0

oświadczenie, że "jest wykonywane poza klasą (jako że generatory są obliczane w czasie wykonywania) i sprawdza odniesienie x ** w bieżącym zakresie **" może być argumentowane. zobacz ten http://ideone.com/bgef81 wynik to [6,6,6], a nie [5,5,5], dlaczego? – WeizhongTu

6

Ponieważ x jest atrybutem klasy (zmienna statyczna), których dostęp podobnych

Przykład

>>> class A(object): 
...  x = 4 
...  gen = (A.x for _ in range(3)) 
... 
>>> a = A() 
>>> list(a.gen) 
[4, 4, 4] 

Tutaj nawet gen jest inny atrybut klasy, co oznacza, że ​​

>>> b = A() 
>>> list(b.gen) 
[] 

Daje to pusty, ponieważ generator został już wyczerpany.


Dzieje się tak dlatego, że generator jest oceniana tylko w przypadku wystawienia a.gen, gdy nie będzie on w stanie rozwiązać nazwę x.

+0

Myślałem, że 'x' był _class variable_ nie zmienna statyczna? Czy się mylę? –

+0

To nie jest całkowicie "Prawda". Zastąp 'gen' przez' y = x + 1'. Następnie przejdź do niego jak '' y'. To będzie działać. Dlaczego nie działa z 'gen'? –

+2

@MoinuddinQuadri z 'y = x + 1', wartość' x' jest sprawdzana * natychmiast *, gdy wartość jeśli 'y' jest ustawiona podczas przetwarzania treści instrukcji' class'. '(x dla _ w zakresie (3))' tworzy wyrażenie generatora, którego ciało nie jest oceniane, dopóki nie spróbujesz skonsumować wartości z wynikowego generatora. Oznacza to, że wyszukiwanie 'x' występuje poza instrukcją' class'. – chepner