As variáveis ​​de classe estática são possíveis no Python?


Respostas:


1900

Variáveis ​​declaradas dentro da definição de classe, mas não dentro de um método, são variáveis ​​de classe ou estáticas:

>>> class MyClass:
...     i = 3
...
>>> MyClass.i
3 

Como o @ millerdev aponta, isso cria uma ivariável no nível da classe , mas é diferente de qualquer ivariável no nível da instância , então você pode ter

>>> m = MyClass()
>>> m.i = 4
>>> MyClass.i, m.i
>>> (3, 4)

Isso é diferente de C ++ e Java, mas não tão diferente de C #, onde um membro estático não pode ser acessado usando uma referência a uma instância.

Veja o que o tutorial do Python tem a dizer sobre o assunto de classes e objetos de classe .

@ Steve Johnson já respondeu sobre métodos estáticos , também documentados em "Funções internas " na Referência da biblioteca do Python .

class C:
    @staticmethod
    def f(arg1, arg2, ...): ...

O @beidy recomenda classmethod s sobre staticmethod, pois o método recebe o tipo de classe como o primeiro argumento, mas ainda estou um pouco confuso sobre as vantagens dessa abordagem sobre o staticmethod. Se você também, provavelmente não importa.


11
Eu estou aprendendo Python, mas as vantagens de @classmethodmais de @staticmethodAFAIK é que você sempre obter o nome da classe do método foi invocado, mesmo que seja uma subclasse. Um método estático não possui essas informações, portanto, não pode chamar um método substituído, por exemplo.
Seb

49
@theJollySin da maneira pitônica para constantes é não criar uma classe para constantes. Basta ter um pouco const.pycom PI = 3.14e você pode importá-lo em qualquer lugar. from const import PI
Giszmo

30
É provável que esta resposta confunda o problema da variável estática. Para começar, nãoi = 3 é uma variável estática, é um atributo de classe e, como é distinto de um atributo no nível da instância , não se comporta como uma variável estática em outros idiomas. Veja a resposta de millerdev , a resposta de Yann , e minha resposta abaixo. i
Rick apoia Monica

2
então apenas uma cópia de i(variável estática) estará na memória, mesmo que eu crie centenas de instâncias dessa classe?
Sdream

2
Para qualquer um interessado que está Daniel mencionado no comentário @Dubslow, é millerdev ( máquina do tempo )
heyjude

618

@Blair Conrad disse que variáveis ​​estáticas declaradas dentro da definição de classe, mas não dentro de um método, são variáveis ​​de classe ou "estáticas":

>>> class Test(object):
...     i = 3
...
>>> Test.i
3

Há algumas pegadinhas aqui. Continuando a partir do exemplo acima:

>>> t = Test()
>>> t.i     # "static" variable accessed via instance
3
>>> t.i = 5 # but if we assign to the instance ...
>>> Test.i  # we have not changed the "static" variable
3
>>> t.i     # we have overwritten Test.i on t by creating a new attribute t.i
5
>>> Test.i = 6 # to change the "static" variable we do it by assigning to the class
>>> t.i
5
>>> Test.i
6
>>> u = Test()
>>> u.i
6           # changes to t do not affect new instances of Test

# Namespaces are one honking great idea -- let's do more of those!
>>> Test.__dict__
{'i': 6, ...}
>>> t.__dict__
{'i': 5}
>>> u.__dict__
{}

Observe como a variável de instância t.ificou fora de sincronia com a variável de classe "estática" quando o atributo ifoi definido diretamente t. Isso ocorre porque ifoi ligado novamente no tespaço para nome, que é diferente do Testespaço para nome. Se você deseja alterar o valor de uma variável "estática", altere-a no escopo (ou objeto) em que foi originalmente definida. Coloquei "estático" entre aspas porque o Python realmente não tem variáveis ​​estáticas no sentido em que C ++ e Java o fazem.

Embora não diga nada específico sobre variáveis ​​ou métodos estáticos, o tutorial do Python possui algumas informações relevantes sobre classes e objetos de classe .

@ Steve Johnson também respondeu sobre métodos estáticos, também documentados em "Funções internas" na Referência da biblioteca do Python.

class Test(object):
    @staticmethod
    def f(arg1, arg2, ...):
        ...

@beid também mencionou classmethod, que é semelhante ao staticmethod. O primeiro argumento de um método de classe é o objeto de classe. Exemplo:

class Test(object):
    i = 3 # class (or static) variable
    @classmethod
    def g(cls, arg):
        # here we can use 'cls' instead of the class name (Test)
        if arg > cls.i:
            cls.i = arg # would be the same as Test.i = arg1

Representação pictórica do exemplo acima


3
Sugiro que você estenda um pouco o exemplo: se, depois de definir Test.i = 6, você instancia um novo objeto (por exemplo, u = Test ()), o novo objeto "herdará" o novo valor da classe (por exemplo, ui == 6)
Marcos

2
Uma maneira de manter as variáveis estáticas em sincronia é torná-los propriedades: class Test(object):, _i = 3, @property, def i(self), return type(self)._i, @i.setter, def i(self,val):, type(self)._i = val. Agora você pode fazer x = Test(), x.i = 12, assert x.i == Test.i.
Rick apoia Monica

1
Então, eu poderia dizer que todas as variáveis ​​são estáticas inicialmente e, em seguida, acessar instâncias cria variáveis ​​de instância em tempo de execução?
Ali

Talvez isso seja interessante: se você definir um método no Teste que altera o Test.i, isso afetará os valores de Test.i e ti.
Pablo

@ millerdev, como você mencionou, o Python não tem variáveis ​​estáticas como C ++ ou JAVA. Então, tudo bem dizer que o Test.i é mais uma variável de classe do que uma variável estática?
21418 Tyto

197

Métodos estáticos e de classe

Como as outras respostas observaram, os métodos estáticos e de classe são facilmente realizados usando os decoradores internos:

class Test(object):

    # regular instance method:
    def MyMethod(self):
        pass

    # class method:
    @classmethod
    def MyClassMethod(klass):
        pass

    # static method:
    @staticmethod
    def MyStaticMethod():
        pass

Como sempre, o primeiro argumento para MyMethod()é vinculado ao objeto de instância da classe. Por outro lado, o primeiro argumento para MyClassMethod()é vinculado ao próprio objeto de classe (por exemplo, neste caso Test). Pois MyStaticMethod(), nenhum dos argumentos é vinculado e ter argumentos é opcional.

"Variáveis ​​estáticas"

No entanto, implementar "variáveis ​​estáticas" (bem, variáveis ​​estáticas mutáveis , de qualquer forma, se isso não for uma contradição em termos ...) não é tão simples. Como millerdev apontou em sua resposta , o problema é que os atributos de classe do Python não são verdadeiramente "variáveis ​​estáticas". Considerar:

class Test(object):
    i = 3  # This is a class attribute

x = Test()
x.i = 12   # Attempt to change the value of the class attribute using x instance
assert x.i == Test.i  # ERROR
assert Test.i == 3    # Test.i was not affected
assert x.i == 12      # x.i is a different object than Test.i

Isso ocorre porque a linha x.i = 12adicionou um novo atributo de instância iao xinvés de alterar o valor do atributo de Testclasse i.

O comportamento parcial esperado da variável estática, ou seja, a sincronização do atributo entre várias instâncias (mas não com a própria classe; consulte "pegadinha" abaixo), pode ser alcançado transformando o atributo da classe em uma propriedade:

class Test(object):

    _i = 3

    @property
    def i(self):
        return type(self)._i

    @i.setter
    def i(self,val):
        type(self)._i = val

## ALTERNATIVE IMPLEMENTATION - FUNCTIONALLY EQUIVALENT TO ABOVE ##
## (except with separate methods for getting and setting i) ##

class Test(object):

    _i = 3

    def get_i(self):
        return type(self)._i

    def set_i(self,val):
        type(self)._i = val

    i = property(get_i, set_i)

Agora você pode fazer:

x1 = Test()
x2 = Test()
x1.i = 50
assert x2.i == x1.i  # no error
assert x2.i == 50    # the property is synced

A variável estática agora permanecerá sincronizada entre todas as instâncias de classe .

(NOTA: Ou seja, a menos que uma instância de classe decida definir sua própria versão do _i! Mas se alguém decide fazer ISSO, ele merece o que recebe, não é ???)

Observe que tecnicamente falando, iainda não é uma 'variável estática'; é um property, que é um tipo especial de descritor. No entanto, o propertycomportamento agora é equivalente a uma variável estática (mutável) sincronizada em todas as instâncias de classe.

"Variáveis ​​estáticas" imutáveis

Para comportamento variável estático imutável, simplesmente omita o propertysetter:

class Test(object):

    _i = 3

    @property
    def i(self):
        return type(self)._i

## ALTERNATIVE IMPLEMENTATION - FUNCTIONALLY EQUIVALENT TO ABOVE ##
## (except with separate methods for getting i) ##

class Test(object):

    _i = 3

    def get_i(self):
        return type(self)._i

    i = property(get_i)

Agora, tentar definir o iatributo de instância retornará um AttributeError:

x = Test()
assert x.i == 3  # success
x.i = 12         # ERROR

Um Gotcha para estar ciente de

Observe que os métodos acima funcionam apenas com instâncias da sua classe - eles não funcionarão ao usar a própria classe . Então, por exemplo:

x = Test()
assert x.i == Test.i  # ERROR

# x.i and Test.i are two different objects:
type(Test.i)  # class 'property'
type(x.i)     # class 'int'

A linha assert Test.i == x.iproduz um erro, porque o iatributo Teste xsão dois objetos diferentes.

Muitas pessoas vão achar isso surpreendente. No entanto, não deveria ser. Se voltarmos e inspecionarmos nossa Testdefinição de classe (a segunda versão), tomaremos nota desta linha:

    i = property(get_i) 

Claramente, o membro ide Testdeve ser um propertyobjeto, que é o tipo de objeto retornado da propertyfunção.

Se você acha que o exposto acima é confuso, é provável que ainda pense nisso da perspectiva de outras linguagens (por exemplo, Java ou c ++). Você deve estudar o propertyobjeto, sobre a ordem em que os atributos do Python são retornados, o protocolo do descritor e a ordem de resolução do método (MRO).

Apresento uma solução para o 'pegadinha' acima; no entanto, eu sugeriria - enfaticamente - que você não tente fazer algo como o seguinte até - no mínimo - entender completamente o porquê de assert Test.i = x.icausar um erro.

Variáveis ​​estáticas reais e reais -Test.i == x.i

Apresento a solução (Python 3) abaixo apenas para fins informativos. Não estou endossando-o como uma "boa solução". Eu tenho minhas dúvidas sobre se emular o comportamento da variável estática de outras linguagens no Python é realmente necessário. No entanto, independentemente de ser realmente útil, o abaixo deve ajudar a entender melhor como o Python funciona.

UPDATE: esta tentativa é realmente bastante terrível ; se você insistir em fazer algo assim (dica: por favor, não; Python é uma linguagem muito elegante e faz com que ela se comporte como outra linguagem não é necessária), use o código na resposta de Ethan Furman .

Emulando o comportamento da variável estática de outros idiomas usando uma metaclasse

Uma metaclasse é a classe de uma classe. A metaclasse padrão para todas as classes no Python (ou seja, as classes de "novo estilo" após o Python 2.3, acredito) é type. Por exemplo:

type(int)  # class 'type'
type(str)  # class 'type'
class Test(): pass
type(Test) # class 'type'

No entanto, você pode definir sua própria metaclasse assim:

class MyMeta(type): pass

E aplique-o à sua própria classe como esta (somente Python 3):

class MyClass(metaclass = MyMeta):
    pass

type(MyClass)  # class MyMeta

Abaixo está uma metaclasse que eu criei que tenta emular o comportamento da "variável estática" de outras linguagens. Basicamente, ele substitui o getter, setter e deleter padrão por versões que verificam se o atributo solicitado é uma "variável estática".

Um catálogo das "variáveis ​​estáticas" é armazenado no StaticVarMeta.staticsatributo Todas as solicitações de atributo são inicialmente tentadas a serem resolvidas usando uma ordem de resolução substituta. Eu chamei isso de "ordem de resolução estática" ou "SRO". Isso é feito procurando o atributo solicitado no conjunto de "variáveis ​​estáticas" para uma determinada classe (ou suas classes pai). Se o atributo não aparecer no "SRO", a classe retornará ao comportamento padrão de obter / definir / excluir do atributo padrão (ou seja, "MRO").

from functools import wraps

class StaticVarsMeta(type):
    '''A metaclass for creating classes that emulate the "static variable" behavior
    of other languages. I do not advise actually using this for anything!!!

    Behavior is intended to be similar to classes that use __slots__. However, "normal"
    attributes and __statics___ can coexist (unlike with __slots__). 

    Example usage: 

        class MyBaseClass(metaclass = StaticVarsMeta):
            __statics__ = {'a','b','c'}
            i = 0  # regular attribute
            a = 1  # static var defined (optional)

        class MyParentClass(MyBaseClass):
            __statics__ = {'d','e','f'}
            j = 2              # regular attribute
            d, e, f = 3, 4, 5  # Static vars
            a, b, c = 6, 7, 8  # Static vars (inherited from MyBaseClass, defined/re-defined here)

        class MyChildClass(MyParentClass):
            __statics__ = {'a','b','c'}
            j = 2  # regular attribute (redefines j from MyParentClass)
            d, e, f = 9, 10, 11   # Static vars (inherited from MyParentClass, redefined here)
            a, b, c = 12, 13, 14  # Static vars (overriding previous definition in MyParentClass here)'''
    statics = {}
    def __new__(mcls, name, bases, namespace):
        # Get the class object
        cls = super().__new__(mcls, name, bases, namespace)
        # Establish the "statics resolution order"
        cls.__sro__ = tuple(c for c in cls.__mro__ if isinstance(c,mcls))

        # Replace class getter, setter, and deleter for instance attributes
        cls.__getattribute__ = StaticVarsMeta.__inst_getattribute__(cls, cls.__getattribute__)
        cls.__setattr__ = StaticVarsMeta.__inst_setattr__(cls, cls.__setattr__)
        cls.__delattr__ = StaticVarsMeta.__inst_delattr__(cls, cls.__delattr__)
        # Store the list of static variables for the class object
        # This list is permanent and cannot be changed, similar to __slots__
        try:
            mcls.statics[cls] = getattr(cls,'__statics__')
        except AttributeError:
            mcls.statics[cls] = namespace['__statics__'] = set() # No static vars provided
        # Check and make sure the statics var names are strings
        if any(not isinstance(static,str) for static in mcls.statics[cls]):
            typ = dict(zip((not isinstance(static,str) for static in mcls.statics[cls]), map(type,mcls.statics[cls])))[True].__name__
            raise TypeError('__statics__ items must be strings, not {0}'.format(typ))
        # Move any previously existing, not overridden statics to the static var parent class(es)
        if len(cls.__sro__) > 1:
            for attr,value in namespace.items():
                if attr not in StaticVarsMeta.statics[cls] and attr != ['__statics__']:
                    for c in cls.__sro__[1:]:
                        if attr in StaticVarsMeta.statics[c]:
                            setattr(c,attr,value)
                            delattr(cls,attr)
        return cls
    def __inst_getattribute__(self, orig_getattribute):
        '''Replaces the class __getattribute__'''
        @wraps(orig_getattribute)
        def wrapper(self, attr):
            if StaticVarsMeta.is_static(type(self),attr):
                return StaticVarsMeta.__getstatic__(type(self),attr)
            else:
                return orig_getattribute(self, attr)
        return wrapper
    def __inst_setattr__(self, orig_setattribute):
        '''Replaces the class __setattr__'''
        @wraps(orig_setattribute)
        def wrapper(self, attr, value):
            if StaticVarsMeta.is_static(type(self),attr):
                StaticVarsMeta.__setstatic__(type(self),attr, value)
            else:
                orig_setattribute(self, attr, value)
        return wrapper
    def __inst_delattr__(self, orig_delattribute):
        '''Replaces the class __delattr__'''
        @wraps(orig_delattribute)
        def wrapper(self, attr):
            if StaticVarsMeta.is_static(type(self),attr):
                StaticVarsMeta.__delstatic__(type(self),attr)
            else:
                orig_delattribute(self, attr)
        return wrapper
    def __getstatic__(cls,attr):
        '''Static variable getter'''
        for c in cls.__sro__:
            if attr in StaticVarsMeta.statics[c]:
                try:
                    return getattr(c,attr)
                except AttributeError:
                    pass
        raise AttributeError(cls.__name__ + " object has no attribute '{0}'".format(attr))
    def __setstatic__(cls,attr,value):
        '''Static variable setter'''
        for c in cls.__sro__:
            if attr in StaticVarsMeta.statics[c]:
                setattr(c,attr,value)
                break
    def __delstatic__(cls,attr):
        '''Static variable deleter'''
        for c in cls.__sro__:
            if attr in StaticVarsMeta.statics[c]:
                try:
                    delattr(c,attr)
                    break
                except AttributeError:
                    pass
        raise AttributeError(cls.__name__ + " object has no attribute '{0}'".format(attr))
    def __delattr__(cls,attr):
        '''Prevent __sro__ attribute from deletion'''
        if attr == '__sro__':
            raise AttributeError('readonly attribute')
        super().__delattr__(attr)
    def is_static(cls,attr):
        '''Returns True if an attribute is a static variable of any class in the __sro__'''
        if any(attr in StaticVarsMeta.statics[c] for c in cls.__sro__):
            return True
        return False

Eu tentei usar o seu caminho, mas eu enfrentei um problema, por favor dê uma olhada no meu pergunta aqui stackoverflow.com/questions/29329850/get-static-variable-value
Muhammed Refaat

@ RickTeachey: Eu acho que você geralmente deve ver tudo o que faz na instância de classe Test(antes de usá-lo para instanciar instâncias) como estando no domínio da metaprogramação? Por exemplo, você altera o comportamento da classe fazendo Test.i = 0(aqui você simplesmente destrói completamente o objeto de propriedade). Eu acho que o "mecanismo de propriedade" entra em ação apenas no acesso à propriedade em instâncias de uma classe (a menos que você altere o comportamento subjacente usando uma meta-classe como intermediária, talvez). Btw, por favor terminar esta resposta :-)
Ole Thomsen Buus

1
@ RickTeachey Obrigado :-) Sua metaclasse no final é interessante, mas na verdade é um pouco complexa demais para o meu gosto. Pode ser útil em uma grande estrutura / aplicativo em que esse mecanismo é absolutamente necessário. De qualquer forma, isso exemplifica que, se novos (complexo) não-padrão meta-comportamento é realmente necessário, Python torna possível :)
Ole Thomsen Buus

1
@OleThomsenBuus: Verifique minha resposta para uma metaclasse mais simples que faça o trabalho.
Ethan Furman

1
@taper Você está correto; Eu editei a resposta para corrigir o problema (não acredito que ele esteja parado lá por tanto tempo!). Desculpe pela confusão.
Rick suporta Monica

33

Você também pode adicionar variáveis ​​de classe às classes em tempo real

>>> class X:
...     pass
... 
>>> X.bar = 0
>>> x = X()
>>> x.bar
0
>>> x.foo
Traceback (most recent call last):
  File "<interactive input>", line 1, in <module>
AttributeError: X instance has no attribute 'foo'
>>> X.foo = 1
>>> x.foo
1

E instâncias de classe podem alterar variáveis ​​de classe

class X:
  l = []
  def __init__(self):
    self.l.append(1)

print X().l
print X().l

>python test.py
[1]
[1, 1]

3
As novas variáveis ​​de classe permanecerão mesmo se a classe for importada para outro módulo?
Zakdances

Sim. As classes são efetivamente singletons, independentemente do namespace de onde você as chama.
Pedro

@ Gregory você disse "E instâncias de classe podem alterar variáveis ​​de classe" Na verdade, este exemplo é chamado de acesso, não modificação. A modificação foi feita pelo próprio objeto através de sua própria função append ().
Amr ALHOSSARY

19

Pessoalmente, usaria um método de classe sempre que precisasse de um método estático. Principalmente porque recebo a classe como argumento.

class myObj(object):
   def myMethod(cls)
     ...
   myMethod = classmethod(myMethod) 

ou use um decorador

class myObj(object):
   @classmethod
   def myMethod(cls)

Para propriedades estáticas .. É hora de procurar alguma definição em python. A variável sempre pode mudar. Existem dois tipos deles mutáveis ​​e imutáveis. Além disso, existem atributos de classe e atributos de instância. Nada como atributos estáticos no sentido de java & c ++

Por que usar o método estático no sentido pitônico, se não tem nenhuma relação com a classe! Se eu fosse você, usaria o método class ou definiria o método independentemente da classe.


1
Variáveis ​​não são mutáveis ​​ou imutáveis; objetos são. (No entanto, um objeto pode, com variados graus de sucesso, tentar impedir a atribuição a alguns dos seus atributos.)
Davis Herring

Java e C ++ usam estática (uso incorreto da palavra, imho) exatamente como você usa o atributo instância versus classe. Um atributo / método de classe é estático em Java e C ++, sem diferença, exceto que em Python o primeiro parâmetro para uma chamada de método de classe é a classe.
Angel O'Sphere

16

Uma coisa especial a ser observada sobre propriedades estáticas e propriedades da instância, mostradas no exemplo abaixo:

class my_cls:
  my_prop = 0

#static property
print my_cls.my_prop  #--> 0

#assign value to static property
my_cls.my_prop = 1 
print my_cls.my_prop  #--> 1

#access static property thru' instance
my_inst = my_cls()
print my_inst.my_prop #--> 1

#instance property is different from static property 
#after being assigned a value
my_inst.my_prop = 2
print my_cls.my_prop  #--> 1
print my_inst.my_prop #--> 2

Isso significa que, antes de atribuir o valor à propriedade da instância, se tentarmos acessar a propriedade através da instância, o valor estático será usado. Cada propriedade declarada na classe python sempre possui um slot estático na memória .


16

Métodos estáticos em python são chamados classmethod s. Dê uma olhada no código a seguir

class MyClass:

    def myInstanceMethod(self):
        print 'output from an instance method'

    @classmethod
    def myStaticMethod(cls):
        print 'output from a static method'

>>> MyClass.myInstanceMethod()
Traceback (most recent call last):
  File "<stdin>", line 1, in <module>
TypeError: unbound method myInstanceMethod() must be called [...]

>>> MyClass.myStaticMethod()
output from a static method

Observe que quando chamamos o método myInstanceMethod , obtemos um erro. Isso ocorre porque requer que o método seja chamado em uma instância desta classe. O método myStaticMethod é definido como um método de classe usando o decorador @classmethod .

Apenas para brincadeiras e risadinhas, poderíamos chamar myInstanceMethod na classe passando uma instância da classe, assim:

>>> MyClass.myInstanceMethod(MyClass())
output from an instance method

2
Umm ... métodos estáticos são feitos com @staticmethod; @classmethodé (obviamente) para métodos de classe (que se destinam principalmente ao uso como construtores alternativos, mas podem servir como uma pinça como métodos estáticos que recebem uma referência à classe pela qual foram chamados).
ShadowRanger

11

Ao definir alguma variável de membro fora de qualquer método de membro, a variável pode ser estática ou não estática, dependendo de como a variável é expressa.

  • CLASSNAME.var é uma variável estática
  • INSTANCENAME.var não é variável estática.
  • self.var dentro da classe não é variável estática.
  • var dentro da função de membro da classe não está definido.

Por exemplo:

#!/usr/bin/python

class A:
    var=1

    def printvar(self):
        print "self.var is %d" % self.var
        print "A.var is %d" % A.var


    a = A()
    a.var = 2
    a.printvar()

    A.var = 3
    a.printvar()

Os resultados são

self.var is 2
A.var is 1
self.var is 2
A.var is 3

O recuo está quebrado. Isso não será executado
Thomas Weller

9

É possível ter staticvariáveis ​​de classe, mas provavelmente não vale o esforço.

Aqui está uma prova de conceito escrita em Python 3 - se algum detalhe exato estiver errado, o código poderá ser ajustado para corresponder exatamente ao que você quer dizer com static variable:


class Static:
    def __init__(self, value, doc=None):
        self.deleted = False
        self.value = value
        self.__doc__ = doc
    def __get__(self, inst, cls=None):
        if self.deleted:
            raise AttributeError('Attribute not set')
        return self.value
    def __set__(self, inst, value):
        self.deleted = False
        self.value = value
    def __delete__(self, inst):
        self.deleted = True

class StaticType(type):
    def __delattr__(cls, name):
        obj = cls.__dict__.get(name)
        if isinstance(obj, Static):
            obj.__delete__(name)
        else:
            super(StaticType, cls).__delattr__(name)
    def __getattribute__(cls, *args):
        obj = super(StaticType, cls).__getattribute__(*args)
        if isinstance(obj, Static):
            obj = obj.__get__(cls, cls.__class__)
        return obj
    def __setattr__(cls, name, val):
        # check if object already exists
        obj = cls.__dict__.get(name)
        if isinstance(obj, Static):
            obj.__set__(name, val)
        else:
            super(StaticType, cls).__setattr__(name, val)

e em uso:

class MyStatic(metaclass=StaticType):
    """
    Testing static vars
    """
    a = Static(9)
    b = Static(12)
    c = 3

class YourStatic(MyStatic):
    d = Static('woo hoo')
    e = Static('doo wop')

e alguns testes:

ms1 = MyStatic()
ms2 = MyStatic()
ms3 = MyStatic()
assert ms1.a == ms2.a == ms3.a == MyStatic.a
assert ms1.b == ms2.b == ms3.b == MyStatic.b
assert ms1.c == ms2.c == ms3.c == MyStatic.c
ms1.a = 77
assert ms1.a == ms2.a == ms3.a == MyStatic.a
ms2.b = 99
assert ms1.b == ms2.b == ms3.b == MyStatic.b
MyStatic.a = 101
assert ms1.a == ms2.a == ms3.a == MyStatic.a
MyStatic.b = 139
assert ms1.b == ms2.b == ms3.b == MyStatic.b
del MyStatic.b
for inst in (ms1, ms2, ms3):
    try:
        getattr(inst, 'b')
    except AttributeError:
        pass
    else:
        print('AttributeError not raised on %r' % attr)
ms1.c = 13
ms2.c = 17
ms3.c = 19
assert ms1.c == 13
assert ms2.c == 17
assert ms3.c == 19
MyStatic.c = 43
assert ms1.c == 13
assert ms2.c == 17
assert ms3.c == 19

ys1 = YourStatic()
ys2 = YourStatic()
ys3 = YourStatic()
MyStatic.b = 'burgler'
assert ys1.a == ys2.a == ys3.a == YourStatic.a == MyStatic.a
assert ys1.b == ys2.b == ys3.b == YourStatic.b == MyStatic.b
assert ys1.d == ys2.d == ys3.d == YourStatic.d
assert ys1.e == ys2.e == ys3.e == YourStatic.e
ys1.a = 'blah'
assert ys1.a == ys2.a == ys3.a == YourStatic.a == MyStatic.a
ys2.b = 'kelp'
assert ys1.b == ys2.b == ys3.b == YourStatic.b == MyStatic.b
ys1.d = 'fee'
assert ys1.d == ys2.d == ys3.d == YourStatic.d
ys2.e = 'fie'
assert ys1.e == ys2.e == ys3.e == YourStatic.e
MyStatic.a = 'aargh'
assert ys1.a == ys2.a == ys3.a == YourStatic.a == MyStatic.a

8

Você também pode fazer com que uma classe seja estática usando a metaclasse.

class StaticClassError(Exception):
    pass


class StaticClass:
    __metaclass__ = abc.ABCMeta

    def __new__(cls, *args, **kw):
        raise StaticClassError("%s is a static class and cannot be initiated."
                                % cls)

class MyClass(StaticClass):
    a = 1
    b = 3

    @staticmethod
    def add(x, y):
        return x+y

Então, sempre que por acidente você tentar inicializar o MyClass, receberá um StaticClassError.


4
Por que é uma aula se você não vai instanciar? Isto parece torcer Python para transformá-lo em Java ....
Ned Batchelder

1
O idioma Borg é uma maneira melhor de lidar com isso.
Rick suporta Monica

@NedBatchelder É uma classe abstrata, destinado apenas para subclasses (e instanciar as subclasses)
stevepastelan

1
Espero que as subclasses não usem super () para invocar os __new__de seus pais ...
Ned Batchelder

7

Um ponto muito interessante sobre a pesquisa de atributos do Python é que ele pode ser usado para criar " variáveis virtuais ":

class A(object):

  label="Amazing"

  def __init__(self,d): 
      self.data=d

  def say(self): 
      print("%s %s!"%(self.label,self.data))

class B(A):
  label="Bold"  # overrides A.label

A(5).say()      # Amazing 5!
B(3).say()      # Bold 3!

Normalmente, não há atribuições para elas depois que elas são criadas. Observe que a pesquisa usa selfporque, embora labelseja estático no sentido de não estar associado a uma instância específica , o valor ainda depende da instância (classe da).


6

Em relação a esta resposta , para uma variável estática constante , você pode usar um descritor. Aqui está um exemplo:

class ConstantAttribute(object):
    '''You can initialize my value but not change it.'''
    def __init__(self, value):
        self.value = value

    def __get__(self, obj, type=None):
        return self.value

    def __set__(self, obj, val):
        pass


class Demo(object):
    x = ConstantAttribute(10)


class SubDemo(Demo):
    x = 10


demo = Demo()
subdemo = SubDemo()
# should not change
demo.x = 100
# should change
subdemo.x = 100
print "small demo", demo.x
print "small subdemo", subdemo.x
print "big demo", Demo.x
print "big subdemo", SubDemo.x

resultando em ...

small demo 10
small subdemo 100
big demo 10
big subdemo 10

Você sempre pode gerar uma exceção se ignorar silenciosamente o valor da configuração ( passacima) não é o seu problema. Se você estiver procurando por uma variável de classe estática no estilo C ++, Java:

class StaticAttribute(object):
    def __init__(self, value):
        self.value = value

    def __get__(self, obj, type=None):
        return self.value

    def __set__(self, obj, val):
        self.value = val

Veja esta resposta e o HOWTO da documentação oficial para obter mais informações sobre descritores.


2
Você também pode usar @property, que é o mesmo que usar um descritor, mas é muito menos código.
Rick apoia Monica

6

Absolutamente sim, o Python por si só não tem nenhum membro de dados estático explicitamente, mas podemos fazê-lo

class A:
    counter =0
    def callme (self):
        A.counter +=1
    def getcount (self):
        return self.counter  
>>> x=A()
>>> y=A()
>>> print(x.getcount())
>>> print(y.getcount())
>>> x.callme() 
>>> print(x.getcount())
>>> print(y.getcount())

resultado

0
0
1
1

explicação

here object (x) alone increment the counter variable
from 0 to 1 by not object y. But result it as "static counter"

6

Sim, definitivamente é possível escrever variáveis ​​e métodos estáticos em python.

Variáveis ​​estáticas: a variável declarada no nível da classe é chamada de variável estática, que pode ser acessada diretamente usando o nome da classe.

    >>> class A:
        ...my_var = "shagun"

    >>> print(A.my_var)
        shagun

Variáveis ​​de instância: variáveis ​​relacionadas e acessadas por instância de uma classe são variáveis ​​de instância.

   >>> a = A()
   >>> a.my_var = "pruthi"
   >>> print(A.my_var,a.my_var)
       shagun pruthi

Métodos estáticos: Semelhante às variáveis, os métodos estáticos podem ser acessados ​​diretamente usando a classe Name. Não há necessidade de criar uma instância.

Mas lembre-se, um método estático não pode chamar um método não estático em python.

    >>> class A:
   ...     @staticmethod
   ...     def my_static_method():
   ...             print("Yippey!!")
   ... 
   >>> A.my_static_method()
   Yippey!!

4

Para evitar qualquer confusão em potencial, gostaria de contrastar variáveis ​​estáticas e objetos imutáveis.

Alguns tipos de objetos primitivos, como números inteiros, flutuantes, strings e toques, são imutáveis ​​no Python. Isso significa que o objeto referido por um determinado nome não pode ser alterado se for de um dos tipos de objeto mencionados acima. O nome pode ser reatribuído para um objeto diferente, mas o próprio objeto não pode ser alterado.

Tornar uma variável estática leva isso um passo adiante, impedindo que o nome da variável aponte para qualquer objeto, exceto para o qual ele aponta atualmente. (Nota: este é um conceito geral de software e não é específico para o Python; consulte as postagens de outras pessoas para obter informações sobre a implementação de estática no Python).


4

A melhor maneira que encontrei é usar outra classe. Você pode criar um objeto e usá-lo em outros objetos.

class staticFlag:
    def __init__(self):
        self.__success = False
    def isSuccess(self):
        return self.__success
    def succeed(self):
        self.__success = True

class tryIt:
    def __init__(self, staticFlag):
        self.isSuccess = staticFlag.isSuccess
        self.succeed = staticFlag.succeed

tryArr = []
flag = staticFlag()
for i in range(10):
    tryArr.append(tryIt(flag))
    if i == 5:
        tryArr[i].succeed()
    print tryArr[i].isSuccess()

Com o exemplo acima, criei uma classe chamada staticFlag .

Esta classe deve apresentar a var estática __success(Var estática privada).

tryIt classe representou a classe regular que precisamos usar.

Agora eu criei um objeto para uma bandeira (staticFlag ). Este sinalizador será enviado como referência a todos os objetos regulares.

Todos esses objetos estão sendo adicionados à lista tryArr.


Resultados deste script:

False
False
False
False
False
True
True
True
True
True

2

Variáveis ​​estáticas na classe factory python3.6

Para quem usa uma fábrica de classes com python3.6 e superior, use a nonlocalpalavra-chave para adicioná-la ao escopo / contexto da classe que está sendo criada da seguinte forma:

>>> def SomeFactory(some_var=None):
...     class SomeClass(object):
...         nonlocal some_var
...         def print():
...             print(some_var)
...     return SomeClass
... 
>>> SomeFactory(some_var="hello world").print()
hello world

sim, mas neste caso hasattr(SomeClass, 'x')é False. duvido que seja isso que alguém queira dizer com uma variável estática.
Rick suporta Monica

@ RickTeachey lol, vi seu código de variável estática, stackoverflow.com/a/27568860/2026508 +1 internet sir, e achei que o hasattr não funcionava assim? então é some_varimutável e estaticamente definido, ou não é? O que o acesso externo ao getter tem a ver com uma variável ser estática ou não? Eu tenho tantas perguntas agora. adoraria ouvir algumas respostas quando chegar a hora.
jmunsch

Sim, essa metaclasse é bem ridícula. Não tenho certeza de que entendi as perguntas, mas, na minha opinião, some_varacima não é um membro da classe. No Python, todos os membros da classe podem ser acessados ​​de fora da classe.
Rick apoia Monica

O nonlocalteclado "esbarra" no escopo da variável. O escopo de uma definição de corpo de classe é independente do escopo em que você se encontra, quando você diz nonlocal some_varque está apenas criando uma referência de nome não local (leia-se: NÃO no escopo de definição de classe) para outro objeto nomeado. Portanto, ele não é anexado à definição de classe porque não está no escopo do corpo da classe.
Rick apoia Monica

1

Então isso é provavelmente um truque, mas eu tenho usado eval(str) para obter um objeto estático, como uma contradição, no python 3.

Há um arquivo Records.py que não possui nada além de classobjetos definidos com métodos e construtores estáticos que salvam alguns argumentos. Em seguida, de outro arquivo .py, import Recordseu preciso selecionar dinamicamente cada objeto e instancia-lo sob demanda, de acordo com o tipo de dados que está sendo lido.

Então, onde object_name = 'RecordOne'ou o nome da classe, eu chamocur_type = eval(object_name) e, em seguida, instancia-lo? cur_inst = cur_type(args) No entanto, antes de instanciar, você pode chamar métodos estáticos, cur_type.getName()por exemplo, como a implementação abstrata da classe base ou qualquer que seja o objetivo. No entanto, no back-end, provavelmente é instanciado em python e não é realmente estático, porque eval está retornando um objeto .... que deve ter sido instanciado ... que fornece um comportamento estático.


0

Você pode usar uma lista ou um dicionário para obter "comportamento estático" entre instâncias.

class Fud:

     class_vars = {'origin_open':False}

     def __init__(self, origin = True):
         self.origin = origin
         self.opened = True
         if origin:
             self.class_vars['origin_open'] = True


     def make_another_fud(self):
         ''' Generating another Fud() from the origin instance '''

         return Fud(False)


     def close(self):
         self.opened = False
         if self.origin:
             self.class_vars['origin_open'] = False


fud1 = Fud()
fud2 = fud1.make_another_fud()

print (f"is this the original fud: {fud2.origin}")
print (f"is the original fud open: {fud2.class_vars['origin_open']}")
# is this the original fud: False
# is the original fud open: True

fud1.close()

print (f"is the original fud open: {fud2.class_vars['origin_open']}")
# is the original fud open: False

0

Se você está tentando compartilhar uma variável estática para, por exemplo, aumentá-la em outras instâncias, algo como este script funciona bem:

# -*- coding: utf-8 -*-
class Worker:
    id = 1

    def __init__(self):
        self.name = ''
        self.document = ''
        self.id = Worker.id
        Worker.id += 1

    def __str__(self):
        return u"{}.- {} {}".format(self.id, self.name, self.document).encode('utf8')


class Workers:
    def __init__(self):
        self.list = []

    def add(self, name, doc):
        worker = Worker()
        worker.name = name
        worker.document = doc
        self.list.append(worker)


if __name__ == "__main__":
    workers = Workers()
    for item in (('Fiona', '0009898'), ('Maria', '66328191'), ("Sandra", '2342184'), ('Elvira', '425872')):
        workers.add(item[0], item[1])
    for worker in workers.list:
        print(worker)
    print("next id: %i" % Worker.id)
Ao utilizar nosso site, você reconhece que leu e compreendeu nossa Política de Cookies e nossa Política de Privacidade.
Licensed under cc by-sa 3.0 with attribution required.