Como acessar variáveis ​​de classe "estáticas" nos métodos de classe em Python?

162

Se eu tiver o seguinte código python:

class Foo(object):
    bar = 1

    def bah(self):
        print(bar)

f = Foo()
f.bah()

Reclama

NameError: global name 'bar' is not defined

Como posso acessar classe / variável estática barno método bah?

Ross Rogers
fonte
Mais informações sobre Python e estática pode ser encontrada aqui: stackoverflow.com/questions/68645/python-static-variable
Bedwyr

Respostas:

166

Em vez de barusar self.barou Foo.bar. Atribuir a Foo.barcriará uma variável estática e atribuir a self.barcriará uma variável de instância.

Pavel Strakhov
fonte
12
O Foo.bar funcionará, mas o self.bar cria uma variável de instância, não estática.
Bedwyr
47
bedwyr, "print self.bar" não criará nenhuma variável de instância (embora atribuir a self.bar).
Constantin
@ Constantin - eu não percebi isso, é uma distinção interessante. Obrigado pela correção :-)
Bedwyr
2
Mas se você não pretende que exista um ivar, é mais fácil usar o Foo.classmember.
Mk12 13/09/09
2
ao usar variáveis ​​estáticas, é uma boa ideia ler as dicas aqui: stackoverflow.com/questions/68645/… . @Constantin oferece uma das muitas dicas.
Trevor Boyd Smith
84

Defina o método da classe:

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

Agora, se bah()tiver que ser um método de instância (ou seja, ter acesso a si mesmo), você ainda poderá acessar diretamente a variável de classe.

class Foo(object):
    bar = 1
    def bah(self):    
        print self.bar
vartec
fonte
3
Por que não apenas Foo.bar, em vez de self.__class__.bar?
Mk12 13/09/09
15
@ Mk12: Quando você tem uma herança de classe, uma "variável de classe" pode estar em uma classe base ou em uma subclasse. A que você quer se referir? Depende do que você está tentando fazer. Foo.bar sempre se refere a um atributo da classe especificada - que pode ser uma classe base ou uma subclasse. self.__class__.bar se referiria à classe da qual a instância é um tipo.
22411 Craig McQueen
7
Agora chegamos a esse ponto infeliz quando nos tornamos tão conscientes dos detalhes do idioma que podemos discriminar entre dois casos de uso perfeitamente sintonizados. De fato, depende do que você quer fazer, mas muitas pessoas não atendem a essas sutilezas.
precisa
2
BTW, este é um uso raro de "variável de classe". Muito mais comum tê-lo definido em uma classe específica, aqui Foo. Esta é uma informação útil para algumas situações avançadas de programação. Mas quase certamente não o que a pergunta original queria como resposta (quem precisar desta resposta já saberá como usar o Foo.bar). +1 porque aprendi algo com isso.
Home
3
Estou aprendendo sobre quando usar variáveis ​​e métodos de classe (em oposição a variáveis ​​e métodos de instância). Quando você deseja acessar uma variável de classe em um método de instância, é necessário usar self.__class__.bar? Notei que self.bar funciona mesmo que bar seja uma variável de classe e não uma variável de instância.
Bill
13

Como em todos os bons exemplos, você simplificou o que realmente está tentando fazer. Isso é bom, mas vale a pena notar que o python tem muita flexibilidade quando se trata de variáveis ​​de classe versus instância. O mesmo pode ser dito dos métodos. Para uma boa lista de possibilidades, recomendo ler a introdução das novas aulas de Michael Fötsch , especialmente as seções 2 a 6.

Uma coisa que exige muito trabalho para se lembrar ao iniciar é que python não é java. Mais do que apenas um clichê. Em java, uma classe inteira é compilada, tornando a resolução do espaço para nome realmente simples: quaisquer variáveis ​​declaradas fora de um método (em qualquer lugar) são variáveis ​​de instância (ou, se estáticas, de classe) e são implicitamente acessíveis nos métodos.

No python, a regra geral é que existem três namespaces pesquisados, em ordem, por variáveis:

  1. A função / método
  2. O módulo atual
  3. Builtins

{begin pedagogy}

Existem exceções limitadas a isso. O principal que me ocorre é que, quando uma definição de classe está sendo carregada, a definição de classe é seu próprio espaço para nome implícito. Mas isso dura apenas enquanto o módulo estiver sendo carregado e é totalmente ignorado quando dentro de um método. Portanto:

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


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

mas:

>>> 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}

No final, a única coisa a lembrar é que você não tem acesso a qualquer uma das variáveis que você deseja acesso, mas provavelmente não implicitamente. Se seus objetivos são simples e diretos, optar por Foo.bar ou self.bar provavelmente será suficiente. Se o seu exemplo está ficando mais complicado, ou você deseja fazer coisas sofisticadas, como herança (você pode herdar métodos estáticos / de classe!), Ou a idéia de se referir ao nome da sua classe dentro da própria classe parece errada para você, confira a introdução que eu vinculei.

David Berger
fonte
IIRC, tecnicamente existem 3 (+) namespaces pesquisados ​​- função-local, módulo / global e componentes internos. Escopos aninhados significa que vários escopos locais podem ser pesquisados, mas esse é um dos casos excepcionais. (...)
Jeff Shannon
Além disso, eu seria cuidadoso ao dizer 'módulo principal', pois é o módulo que contém a função pesquisada, não o módulo principal ... E procurar o atributo a partir de uma instância ref é uma coisa diferente, mas esta resposta explica por que você precisa da instância / classe ref.
10119 Jeff Shannon
10
class Foo(object):
     bar = 1
     def bah(self):
         print Foo.bar

f = Foo() 
f.bah()
Allan Mwesigwa
fonte
-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
fonte
4
Embora esse código possa resolver a questão, incluir uma explicação de como e por que isso resolve o problema realmente ajudaria a melhorar a qualidade da sua postagem e provavelmente resultaria em mais votos positivos. Lembre-se de que você está respondendo à pergunta dos leitores no futuro, não apenas à pessoa que está perguntando agora. Por favor edite sua resposta para adicionar explicações e dar uma indicação do que limitações e premissas se aplicam. Da avaliação
double-beep