È possibile avere variabili di classe statiche o metodi in Python? Quale sintassi è necessaria per fare questo?
È possibile avere variabili di classe statiche o metodi in Python? Quale sintassi è necessaria per fare questo?
Risposte:
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 i
variabile a livello di classe , ma questa è distinta da qualsiasi i
variabile 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.
const.py
con PI = 3.14
ed è possibile importare ovunque. from const import PI
i = 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
i
(variabile statica) sarà in memoria anche se creo centinaia di istanze di questa classe?
@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.i
non è stata sincronizzata con la variabile di classe "statica" quando l'attributo è i
stato impostato direttamente t
. Questo perché è i
stato rilegato all'interno dello t
spazio 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
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
.
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.
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 = 12
ha aggiunto un nuovo attributo un'istanza i
di x
invece di cambiare il valore della Test
classe i
attributo.
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 property
comportamento è ora equivalente a una variabile statica (mutabile) sincronizzata su tutte le istanze di classe.
Per un comportamento immutabile delle variabili statiche, è sufficiente omettere il property
setter:
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' i
attributo dell'istanza restituirà un AttributeError
:
x = Test()
assert x.i == 3 # success
x.i = 12 # ERROR
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.i
produce un errore, poiché l' i
attributo di Test
e x
sono due oggetti diversi.
Molte persone lo troveranno sorprendente. Tuttavia, non dovrebbe essere. Se torniamo indietro e ispezioniamo la nostra Test
definizione di classe (la seconda versione), prendiamo nota di questa riga:
i = property(get_i)
Chiaramente, il membro i
di Test
deve essere un property
oggetto, che è il tipo di oggetto restituito dalla property
funzione.
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' property
oggetto, 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.i
causa un errore.
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
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 :-)
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]
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.
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 .
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
@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).
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.
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
È possibile avere static
variabili 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
È 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.
__new__
dei suoi genitori ...
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 self
perché, sebbene label
sia statico nel senso di non essere associato a una particolare istanza, il valore dipende ancora dalla (classe dell'istanza).
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 ( pass
sopra) 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.
@property
, che è lo stesso che usare un descrittore, ma è molto meno codice.
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"
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!!
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).
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
Per chiunque utilizzi un factory di classe con python3.6 e versioni successive utilizzare la nonlocal
parola 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
hasattr(SomeClass, 'x')
è False
. dubito che questo sia ciò che chiunque intende per variabile statica.
some_var
immutabile 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.
some_var
sopra non è affatto un membro della classe. In Python è possibile accedere a tutti i membri della classe dall'esterno della classe.
nonlocal
keywoard "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.
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 class
oggetti definiti con metodi e costruttori statici che salvano alcuni argomenti. Quindi da un altro file .py, import Records
ma 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.
È 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
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)
@classmethod
oltre@staticmethod
AFAIK è 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.