2009-11-19 6 views
97

J'essaie de comprendre la portée dans les classes imbriquées en Python. Voici mon code exemple:Portée des classes imbriquées?

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. je reçois:

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

Je suis en train d'accéder au outer_var statique de InnerClass.

Existe-t-il un moyen de le faire?

Répondre

98
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 les choses fonctionnent semblables dans d'autres langues, et utilise la recherche globale au lieu de la portée de l'accès à outer_var. (Si vous changez quel objet le nom Outer est lié, alors ce code utilisera cet objet la prochaine fois qu'il est exécuté.)

Si vous voulez à la place tous les Inner objets d'avoir une référence à un Outer parce outer_var est vraiment une instance attribut:

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 les classes de nidification est un peu hors du commun en Python, et ne signifie pas automatiquement toute sorte de relation spéciale entre les classes. Tu ferais mieux de ne pas nicher. (Vous pouvez toujours définir un attribut de classe sur Outer à Inner, si vous le souhaitez.)

+1

Il pourrait être utile d'ajouter à quelle (s) version (s) de Python votre réponse fonctionnera. –

+1

J'ai écrit ceci avec 2.6/2.x à l'esprit, mais, en le regardant, je ne vois rien qui ne fonctionnerait pas de la même manière dans 3.x. –

+0

Je ne comprends pas très bien ce que vous voulez dire dans cette partie, "(Si vous changez l'objet auquel le nom Outer est lié, alors ce code utilisera cet objet la prochaine fois qu'il est exécuté.)" Pouvez-vous m'aider? ? – batbrat

20

Vous pourriez être mieux loti si vous n'utilisez pas de classes imbriquées. Si vous devez nid, essayez ceci:

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

Ou déclarer les deux classes avant de les nicher: (. Après cela, vous pouvez del InnerClass si vous avez besoin)

class OuterClass: 
    outer_var = 1 

class InnerClass: 
    inner_var = OuterClass.outer_var 

OuterClass.InnerClass = InnerClass 

40

Je pense que vous pouvez tout simplement faire:

class OuterClass: 
    outer_var = 1 

    class InnerClass: 
     pass 
    InnerClass.inner_var = outer_var 

Le problème rencontré est dû à ceci:

A block is a piece of Python program text that is executed as a unit. The following are blocks: a module, a function body, and a class definition.
(...)
A scope defines the visibility of a name within a block.
(...)
The scope of names defined in a class block is limited to the class block; it does not extend to the code blocks of methods – this includes generator expressions since they are implemented using a function scope. This means that the following will fail:

class A: 

     a = 42 

     b = list(a + i for i in range(10)) 

http://docs.python.org/reference/executionmodel.html#naming-and-binding

Les moyens ci-dessus:
un corps de fonction est un bloc de code et une méthode est une fonction, les noms définis sur le corps fonctionnel présent dans une définition de classe ne se prolongent pas au corps de la fonction. Paraphrasant ceci pour votre cas:
une définition de classe est un bloc de code, alors les noms définis dans la définition de classe interne présente dans une définition de classe externe ne s'étendent pas à la définition de classe interne.

+2

Incroyable. Votre exemple échoue, affirmant que "nom global 'n'est pas défini". Cependant, en substituant une compréhension de liste '[a + i for i in range (10)]' lie avec succès A.b à la liste attendue [42..51]. – George

+5

@George Notez que l'exemple avec ** classe A ** n'est pas le mien, il provient du document officiel Python dont j'ai donné le lien. Cet exemple échoue et cet échec est ce que l'on veut montrer dans cet exemple. En fait '' list (a + i pour i dans range (10)) '' est '' list ((a + i pour i dans range (10))) '' c'est-à-dire '' list (a_generator) ' '. Ils disent que ** un générateur ** est implémenté avec une portée similaire à l'étendue des fonctions. – eyquem

+0

@George Pour moi, cela signifie que les fonctions agissent différemment selon qu'elles se trouvent dans un module ou dans une classe. Dans le premier cas, une fonction sort de l'extérieur pour trouver l'objet lié à un identifiant libre. Dans le second cas, une fonction, c'est-à-dire une méthode, ne sort pas de son corps. Les fonctions d'un module et les méthodes d'une classe sont en réalité deux sortes d'objets. Les méthodes ne sont pas seulement des fonctions en classe. C'est mon idée. – eyquem

4

solution la plus simple:

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

Il vous oblige à être explicite, mais ne prend pas beaucoup d'efforts.

+0

NameError: le nom 'OuterClass' n'est pas défini - -1 –

+0

@Mr_and_Mrs_D Oui, voir Modifier. –

+1

Le point est d'y accéder à partir de la portée de la classe Outer - plus une erreur similaire se produira si vous appelez ceci à partir d'une portée statique à l'intérieur de Outer. Je vous suggère de supprimer ce post –

1

En Python, les objets mutables sont transmis comme référence, vous pouvez donc 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) 
+1

Veuillez noter que vous avez un cycle de référence ici, et dans certains cas, l'instance de cette classe ne sera pas libérée. Un exemple, avec cPython, si vous avez défini la méthode '__del__', le garbage collector ne sera pas capable de gérer le cycle de référence, et les objets iront dans' gc.garbage'. Le code ci-dessus, tel quel, n'est pas problématique. La façon d'y faire face est d'utiliser une ** référence faible **. Vous pouvez lire la documentation sur [weakref (2.7)] (https://docs.python.org/2/library/weakref.html) ou [weakref (3.5)] (https://docs.python.org/3.5/ library/weakref.html) – guyarad

0

Toutes les explications se trouvent dans Python Documentation Le Python Tutorial

Pour votre première erreur <type 'exceptions.NameError'>: name 'outer_var' is not defined. L'explication est:

There is no shorthand for referencing data attributes (or other methods!) from within methods. I find that this actually increases the readability of methods: there is no chance of confusing local variables and instance variables when glancing through a method.

cité Le Python Tutorial 9,4

Pour votre deuxième erreur <type 'exceptions.NameError'>: name 'OuterClass' is not defined

When a class definition is left normally (via the end), a class object is created.

cité Le Python Tutorial 9.3.1

Alors lorsque vous essayez inner_var = Outerclass.outer_var, le Quterclass n'a pas encore été créée, c'est pourquoi name 'OuterClass' is not defined

Une explication plus détaillée, mais fastidieux pour votre première erreur:

Although classes have access to enclosing functions’ scopes, though, they do not act as enclosing scopes to code nested within the class: Python searches enclosing functions for referenced names, but never any enclosing classes. That is, a class is a local scope and has access to enclosing local scopes, but it does not serve as an enclosing local scope to further nested code.

cité Learning.Python (5) .Mark.Lutz

Questions connexes