Comment puis-je accéder aux variables de classe «statiques» dans les méthodes de classe en Python?

162

Si j'ai le code python suivant:

class Foo(object):
    bar = 1

    def bah(self):
        print(bar)

f = Foo()
f.bah()

Il se plaint

NameError: global name 'bar' is not defined

Comment puis-je accéder à la classe / variable statique bardans la méthode bah?

Ross Rogers
la source
Plus d'informations concernant Python et la statique peuvent être trouvées ici: stackoverflow.com/questions/68645/python-static-variable
bedwyr

Réponses:

166

Au lieu d' barutiliser self.barou Foo.bar. Assigner à Foo.barcréera une variable statique, et assigner à self.barcréera une variable d'instance.

Pavel Strakhov
la source
12
Foo.bar fonctionnera, mais self.bar crée une variable d'instance, pas une variable statique.
bedwyr
47
bedwyr, "print self.bar" ne créera aucune variable d'instance (bien que l'assignation à self.bar le fera).
Constantin
@Constantin - Je ne m'en suis pas rendu compte, c'est une distinction intéressante. Merci pour la correction :-)
bedwyr
2
Mais si vous n'avez pas l'intention qu'il y ait un ivar, il est plus clair d'utiliser Foo.classmember.
mk12 le
2
lorsque vous utilisez des variables statiques, c'est une bonne idée de lire les pièges à partir d'ici: stackoverflow.com/questions/68645/… . @Constantin donne l'un des nombreux pièges.
Trevor Boyd Smith
84

Définissez la méthode de classe:

class Foo(object):
    bar = 1
    @classmethod
    def bah(cls):    
        print cls.bar

Maintenant, si bah()doit être une méthode d'instance (c'est-à-dire avoir accès à soi), vous pouvez toujours accéder directement à la variable de classe.

class Foo(object):
    bar = 1
    def bah(self):    
        print self.bar
vartec
la source
3
Pourquoi pas juste Foo.bar, au lieu de self.__class__.bar?
mk12
15
@ Mk12: Lorsque vous avez l'héritage de classe, une "variable de classe" peut être dans une classe de base ou une sous-classe. À quoi voulez-vous vous référer? Cela dépend de ce que vous essayez de faire. Foo.bar ferait toujours référence à un attribut de la classe spécifiée - qui pourrait être une classe de base ou une sous-classe. self.__class__.bar ferait référence à la classe dont l'instance est un type.
Craig McQueen
7
Nous avons maintenant atteint ce point malheureux où nous devenons si conscients des détails du langage que nous pouvons discriminer entre deux cas d'utilisation finement harmonisés. Cela dépend en effet de ce que vous voulez faire, mais beaucoup de gens ne seront pas attentifs à de telles subtilités.
holdenweb
2
BTW, c'est une utilisation RARE de "variable de classe". Il est beaucoup plus courant de l'avoir défini dans une classe spécifique, ici Foo. Ce sont des informations utiles pour certaines situations de programmation avancées. Mais presque certainement pas ce que la question originale voulait comme réponse (toute personne qui a besoin de CETTE réponse saura déjà comment faire Foo.bar). +1 parce que j'en ai appris quelque chose.
ToolmakerSteve
3
J'apprends quand utiliser des variables et des méthodes de classe (par opposition aux variables et méthodes d'instance). Lorsque vous souhaitez accéder à une variable de classe dans une méthode d'instance, est-il nécessaire d'utiliser self.__class__.bar? J'ai remarqué que self.bar fonctionne même si bar est une variable de classe et non une variable d'instance.
Bill
13

Comme pour tous les bons exemples, vous avez simplifié ce que vous essayez de faire. C'est bien, mais il convient de noter que python a beaucoup de flexibilité en ce qui concerne les variables de classe par rapport aux variables d'instance. On peut en dire autant des méthodes. Pour une bonne liste de possibilités, je vous recommande de lire l' introduction aux classes de nouveau style de Michael Fötsch , en particulier les sections 2 à 6.

Une chose qui demande beaucoup de travail à retenir lors de la mise en route est que python n'est pas java. Plus qu'un simple cliché. En java, une classe entière est compilée, rendant la résolution de l'espace de noms très simple: toutes les variables déclarées en dehors d'une méthode (n'importe où) sont des variables d'instance (ou, si statique, de classe) et sont implicitement accessibles dans les méthodes.

Avec python, la grande règle d'or est qu'il y a trois espaces de noms qui sont recherchés, dans l'ordre, pour les variables:

  1. La fonction / méthode
  2. Le module actuel
  3. Builtins

{begin pedagogy}

Il existe des exceptions limitées à cela. Le principal qui me vient à l'esprit est que, lorsqu'une définition de classe est en cours de chargement, la définition de classe est son propre espace de noms implicite. Mais cela ne dure que tant que le module est en cours de chargement, et est entièrement contourné dans une méthode. Donc:

>>> class A(object):
        foo = 'foo'
        bar = foo


>>> A.foo
'foo'
>>> A.bar
'foo'

mais:

>>> class B(object):
        foo = 'foo'
        def get_foo():
            return foo
        bar = get_foo()



Traceback (most recent call last):
  File "<pyshell#11>", line 1, in <module>
    class B(object):
  File "<pyshell#11>", line 5, in B
    bar = get_foo()
  File "<pyshell#11>", line 4, in get_foo
    return foo
NameError: global name 'foo' is not defined

{end pedagogy}

En fin de compte , la chose à retenir est que vous n'avez accès à l' une des variables que vous souhaitez accéder, mais probablement pas implicitement. Si vos objectifs sont simples et directs, opter pour Foo.bar ou self.bar sera probablement suffisant. Si votre exemple devient plus compliqué, ou si vous voulez faire des choses fantaisistes comme l'héritage (vous pouvez hériter de méthodes statiques / de classe!), Ou que l'idée de faire référence au nom de votre classe dans la classe elle-même vous semble erronée, consultez l'intro que j'ai liée.

David Berger
la source
IIRC, il y a techniquement 3 espaces de noms (+) recherchés - function-local, module / global et builtins. Les étendues imbriquées signifient que plusieurs étendues locales peuvent être recherchées, mais c'est l'un des cas exceptionnels. (...)
Jeff Shannon
De plus, je ferais attention de dire `` module principal '', car c'est le module contenant la fonction qui est recherché, pas le module principal ... Et rechercher l'attribut à partir d'une instance ref est une chose différente, mais cette réponse explique pourquoi vous avez besoin de l'instance / classe ref.
Jeff Shannon le
10
class Foo(object):
     bar = 1
     def bah(self):
         print Foo.bar

f = Foo() 
f.bah()
Allan Mwesigwa
la source
-2
class Foo(object):    
    bar = 1

    def bah(object_reference):
        object_reference.var = Foo.bar
        return object_reference.var


f = Foo() 
print 'var=', f.bah()
Lopi Dani
la source
4
Bien que ce code puisse résoudre la question, inclure une explication sur comment et pourquoi cela résout le problème aiderait vraiment à améliorer la qualité de votre message et entraînerait probablement plus de votes à la hausse. N'oubliez pas que vous répondez à la question des lecteurs à l'avenir, pas seulement à la personne qui la pose maintenant. Veuillez modifier votre réponse pour ajouter des explications et donner une indication des limites et des hypothèses applicables. De l'avis
double-beep