121 votes

La portée des classes imbriquées ?

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 la classe ne se termine pas et je reçois l'erreur :

<type 'exceptions.NameError'>: name 'outer_var' is not defined

Essayer inner_var = Outerclass.outer_var ne fonctionne pas. J'obtiens :

<type 'exceptions.NameError'>: name 'OuterClass' is not defined

J'essaie d'accéder aux données statiques outer_var de InnerClass .

Y a-t-il un moyen de le faire ?

107voto

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 ce qui se fait dans d'autres langages, et cela utilise la recherche globale au lieu de la recherche de l'accès à la fonction outer_var . (Si vous changez l'objet dont le nom Outer est lié, alors ce code utilisera cet objet lors de sa prochaine exécution).

Si vous voulez plutôt que tous les Inner pour avoir une référence à un Outer porque outer_var est en réalité 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 des classes est plutôt rare en Python, et n'implique pas automatiquement une sorte de relation spéciale entre les classes. Il est préférable de ne pas les imbriquer. (Vous pouvez toujours définir un attribut de classe sur Outer a Inner si vous le souhaitez).

45voto

eyquem Points 9942

Je pense que vous pouvez simplement le 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 morceau de texte de programme Python qui est exécuté en tant qu'unité. Les blocs suivants sont des blocs : un module, un corps de fonction, et une classe une définition.
(...)
Une portée définit la visibilité d'un nom dans un bloc.
(...)
La portée des noms définis dans un bloc de classe est la suivante limitée au bloc de classe ; elle ne s'étend pas aux blocs de code des méthodes. méthodes - ceci inclut les expressions de générateur puisqu'elles sont puisqu'elles sont implémentées en utilisant la portée d'une fonction. Cela signifie que l'opération suivante échouera :

   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, alors les noms définis hors du corps de fonction présents dans une définition de classe ne s'étendent pas au corps de fonction.

Je paraphrase ceci pour votre cas :
une définition de classe est un bloc de code, alors les noms définis en dehors de la définition de classe interne et présents dans une définition de classe externe ne s'étendent pas à la définition de classe interne.

20voto

Jason Orendorff Points 15869

Vous feriez mieux de ne pas utiliser de classes imbriquées. Si vous devez imbriquer, essayez ceci :

x = 1
class OuterClass:
    outer_var = x
    class InnerClass:
        inner_var = x

Ou déclarer 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 vous en avez besoin).

3voto

Cristian Garcia Points 805

La solution la plus simple :

class OuterClass:
    outer_var = 1
    class InnerClass:
        def __init__(self):
            self.inner_var = OuterClass.outer_var

Elle exige que vous soyez explicite, mais ne demande pas beaucoup d'efforts.

1voto

T. Alpers Points 26

En Python, les objets mutables sont passés en tant que référence, donc vous pouvez passer 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)

Prograide.com

Prograide est une communauté de développeurs qui cherche à élargir la connaissance de la programmation au-delà de l'anglais.
Pour cela nous avons les plus grands doutes résolus en français et vous pouvez aussi poser vos propres questions ou résoudre celles des autres.

Powered by:

X