J'essaie de comprendre la portée des classes imbriquées en Python. Voici mon exemple de code:
class OuterClass:
outer_var = 1
class InnerClass:
inner_var = outer_var
La création de classe ne se termine pas et j'obtiens l'erreur suivante:
<type 'exceptions.NameError'>: name 'outer_var' is not defined
En essayant inner_var = Outerclass.outer_var
ne fonctionne pas. Je reçois:
<type 'exceptions.NameError'>: name 'OuterClass' is not defined
J'essaie d'accéder à la statique outer_var
de InnerClass
.
Y a-t-il un moyen de faire cela?
class Outer(object):
outer_var = 1
class Inner(object):
@property
def inner_var(self):
return Outer.outer_var
Ce n'est pas tout à fait la même chose que des choses similaires fonctionnent dans d'autres langues, et utilise la recherche globale au lieu d'étendre l'accès à outer_var
. (Si vous changez l'objet auquel le nom Outer
est lié, alors ce code utilisera cet objet la prochaine fois qu'il sera exécuté.)
Si vous voulez plutôt que tous les objets Inner
aient une référence à un Outer
parce que outer_var
est vraiment un attribut d'instance:
class Outer(object):
def __init__(self):
self.outer_var = 1
def get_inner(self):
return self.Inner(self)
# "self.Inner" is because Inner is a class attribute of this class
# "Outer.Inner" would also work, or move Inner to global scope
# and then just use "Inner"
class Inner(object):
def __init__(self, outer):
self.outer = outer
@property
def inner_var(self):
return self.outer.outer_var
Notez que l'imbrication de classes est quelque peu inhabituelle en Python et n'implique pas automatiquement une relation particulière entre les classes. Tu ferais mieux de ne pas nidifier. (Vous pouvez toujours définir un attribut de classe sur Outer
à Inner
, si vous le souhaitez.)
Je pense que vous pouvez simplement faire:
class OuterClass:
outer_var = 1
class InnerClass:
pass
InnerClass.inner_var = outer_var
Le problème que vous avez rencontré est dû à ceci:
Un bloc est un élément du texte du programme Python exécuté en tant qu'unité. Voici les blocs: un module, un corps de fonction et une définition de classe.
(...)
Une étendue définit la visibilité d'un nom dans un bloc.
(...)
La portée des noms définis dans un bloc de classe est limitée au bloc de classe. il ne s'étend pas aux blocs de code des méthodes - cela inclut les expressions de générateur car elles sont implémentées à l'aide d'une portée de fonction. Cela signifie que les éléments suivants vont échouer:class A: a = 42 b = list(a + i for i in range(10))
http://docs.python.org/reference/executionmodel.html#naming-and-binding
Ce qui précède signifie:
Un corps de fonction est un bloc de code et une méthode est une fonction. Les noms définis en dehors du corps de fonction présent dans une définition de classe ne s'étendent pas au corps de fonction.
En paraphrasant ceci pour votre cas:
Une définition de classe est un bloc de code. Par conséquent, les noms définis dans la définition de classe interne présente dans une définition de classe externe ne sont pas étendus à la définition de classe interne.
Vous pourriez être mieux lotis si vous n'utilisez simplement pas les classes imbriquées. Si vous devez imbriquer, essayez ceci:
x = 1
class OuterClass:
outer_var = x
class InnerClass:
inner_var = x
Ou déclarez les deux classes avant de les imbriquer:
class OuterClass:
outer_var = 1
class InnerClass:
inner_var = OuterClass.outer_var
OuterClass.InnerClass = InnerClass
(Après cela, vous pouvez del InnerClass
Si tu as besoin de.)
La solution la plus simple:
class OuterClass:
outer_var = 1
class InnerClass:
def __init__(self):
self.inner_var = OuterClass.outer_var
Cela exige que vous soyez explicite, mais cela ne demande pas beaucoup d'effort.
Dans Python sont passés en tant que référence, vous pouvez donc transmettre une référence de la classe externe à la classe interne.
class OuterClass:
def __init__(self):
self.outer_var = 1
self.inner_class = OuterClass.InnerClass(self)
print('Inner variable in OuterClass = %d' % self.inner_class.inner_var)
class InnerClass:
def __init__(self, outer_class):
self.outer_class = outer_class
self.inner_var = 2
print('Outer variable in InnerClass = %d' % self.outer_class.outer_var)
Toutes les explications peuvent être trouvées dans Documentation Python Le Python Tutorial
Pour votre première erreur <type 'exceptions.NameError'>: name 'outer_var' is not defined
. L'explication est la suivante:
Il n'y a pas de raccourci pour référencer les attributs de données (ou d'autres méthodes!) À l'intérieur des méthodes. Je trouve que cela augmente réellement la lisibilité des méthodes: il n'y a aucune chance de confondre les variables locales et les variables d'instance lorsque vous parcourez une méthode.
cité de Le Python Tutorial 9.4
Pour votre deuxième erreur <type 'exceptions.NameError'>: name 'OuterClass' is not defined
Lorsqu'une définition de classe est laissée normalement (via la fin), un objet de classe est créé.
cité de Le Python Didacticiel 9.3.1
Alors quand tu essaies inner_var = Outerclass.outer_var
, le Quterclass
n'a pas encore été créé, c'est pourquoi name 'OuterClass' is not defined
Une explication plus détaillée mais fastidieuse de votre première erreur:
Bien que les classes aient accès aux portées des fonctions englobantes, elles n'agissent pas comme des portées au code imbriqué dans la classe: Python recherche les fonctions englobant les noms référencés, mais jamais les classes englobantes. En d'autres termes, une classe est une étendue locale et a accès à des étendues locales englobantes, mais elle ne sert pas d'étendue locale englobante pour le code imbriqué.
cité de Learning.Python (5th) .Mark.Lutz