2017-11-10 3 views
6

Hier kann ich nicht auf die Klassenvariable in einem Python Liste Verständnis zugreifen.Python 3 - Klassenvariable ist nicht definiert

class Student: 
    max_year = 18 
    year_choice = [i for i in range(100) if i > max_year] 

    def __init__(self, name): 
    self.name = name 
    print (self.year_choice) 

Student('Blah') 

Aber es funktioniert gut in Python 2.

../workspace$ python student.py 
[19, 20, 21, 22, 2.... 99] 

Aber immer einen Fehler in Python 3.

../workspace$ python student.py 
File "student.py", line 18, in <listcomp> 
year_choice = [i for i in range(100) if i > max_year] 
NameError: name 'max_year' is not defined 

von diesem Debuggen Wenn ich unter Anweisung geändert

[i for i in range(100) if i > max_year]

dieser

[i for i in range(max_year)] # don't look too much into it ;) 

adaequat. Warum kann ich nicht innerhalb auf Klassenvariable zugreifen, wenn/else Liste Verständnis?

+0

Ok, das ist wirklich komisch. ;) Außerdem hat es nichts mit dem 'if' in der Liste comp zu tun. Das funktioniert auch nicht: '[max_year-i für i in range (max_year)]' –

+1

Ja von @py_dude Antwort, Wir können self.max_year definieren Es wird mit Klasse und Instanz dieser Klasse geteilt. nicht nur für dieses Beispiel. Aber hier bin ich neugierig zu wissen, warum wir nicht auf die Klassenvariable innerhalb von if/else Liste Verständnis zugreifen können? – ramganesh

+0

Gute Frage, aber es ist immer noch ein Duplikat ;-) –

Antwort

4

Der Grund, dass diese Linie

year_choice = [i for i in range(100) if i > max_year] 

Werke in Python 2, aber nicht in Python 3 ist, dass in Python 3 Listenkomprehensionen einen neuen Bereich zu erstellen, und die max_year Attributklasse ist in diesem Rahmen nicht. In Python 2 erstellt ein Listenverständnis keinen neuen Bereich, es wird im Kontext des umgebenden Codes ausgeführt. Das wurde ursprünglich aus Performance-Gründen gemacht, aber viele Leute fanden es verwirrend, so dass es in Python 3 geändert wurde, Listen-Comprehensions in Übereinstimmung mit Generator-Ausdrücken gebracht wurden und Kompromittierungen gesetzt und diktiert wurden.

AFAIK, es gibt keinen einfachen Weg in Python 3, auf ein Klassenattribut innerhalb eines Listenverständnisses zuzugreifen, das im äußeren Kontext einer Klasse statt innerhalb einer Methode läuft. Sie können nicht mit Student.max_year darauf verweisen, da zu diesem Zeitpunkt die Student Klasse nicht existiert.

Allerdings hat es wirklich keinen Sinn, dieses Listenverständnis dort zu haben. Sie können die gewünschte Liste kompakter und effizienter erstellen. Zum Beispiel:

class Student(object): 
    max_year = 18 
    year_choice = list(range(max_year + 1, 100)) 

    def __init__(self, name): 
     self.name = name 
     print (self.year_choice) 

Student('Blah') 

Ausgang

[19, 20, 21, 22, 23, 24, 25, 26, 27, 28, 29, 30, 31, 32, 33, 34, 35, 36, 37, 38, 39, 40, 41, 42, 43, 44, 45, 46, 47, 48, 49, 50, 51, 52, 53, 54, 55, 56, 57, 58, 59, 60, 61, 62, 63, 64, 65, 66, 67, 68, 69, 70, 71, 72, 73, 74, 75, 76, 77, 78, 79, 80, 81, 82, 83, 84, 85, 86, 87, 88, 89, 90, 91, 92, 93, 94, 95, 96, 97, 98, 99] 

Dieser Code die gleiche Leistung auf Python 2 und Python 3.

produziert habe ich die Klasse Signatur

geändert
class Student(object): 

, so dass es eine neue Stilklasse in Python 2 (in Python 3 alle Klassen sind neuartig).


Der Grund, dass [i for i in range(max_year)] um diese Einschränkung zu bekommen, ist, dass ein Iterator von range(max_year) erstellt wird, die dann als Argument für die temporäre Funktion übergeben wird, das die Liste Verständnis läuft.Es entspricht also diesem Code:

class Student(object): 
    max_year = 18 
    def _listcomp(iterator): 
     result = [] 
     for i in iterator: 
      result.append(i) 
     return result 

    year_choice = _listcomp(iter(range(max_year + 1, 100))) 
    del _listcomp 

    def __init__(self, name): 
     self.name = name 
     print(self.year_choice) 

Student('Blah') 

Vielen Dank an Antti Haapala für diese Erklärung.

Verwandte Themen