Le variabili di classe statiche sono possibili in Python?


Risposte:


1900

Le variabili dichiarate all'interno della definizione di classe, ma non all'interno di un metodo sono variabili di classe o statiche:

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

Come sottolinea @ millerdev , questo crea una ivariabile a livello di classe , ma questa è distinta da qualsiasi ivariabile a livello di istanza , quindi potresti avere

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

Questo è diverso da C ++ e Java, ma non così diverso da C #, in cui non è possibile accedere a un membro statico utilizzando un riferimento a un'istanza.

Scopri cosa ha da dire il tutorial su Python in materia di classi e oggetti di classe .

@Steve Johnson ha già risposto in merito ai metodi statici , documentato anche in "Funzioni integrate" nella Python Library Reference .

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

@beidy raccomanda i metodi di classe su staticmethod, poiché il metodo riceve quindi il tipo di classe come primo argomento, ma sono ancora un po 'confuso sui vantaggi di questo approccio rispetto a staticmethod. Se lo sei anche tu, probabilmente non importa.


11
Sto solo imparando Python, ma i vantaggi di @classmethodoltre @staticmethodAFAIK è che ottieni sempre il nome della classe su cui è stato invocato il metodo, anche se è una sottoclasse. Un metodo statico non contiene queste informazioni, quindi non può chiamare un metodo ignorato, ad esempio.
Seb

49
@theJollySin il modo pitonico per le costanti è di non far crescere una classe per le costanti. Basta avere un po ' const.pycon PI = 3.14ed è possibile importare ovunque. from const import PI
Giszmo,

30
È probabile che questa risposta confonda il problema della variabile statica. Innanzitutto, noni = 3 è una variabile statica, è un attributo di classe e poiché è distinto da un attributo a livello di istanza , non si comporta come una variabile statica in altre lingue. Vedi la risposta di millerdev , la risposta di Yann , e la mia risposta qui di seguito. i
Rick supporta Monica il

2
quindi solo una copia di i(variabile statica) sarà in memoria anche se creo centinaia di istanze di questa classe?
sdream,

2
Per chiunque sia interessato a chi è stato citato Daniel nel commento di @Dubslow, è millerdev ( macchina del passato )
HeyJude,

619

@Blair Conrad ha detto che le variabili statiche dichiarate all'interno della definizione di classe, ma non all'interno di un metodo sono variabili di classe o "statiche":

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

Ci sono alcuni gotcha qui. Proseguendo dall'esempio sopra:

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

Notare come la variabile di istanza t.inon è stata sincronizzata con la variabile di classe "statica" quando l'attributo è istato impostato direttamente t. Questo perché è istato rilegato all'interno dello tspazio dei nomi, che è distinto daTest spazio nomi. Se si desidera modificare il valore di una variabile "statica", è necessario modificarlo nell'ambito (o oggetto) in cui è stato originariamente definito. Ho messo "statico" tra virgolette perché Python non ha davvero variabili statiche nel senso che fanno C ++ e Java.

Anche se non dice nulla di specifico su variabili o metodi statici, il tutorial di Python ha alcune informazioni rilevanti su classi e oggetti di classe .

@Steve Johnson ha anche risposto in merito ai metodi statici, documentato anche in "Funzioni integrate" nel Python Library Reference.

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

@beid ha anche menzionato il metodo di classe, che è simile al metodo statico. Il primo argomento di un classmethod è l'oggetto class. Esempio:

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

Rappresentazione pittorica dell'esempio sopra


3
Ti suggerisco di estendere un po 'l'esempio: se, dopo aver impostato Test.i = 6, hai un'istanza di un nuovo oggetto (ad es. U = Test ()), il nuovo oggetto "erediterà" il nuovo valore della classe (ad es. ui == 6)
Segna il

2
Un modo per mantenere le variabili statiche in sincronia è quello di rendere loro proprietà: class Test(object):, _i = 3, @property, def i(self), return type(self)._i, @i.setter, def i(self,val):, type(self)._i = val. Ora si può fare x = Test(), x.i = 12, assert x.i == Test.i.
Rick supporta Monica il

1
Quindi potrei dire che tutte le variabili sono inizialmente statiche e quindi l'accesso alle istanze rende variabili di istanza in fase di esecuzione?
Ali,

Forse questo è interessante: se si definisce un metodo in Test che modifica Test.i, ciò influenzerà ENTRAMBI i valori Test.i e ti.
Pablo

@millerdev, come hai già detto, Python non ha variabili statiche come C ++ o JAVA .. Quindi, va bene dire che Test.i è più una variabile di classe che una variabile statica?
Tyto,

197

Metodi statici e di classe

Come hanno notato le altre risposte, i metodi statici e di classe sono facilmente realizzabili utilizzando i decoratori integrati:

class Test(object):

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

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

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

Come al solito, il primo argomento a MyMethod()è associato all'oggetto istanza della classe. Al contrario, il primo argomento a MyClassMethod()è associato all'oggetto class stesso (ad esempio, in questo caso, Test). Perché MyStaticMethod()nessuno degli argomenti è vincolato e avere argomenti a tutti è facoltativo.

"Variabili statiche"

Tuttavia, l'implementazione di "variabili statiche" (beh, variabili statiche mutabili , comunque, se non è una contraddizione in termini ...) non è così semplice. Come ha sottolineato Millerdev nella sua risposta , il problema è che gli attributi di classe di Python non sono veramente "variabili statiche". Prendere in considerazione:

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

Questo perché la linea x.i = 12ha aggiunto un nuovo attributo un'istanza idi xinvece di cambiare il valore della Testclasse iattributo.

Il comportamento parziale delle variabili statiche attese, ovvero la sincronizzazione dell'attributo tra più istanze (ma non con la classe stessa; vedere "gotcha" di seguito), può essere ottenuto trasformando l'attributo di classe in una proprietà:

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)

Ora puoi fare:

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

La variabile statica rimarrà ora sincronizzata tra tutte le istanze di classe .

(NOTA: Cioè, a meno che un'istanza di classe non decida di definire la propria versione di _i! Ma se qualcuno decide di farlo, si meritano ciò che ottengono, no ???)

Si noti che tecnicamente parlando, non iè ancora affatto una "variabile statica"; è un property, che è un tipo speciale di descrittore. Tuttavia, il propertycomportamento è ora equivalente a una variabile statica (mutabile) sincronizzata su tutte le istanze di classe.

"Variabili statiche" immutabili

Per un comportamento immutabile delle variabili statiche, è sufficiente omettere il 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)

Ora il tentativo di impostare l' iattributo dell'istanza restituirà un AttributeError:

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

Un Gotcha di cui essere consapevoli

Nota che i metodi sopra funzionano solo con istanze della tua classe - non funzioneranno quando si usa la classe stessa . Quindi per esempio:

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'

La riga assert Test.i == x.iproduce un errore, poiché l' iattributo di Teste xsono due oggetti diversi.

Molte persone lo troveranno sorprendente. Tuttavia, non dovrebbe essere. Se torniamo indietro e ispezioniamo la nostra Testdefinizione di classe (la seconda versione), prendiamo nota di questa riga:

    i = property(get_i) 

Chiaramente, il membro idi Testdeve essere un propertyoggetto, che è il tipo di oggetto restituito dalla propertyfunzione.

Se trovi quanto sopra confuso, molto probabilmente ci stai ancora pensando dal punto di vista di altri linguaggi (ad esempio Java o c ++). Dovresti andare a studiare l' propertyoggetto, riguardo all'ordine in cui vengono restituiti gli attributi Python, il protocollo descrittore e l'ordine di risoluzione del metodo (MRO).

Presento una soluzione al 'gotcha' sopra riportato di seguito; tuttavia suggerirei - strenuamente - di non provare a fare qualcosa di simile fino a quando - almeno - capirai bene perché assert Test.i = x.icausa un errore.

Variabili statiche REALI, ATTUALI -Test.i == x.i

Vi presento la soluzione (Python 3) di seguito solo a scopo informativo. Non lo sto sostenendo come una "buona soluzione". Ho i miei dubbi sul fatto che emulare il comportamento delle variabili statiche di altre lingue in Python sia mai effettivamente necessario. Tuttavia, indipendentemente dal fatto che sia effettivamente utile, il seguito dovrebbe aiutare a comprendere meglio come funziona Python.

AGGIORNAMENTO: questo tentativo è davvero orribile ; se insisti nel fare qualcosa del genere (suggerimento: per favore, non farlo; Python è un linguaggio molto elegante e calpestare le scarpe nel comportarsi come se non fosse necessario un altro linguaggio), usa invece il codice nella risposta di Ethan Furman .

Emulazione del comportamento delle variabili statiche di altre lingue mediante una metaclasse

Una metaclasse è la classe di una classe. La metaclasse predefinita per tutte le classi in Python (ovvero, le classi "new style" post Python 2.3 credo) sia type. Per esempio:

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

Tuttavia, puoi definire la tua metaclasse in questo modo:

class MyMeta(type): pass

E applicalo alla tua classe in questo modo (solo Python 3):

class MyClass(metaclass = MyMeta):
    pass

type(MyClass)  # class MyMeta

Di seguito è stata creata una metaclasse che tenta di emulare il comportamento di "variabili statiche" di altre lingue. Funziona sostanzialmente sostituendo getter, setter e deleter predefiniti con versioni che controllano se l'attributo richiesto è una "variabile statica".

Nell'attributo è memorizzato un catalogo delle "variabili statiche" StaticVarMeta.statics. Inizialmente si tenta di risolvere tutte le richieste di attributi utilizzando un ordine di risoluzione sostitutivo. Ho soprannominato questo "ordine di risoluzione statica", o "SRO". Questo viene fatto cercando l'attributo richiesto nel set di "variabili statiche" per una data classe (o le sue classi principali). Se l'attributo non viene visualizzato in "SRO", la classe ricadrà sul comportamento get / set / delete dell'attributo predefinito (ovvero "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

Ho provato ad usare la tua strada, ma ho dovuto affrontare un problema, gentilmente dare un'occhiata alla mia domanda qui stackoverflow.com/questions/29329850/get-static-variable-value
Muhammed Refaat

@RickTeachey: Immagino che dovresti generalmente vedere qualsiasi cosa tu faccia sull'istanza di classe Test(prima di usarla per istanze di istanze) come nel dominio della meta-programmazione? Ad esempio, si modifica il comportamento in classe facendo Test.i = 0(qui semplicemente si distrugge completamente l'oggetto proprietà). Immagino che il "meccanismo di proprietà" entri in azione solo sull'accesso alla proprietà nelle istanze di una classe (a meno che tu non cambi il comportamento sottostante usando una meta-classe come intermedio, forse). A proposito, per favore, completa questa risposta :-)
Ole Thomsen Buus,

1
@RickTeachey Grazie :-) La tua metaclasse alla fine è interessante ma in realtà è un po 'troppo complessa per i miei gusti. Potrebbe essere utile in un ampio framework / applicazione in cui questo meccanismo è assolutamente necessario. Ad ogni modo, questo esemplifica che se è veramente necessario un nuovo (complesso) meta-comportamento non predefinito, Python lo rende possibile :)
Ole Thomsen Buus

1
@OleThomsenBuus: controlla la mia risposta per una metaclasse più semplice che fa il lavoro.
Ethan Furman,

1
@taper Hai ragione; Ho modificato la risposta per risolvere il problema (non riesco a credere che sia stato seduto lì sbagliato per così tanto tempo!). Dispiace per la confusione.
Rick supporta Monica l'

33

Puoi anche aggiungere variabili di classe alle classi al volo

>>> 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 le istanze di classe possono cambiare le variabili di classe

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

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

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

3
Le nuove variabili di classe si attaccheranno anche se la classe viene importata in un altro modulo?
zakdances

Sì. Le classi sono effettivamente singoli, indipendentemente dallo spazio dei nomi da cui li chiami.
Pedro,

@Gregory hai detto "E le istanze di classe possono cambiare le variabili di classe" In realtà questo esempio si chiama accesso non modifica. La modifica è stata effettuata dall'oggetto stesso tramite la sua funzione append ().
Amr ALHOSSARY,

19

Personalmente userei un metodo di classe ogni volta che mi serviva un metodo statico. Principalmente perché ottengo la lezione come argomento.

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

o usa un decoratore

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

Per le proprietà statiche .. È ora che cerchi qualche definizione di Python .. La variabile può sempre cambiare. Esistono due tipi di essi mutabili e immutabili. Inoltre, ci sono attributi di classe e attributi di istanza. Niente di simile agli attributi statici nel senso di Java & C ++

Perché usare il metodo statico in senso pitonico, se non ha alcuna relazione con la classe! Se fossi in te, userei il metodo di classe o definirei il metodo indipendente dalla classe.


1
Le variabili non sono mutabili o immutabili; gli oggetti sono. (Tuttavia, un oggetto può, con vari gradi di successo, tentare di impedire l'assegnazione ad alcuni dei suoi attributi.)
Davis Herring,

Java e C ++ usano static (cattivo uso della parola, imho) esattamente come si usa l'istanza rispetto all'attributo class. Un attributo / metodo di classe è statico in Java e C ++, nessuna differenza, tranne che in Python il primo parametro di una chiamata al metodo di classe è la classe.
Angel O'Sphere,

16

Una cosa speciale da notare sulle proprietà statiche e le proprietà dell'istanza, mostrate nell'esempio seguente:

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

Ciò significa che prima di assegnare il valore alla proprietà dell'istanza, se proviamo ad accedere alla proprietà tramite l'istanza, viene utilizzato il valore statico. Ogni proprietà dichiarata nella classe python ha sempre uno slot statico in memoria .


16

I metodi statici in Python sono chiamati classmethod s. Dai un'occhiata al seguente codice

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

Si noti che quando chiamiamo il metodo myInstanceMethod , viene visualizzato un errore. Questo perché richiede che il metodo sia chiamato su un'istanza di questa classe. Il metodo myStaticMethod è impostato come metodo di classe usando il decoratore @classmethod .

Solo per calci e risatine, potremmo chiamare myInstanceMethod sulla classe passando un'istanza della classe, in questo modo:

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

2
Umm ... i metodi statici sono fatti con @staticmethod; @classmethodè (ovviamente) per i metodi di classe (che sono principalmente destinati all'uso come costruttori alternativi, ma possono servire in un pizzico come metodi statici che ricevono un riferimento alla classe da cui sono stati chiamati).
ShadowRanger

11

Quando si definisce una variabile membro al di fuori di qualsiasi metodo membro, la variabile può essere statica o non statica a seconda di come viene espressa la variabile.

  • CLASSNAME.var è una variabile statica
  • INSTANCENAME.var non è una variabile statica.
  • La classe interna self.var non è una variabile statica.
  • var all'interno della funzione membro della classe non è definita.

Per esempio:

#!/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()

I risultati sono

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

Il rientro è rotto. Questo non verrà eseguito
Thomas Weller il

9

È possibile avere staticvariabili di classe, ma probabilmente non ne vale la pena.

Ecco una prova di concetto scritta in Python 3 - se uno qualsiasi dei dettagli esatti è errato, il codice può essere modificato per corrispondere a qualsiasi cosa tu intenda con un 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 in 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 alcuni test:

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

È inoltre possibile imporre che una classe sia statica utilizzando la 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

Quindi ogni volta che per caso provi a inizializzare MyClass otterrai uno StaticClassError.


4
Perché è persino una lezione se non hai intenzione di istanziarla? È come torcere Python per trasformarlo in Java ....
Ned Batchelder il

1
Il linguaggio Borg è un modo migliore per gestirlo.
Rick supporta Monica il

@NedBatchelder È una classe astratta, destinata solo alla sottoclasse (e all'istanza delle sottoclassi)
stevepastelan,

1
Spero che le sottoclassi non usino super () per invocare il nome __new__dei suoi genitori ...
Ned Batchelder

7

Un punto molto interessante sulla ricerca degli attributi di Python è che può essere usato per creare " variabili virtuali ":

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 non ci sono assegnazioni a questi dopo che sono stati creati. Si noti che la ricerca utilizza selfperché, sebbene labelsia statico nel senso di non essere associato a una particolare istanza, il valore dipende ancora dalla (classe dell'istanza).


6

Per quanto riguarda questa risposta , per una variabile statica costante , è possibile utilizzare un descrittore. Ecco un esempio:

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

con il risultato di ...

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

Puoi sempre sollevare un'eccezione se ignorare tranquillamente il valore di impostazione ( passsopra) non fa per te. Se stai cercando una variabile di classe statica C ++, in stile 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

Dai un'occhiata a questa risposta e alla documentazione ufficiale HOWTO per ulteriori informazioni sui descrittori.


2
Puoi anche semplicemente usare @property, che è lo stesso che usare un descrittore, ma è molto meno codice.
Rick supporta Monica il

6

Assolutamente sì, Python da solo non ha esplicitamente alcun membro di dati statici, ma possiamo farlo

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())

produzione

0
0
1
1

spiegazione

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

6

Sì, sicuramente è possibile scrivere variabili e metodi statici in Python.

Variabili statiche: le variabili dichiarate a livello di classe sono chiamate variabili statiche a cui è possibile accedere direttamente utilizzando il nome della classe.

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

    >>> print(A.my_var)
        shagun

Variabili di istanza: le variabili che sono correlate e accessibili per istanza di una classe sono variabili di istanza.

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

Metodi statici: simili alle variabili, è possibile accedere direttamente ai metodi statici utilizzando il nome classe. Non è necessario creare un'istanza.

Ma tieni presente che un metodo statico non può chiamare un metodo non statico in Python.

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

4

Per evitare qualsiasi potenziale confusione, vorrei contrastare le variabili statiche e gli oggetti immutabili.

Alcuni tipi di oggetti primitivi come numeri interi, float, stringhe e touple sono immutabili in Python. Ciò significa che l'oggetto a cui fa riferimento un determinato nome non può cambiare se appartiene a uno dei suddetti tipi di oggetto. Il nome può essere riassegnato a un altro oggetto, ma l'oggetto stesso non può essere modificato.

Rendere statica una variabile fa un ulteriore passo in avanti impedendo al nome della variabile di puntare a qualsiasi oggetto tranne quello a cui attualmente punta. (Nota: si tratta di un concetto software generale e non specifico di Python; consultare i post di altri per informazioni sull'implementazione della statica in Python).


4

Il modo migliore che ho trovato è usare un'altra classe. È possibile creare un oggetto e quindi utilizzarlo su altri oggetti.

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()

Con l'esempio sopra, ho creato una classe chiamata staticFlag.

Questa classe dovrebbe presentare la var statica __success(Var statico privato).

tryIt class rappresentava la classe normale che dobbiamo usare.

Ora ho creato un oggetto per una bandiera ( staticFlag). Questo flag verrà inviato come riferimento a tutti gli oggetti normali.

Tutti questi oggetti vengono aggiunti all'elenco tryArr.


Risultati dello script:

False
False
False
False
False
True
True
True
True
True

2

Variabili statiche in classe factory python3.6

Per chiunque utilizzi un factory di classe con python3.6 e versioni successive utilizzare la nonlocalparola chiave per aggiungerla all'ambito / contesto della classe creata in questo modo:

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

si, ma in questo caso lo hasattr(SomeClass, 'x')è False. dubito che questo sia ciò che chiunque intende per variabile statica.
Rick supporta Monica il

@RickTeachey lol, hai visto il tuo codice variabile statico, stackoverflow.com/a/27568860/2026508 +1 internet signore, e ho pensato che hasattr non funzionasse in quel modo? così è some_varimmutabile e staticamente definito, o no? Che cosa ha a che fare con l'accesso esterno al getter con una variabile statica o no? ho così tante domande ora. mi piacerebbe sentire alcune risposte quando hai tempo.
jmunsch,

Sì, quella metaclasse è piuttosto ridicola. Non sono sicuro di aver capito le domande, ma a mio avviso, some_varsopra non è affatto un membro della classe. In Python è possibile accedere a tutti i membri della classe dall'esterno della classe.
Rick supporta Monica il

Il nonlocalkeywoard "sconvolge" l'ambito della variabile. L'ambito di una definizione del corpo di una classe è indipendente dall'ambito in cui si trova quando si dice nonlocal some_var, ovvero la creazione di un riferimento non locale (leggi: NON nell'ambito della definizione di classe) a un altro oggetto denominato. Pertanto non viene associato alla definizione della classe perché non rientra nell'ambito del corpo della classe.
Rick supporta Monica il

1

Quindi questo è probabilmente un trucco, ma sto usando eval(str) per ottenere un oggetto statico, una specie di contraddizione, in Python 3.

Esiste un file Records.py che non ha altro che classoggetti definiti con metodi e costruttori statici che salvano alcuni argomenti. Quindi da un altro file .py, import Recordsma devo selezionare dinamicamente ciascun oggetto e quindi istanziarlo su richiesta in base al tipo di dati letti.

Quindi, dove object_name = 'RecordOne'o il nome della classe, chiamo cur_type = eval(object_name)e quindi per istanziarlo, cur_inst = cur_type(args) tuttavia. Prima di creare un'istanza, è possibile chiamare metodi statici, cur_type.getName()ad esempio, come l'implementazione di una classe base astratta o qualunque sia l'obiettivo. Comunque nel backend, è probabilmente istanziato in Python e non è veramente statico, perché eval sta restituendo un oggetto .... che deve essere stato istanziato .... che dà un comportamento simile a quello statico.


0

È possibile utilizzare un elenco o un dizionario per ottenere un "comportamento statico" tra le istanze.

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 stai tentando di condividere una variabile statica per, ad esempio, aumentandola in altre istanze, qualcosa come questo script funziona bene:

# -*- 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)
Utilizzando il nostro sito, riconosci di aver letto e compreso le nostre Informativa sui cookie e Informativa sulla privacy.
Licensed under cc by-sa 3.0 with attribution required.