Czy w Pythonie można mieć zmienne lub metody klasy statycznej? Jaka składnia jest do tego wymagana?
Czy w Pythonie można mieć zmienne lub metody klasy statycznej? Jaka składnia jest do tego wymagana?
Odpowiedzi:
Zmienne zadeklarowane w definicji klasy, ale nie wewnątrz metody, są zmiennymi klasy lub statycznymi:
>>> class MyClass:
... i = 3
...
>>> MyClass.i
3
Jak wskazuje @ millerdev , tworzy to i
zmienną na poziomie klasy , ale różni się ona od dowolnej i
zmiennej na poziomie instancji , więc możesz mieć
>>> m = MyClass()
>>> m.i = 4
>>> MyClass.i, m.i
>>> (3, 4)
Różni się to od C ++ i Java, ale nie różni się tak bardzo od C #, gdzie do elementu statycznego nie można uzyskać dostępu za pomocą odwołania do instancji.
Zobacz, co ma do powiedzenia samouczek języka Python na temat klas i obiektów klas .
@Steve Johnson już odpowiedział na pytania dotyczące metod statycznych , udokumentowane również w części „Funkcje wbudowane” w dokumentacji Python Library Reference .
class C:
@staticmethod
def f(arg1, arg2, ...): ...
@beidy zaleca classmethod s zamiast staticmethod, ponieważ metoda następnie odbiera typ klasy jako pierwszy argument, ale nadal jestem trochę rozmyślany nad zaletami tego podejścia w stosunku do staticmethod. Jeśli ty też, to prawdopodobnie nie ma znaczenia.
const.py
z PI = 3.14
i można go importować wszędzie. from const import PI
i = 3
jest nie zmienna statyczna, jest atrybutem klasy, a ponieważ różni się od atrybutu instancji poziomie i
to robi nie zachowywać się jak zmiennej statycznej w innych językach. Zobacz odpowiedź millerdev jest , odpowiedź Yann jest i moją odpowiedź poniżej.
i
(zmiennej statycznej) będzie w pamięci, nawet jeśli utworzę setki instancji tej klasy?
@Blair Conrad powiedział, że zmienne statyczne zadeklarowane w definicji klasy, ale nie w metodzie są zmiennymi klasy lub „statycznymi”:
>>> class Test(object):
... i = 3
...
>>> Test.i
3
Jest tu kilka gotcha. Kontynuując powyższy przykład:
>>> 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__
{}
Zauważ, jak zmienna instancji t.i
nie zsynchronizowała się ze zmienną klasy „statycznej”, gdy atrybut i
został ustawiony bezpośrednio na t
. Jest tak, ponieważ i
został ponownie związany w t
przestrzeni nazw, która różni się od Test
przestrzeni nazw. Jeśli chcesz zmienić wartość zmiennej „statycznej”, musisz ją zmienić w zakresie (lub obiekcie), w którym została pierwotnie zdefiniowana. W cudzysłowie wstawiam „static”, ponieważ Python tak naprawdę nie ma zmiennych statycznych w takim sensie, jak C ++ i Java.
Chociaż nie mówi nic konkretnego o zmiennych statycznych lub metodach, samouczek języka Python zawiera pewne istotne informacje na temat klas i obiektów klas .
@ Steve Johnson również odpowiedziała na temat metod statycznych, udokumentowanych również w części „Funkcje wbudowane” w dokumentacji Python Library Reference.
class Test(object):
@staticmethod
def f(arg1, arg2, ...):
...
@beid wspomniał także o classmethod, która jest podobna do staticmethod. Pierwszym argumentem metody metody jest obiekt klasy. Przykład:
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
. Teraz można zrobić x = Test()
, x.i = 12
, assert x.i == Test.i
.
Jak zauważyły inne odpowiedzi, metody statyczne i klasowe można łatwo osiągnąć za pomocą wbudowanych dekoratorów:
class Test(object):
# regular instance method:
def MyMethod(self):
pass
# class method:
@classmethod
def MyClassMethod(klass):
pass
# static method:
@staticmethod
def MyStaticMethod():
pass
Jak zwykle pierwszy argument do MyMethod()
jest powiązany z obiektem instancji klasy. Natomiast pierwszy argument związanyMyClassMethod()
jest z samym obiektem klasy (np. W tym przypadku Test
). Ponieważ MyStaticMethod()
żaden z argumentów nie jest związany, a posiadanie argumentów w ogóle jest opcjonalne.
Jednak implementacja „zmiennych statycznych” (cóż, zmiennych zmiennych statycznych, zresztą, jeśli nie jest to sprzeczność pod względem ...) nie jest tak prosta. Jak zauważył millerdev w swojej odpowiedzi , problem polega na tym, że atrybuty klas Pythona nie są tak naprawdę „zmiennymi statycznymi”. Rozważać:
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
To dlatego, że linia x.i = 12
został dodany nowy atrybut instancji i
aby x
zamiast zmieniać wartość Test
klasy i
atrybutu.
Częściowy oczekiwane zachowanie zmiennej statycznej, tj. Synchronizacja atrybutu między wieloma instancjami (ale nie z samą klasą; patrz „gotcha” poniżej), można osiągnąć, zmieniając atrybut klasy we właściwość:
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)
Teraz możesz zrobić:
x1 = Test()
x2 = Test()
x1.i = 50
assert x2.i == x1.i # no error
assert x2.i == 50 # the property is synced
Zmienna statyczna będzie teraz synchronizowana między wszystkimi instancjami klas .
(UWAGA: To znaczy, chyba że instancja klasy postanowi zdefiniować własną wersję _i
! Ale jeśli ktoś zdecyduje się TO zrobić, zasługuje na to, co dostaje, prawda?)
Należy zauważyć, że technicznie rzecz biorąc, i
nadal nie jest wcale „zmienną statyczną”; to jestproperty
, co jest szczególnym rodzajem deskryptora. Jednak property
zachowanie jest teraz równoważne (zmiennej) zmiennej statycznej zsynchronizowanej we wszystkich instancjach klasy.
Aby zachować niezmienne zachowanie zmiennej statycznej, po prostu pomiń 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)
Teraz próba ustawienia i
atrybutu instancji zwróci AttributeError
:
x = Test()
assert x.i == 3 # success
x.i = 12 # ERROR
Należy zauważyć, że powyższe metody pracy jedynie z wystąpień w klasie - będą nie pracy przy użyciu samej klasy . Na przykład:
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'
Linia assert Test.i == x.i
generuje błąd, ponieważ i
atrybut Test
i x
są dwoma różnymi obiektami.
Wiele osób uzna to za zaskakujące. Jednak nie powinno tak być. Jeśli wrócimy i sprawdzimy Test
definicję naszej klasy (druga wersja), zauważymy następujący wiersz:
i = property(get_i)
Oczywiste jest, że element i
z Test
musi być property
obiekt, który jest typ obiektu zwracane z property
funkcji.
Jeśli powyższe stwierdzenie jest mylące, najprawdopodobniej nadal myślisz o tym z perspektywy innych języków (np. Java lub c ++). Powinieneś studiowaćproperty
obiekt, na temat kolejności zwracania atrybutów Python, protokołu deskryptora i kolejności rozwiązywania metod (MRO).
Poniżej przedstawiam rozwiązanie powyższej „gotcha”; sugerowałbym jednak - stanowczo - że nie próbujesz robić czegoś takiego, dopóki - przynajmniej - nie do końca zrozumiesz, dlaczegoassert Test.i = x.i
powoduje błąd.
Test.i == x.i
Przedstawiam poniższe rozwiązanie (Python 3) wyłącznie w celach informacyjnych. Nie popieram tego jako „dobrego rozwiązania”. Mam wątpliwości, czy emulowanie zachowań zmiennych statycznych innych języków w Pythonie jest rzeczywiście konieczne. Jednak niezależnie od tego, czy jest to rzeczywiście przydatne, poniższe informacje powinny pomóc lepiej zrozumieć, jak działa Python.
AKTUALIZACJA: ta próba jest naprawdę okropna ; jeśli nalegasz na zrobienie czegoś takiego (podpowiedź: nie rób; Python jest bardzo eleganckim językiem i zmuszanie go do zachowywania się tak, jakby inny język nie był po prostu konieczny), zamiast tego użyj kodu w odpowiedzi Ethana Furmana .
Emulacja zachowania zmiennych statycznych innych języków przy użyciu metaklasy
Metaklasa jest klasą klasy. Domyślna metaklasa dla wszystkich klas w Pythonie (to znaczy klasy „nowego stylu” po Pythonie 2.3) type
. Na przykład:
type(int) # class 'type'
type(str) # class 'type'
class Test(): pass
type(Test) # class 'type'
Możesz jednak zdefiniować własną metaklasę w następujący sposób:
class MyMeta(type): pass
I zastosuj go do własnej klasy w ten sposób (tylko Python 3):
class MyClass(metaclass = MyMeta):
pass
type(MyClass) # class MyMeta
Poniżej utworzyłem metaklasę, która próbuje naśladować zachowanie „zmiennych statycznych” innych języków. Zasadniczo działa, zastępując domyślny moduł pobierający, ustawiający i usuwający wersjami, które sprawdzają, czy żądany atrybut jest „zmienną statyczną”.
Katalog „zmiennych statycznych” jest przechowywany w StaticVarMeta.statics
atrybucie. Wszystkie żądania atrybutów są początkowo próbowane do rozwiązania przy użyciu zastępczej kolejności rozstrzygania. Nazwałem to „statycznym porządkiem rozdzielczości” lub „SRO”. Odbywa się to poprzez wyszukiwanie żądanego atrybutu w zbiorze „zmiennych statycznych” dla danej klasy (lub jej klas nadrzędnych). Jeśli atrybut nie pojawi się w „SRO”, klasa powróci do domyślnego zachowania get / set / delete atrybutu (tj. „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
(przed użyciem jej do tworzenia instancji), jako należące do dziedziny metaprogramowania? Na przykład zmieniasz zachowanie klasy, robiąc Test.i = 0
(tutaj po prostu całkowicie niszczysz obiekt własności). Wydaje mi się, że „mechanizm własności” włącza się tylko w przypadku dostępu do właściwości w instancjach klasy (chyba że zmienisz podstawowe zachowanie za pomocą meta-klasy jako pośredniej). Przy okazji, proszę zakończyć tę odpowiedź :-)
Możesz także dodawać zmienne klas do klas w locie
>>> 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
Instancje klas mogą zmieniać zmienne klas
class X:
l = []
def __init__(self):
self.l.append(1)
print X().l
print X().l
>python test.py
[1]
[1, 1]
Osobiście używałbym metody klasowej, ilekroć potrzebowałem metody statycznej. Głównie dlatego, że dostaję klasę jako argument.
class myObj(object):
def myMethod(cls)
...
myMethod = classmethod(myMethod)
lub użyj dekoratora
class myObj(object):
@classmethod
def myMethod(cls)
W przypadku właściwości statycznych. Czas poszukać definicji w języku Python. Zmienna zawsze może się zmienić. Istnieją dwa rodzaje mutowalnych i niezmiennych. Ponadto istnieją atrybuty klas i instancji. Nic tak naprawdę jak atrybuty statyczne w sensie java & c ++
Po co używać metody statycznej w sensie pytonicznym, jeśli nie ma ona żadnego związku z klasą! Gdybym był tobą, użyłbym metody klasy lub zdefiniowałbym metodę niezależną od klasy.
Jedną szczególną rzeczą do zapamiętania na temat właściwości statycznych i właściwości instancji, pokazanych w poniższym przykładzie:
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
Oznacza to, że przed przypisaniem wartości do właściwości wystąpienia, jeśli spróbujemy uzyskać dostęp do właściwości przez wystąpienie, zostanie użyta wartość statyczna. Każda właściwość zadeklarowana w klasie python zawsze ma stałe miejsce w pamięci .
Metody statyczne w pythonie są nazywane metodami klasy . Spójrz na następujący kod
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
Zauważ, że kiedy wywołujemy metodę myInstanceMethod , pojawia się błąd. Jest tak, ponieważ wymaga wywołania tej metody na instancji tej klasy. Metodę myStaticMethod ustawia się jako metodę klasy za pomocą dekoratora @classmethod .
Tylko dla kopnięć i chichotów moglibyśmy wywołać myInstanceMethod w klasie, przekazując instancję klasy, tak jak poniżej :
>>> MyClass.myInstanceMethod(MyClass())
output from an instance method
@staticmethod
; @classmethod
jest (oczywiście) dla metod klasowych (które są przede wszystkim przeznaczone do stosowania jako alternatywne konstruktory, ale mogą służyć jako szczypta jako metody statyczne, które otrzymują referencję do klasy, przez którą zostały wywołane).
Gdy zdefiniujesz jakąś zmienną składową poza jakąkolwiek metodą składową, zmienna może być statyczna lub niestatyczna, w zależności od sposobu wyrażenia zmiennej.
Na przykład:
#!/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()
Wyniki są
self.var is 2
A.var is 1
self.var is 2
A.var is 3
Możliwe są static
zmienne klasowe, ale prawdopodobnie nie warte wysiłku.
Oto proof-of-concept napisany w Pythonie 3 - jeśli którykolwiek z dokładnych szczegółów jest nieprawidłowy, kod można dostosować tak, aby pasował do dowolnego znaczenia 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)
i w użyciu:
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')
i niektóre testy:
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
Można również wymusić statyczność klasy za pomocą metaklasy.
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
Za każdym razem, gdy przypadkiem spróbujesz zainicjować MyClass , otrzymasz błąd StaticClassError.
__new__
swoich rodziców ...
Jednym z bardzo interesujących punktów dotyczących wyszukiwania atrybutów Pythona jest to, że można go używać do tworzenia „ zmiennych wirtualnych ”:
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!
Zwykle nie ma żadnych przypisań do nich po ich utworzeniu. Zauważ, że wyszukiwanie korzysta, self
ponieważ chociaż label
jest statyczne w tym sensie, że nie jest powiązane z konkretną instancją, wartość nadal zależy od (klasy) instancji.
W odniesieniu do tej odpowiedzi , dla stałej zmiennej statycznej można użyć deskryptora. Oto przykład:
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
w wyniku czego ...
small demo 10
small subdemo 100
big demo 10
big subdemo 10
Zawsze możesz zgłosić wyjątek, jeśli cicho ignorowanie wartości ustawienia ( pass
powyżej) nie jest Twoją sprawą. Jeśli szukasz zmiennej klasy statycznej w języku 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
Spójrz na tę odpowiedź i oficjalne dokumenty HOWTO, aby uzyskać więcej informacji na temat deskryptorów.
@property
, co jest tym samym, co użycie deskryptora, ale jest to o wiele mniej kodu.
Absolutnie tak, Python sam w sobie nie ma wyraźnie żadnego statycznego elementu danych, ale możemy to zrobić
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())
wynik
0
0
1
1
wyjaśnienie
here object (x) alone increment the counter variable
from 0 to 1 by not object y. But result it as "static counter"
Tak, zdecydowanie możliwe jest pisanie zmiennych statycznych i metod w Pythonie.
Zmienne statyczne: Zmienne zadeklarowane na poziomie klasy nazywane są zmiennymi statycznymi, do których można uzyskać bezpośredni dostęp za pomocą nazwy klasy.
>>> class A:
...my_var = "shagun"
>>> print(A.my_var)
shagun
Zmienne instancji: Zmienne, które są powiązane i dostępne przez instancję klasy, są zmiennymi instancji.
>>> a = A()
>>> a.my_var = "pruthi"
>>> print(A.my_var,a.my_var)
shagun pruthi
Metody statyczne: Podobnie jak zmienne, do metod statycznych można uzyskać bezpośredni dostęp za pomocą nazwy klasy. Nie ma potrzeby tworzenia instancji.
Należy jednak pamiętać, że metoda statyczna nie może wywoływać metody niestatycznej w Pythonie.
>>> class A:
... @staticmethod
... def my_static_method():
... print("Yippey!!")
...
>>> A.my_static_method()
Yippey!!
Aby uniknąć potencjalnych nieporozumień, chciałbym skontrastować zmienne statyczne i niezmienne obiekty.
Niektóre prymitywne typy obiektów, takie jak liczby całkowite, zmiennoprzecinkowe, łańcuchy i zmienne są niezmienne w Pythonie. Oznacza to, że obiekt, do którego odnosi się dana nazwa, nie może się zmienić, jeśli należy do jednego z wyżej wymienionych typów obiektów. Nazwę można przypisać do innego obiektu, ale sam obiekt nie może zostać zmieniony.
Uczynienie zmiennej statyczną posuwa się o krok dalej, uniemożliwiając nazwę zmiennej, aby wskazywała na dowolny obiekt oprócz tego, na który obecnie wskazuje. (Uwaga: jest to ogólna koncepcja oprogramowania i nie jest specyficzna dla Pythona; zobacz posty innych osób, aby uzyskać informacje na temat implementacji statyki w Pythonie).
Najlepszym sposobem, jaki znalazłem, jest użycie innej klasy. Możesz utworzyć obiekt, a następnie użyć go na innych obiektach.
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()
Na powyższym przykładzie stworzyłem klasę o nazwie staticFlag
.
Ta klasa powinna prezentować statyczny var __success
(Private Static Var).
tryIt
klasa reprezentowała zwykłą klasę, której musimy użyć.
Teraz zrobiłem obiekt dla jednej flagi (staticFlag
). Ta flaga zostanie wysłana jako odniesienie do wszystkich zwykłych obiektów.
Wszystkie te obiekty są dodawane do listy tryArr
.
Wyniki tego skryptu:
False
False
False
False
False
True
True
True
True
True
Dla każdego, kto korzysta z fabryki klas z python3.6 i nowszymi , użyj nonlocal
słowa kluczowego, aby dodać go do zakresu / kontekstu tworzonej klasy w następujący sposób:
>>> 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')
jest False
. Wątpię, czy to w ogóle ktoś rozumie przez zmienną statyczną.
some_var
niezmienny i statycznie zdefiniowany, czy nie? Co zewnętrzny dostęp gettera ma wspólnego z tym, że zmienna jest statyczna, czy nie? mam teraz tyle pytań. bardzo chciałbym usłyszeć odpowiedzi, kiedy będziesz miał czas.
some_var
powyżej nie jest wcale członkiem klasy. W Pythonie dostęp do wszystkich członków klasy można uzyskać spoza klasy.
nonlocal
Keywoard „wpada” zakres zmiennej. Zakres definicji treści klasy jest niezależny od zakresu, w którym się znajduje, kiedy mówisz nonlocal some_var
, że tworzy po prostu nielokalne (czytaj: NIE w zakresie definicji definicji klasy) odwołanie do innego nazwanego obiektu. Dlatego nie jest dołączany do definicji klasy, ponieważ nie znajduje się w zakresie treści klasy.
Prawdopodobnie jest to włamanie, ale używałem eval(str)
do uzyskania statycznego obiektu, swego rodzaju sprzeczności, w Pythonie 3.
Istnieje plik Records.py, który zawiera wyłącznie class
obiekty zdefiniowane za pomocą metod statycznych i konstruktorów, które zapisują niektóre argumenty. Następnie z innego pliku .py import Records
muszę dynamicznie wybrać każdy obiekt, a następnie utworzyć go na żądanie zgodnie z typem wczytywanych danych.
Więc gdzie object_name = 'RecordOne'
lub nazwę klasy, wzywam, cur_type = eval(object_name)
a następnie w celu jej utworzenia. Robisz to. cur_inst = cur_type(args)
Jednak zanim utworzysz instancję, możesz wywołać metody statyczne z, cur_type.getName()
na przykład, czegoś w rodzaju abstrakcyjnej implementacji klasy bazowej lub innego celu. Jednak w backendie prawdopodobnie jest on tworzony w Pythonie i nie jest naprawdę statyczny, ponieważ eval zwraca obiekt .... który musiał zostać utworzony ... który daje statyczne zachowanie.
Za pomocą listy lub słownika można uzyskać „zachowanie statyczne” między instancjami.
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
Jeśli próbujesz udostępnić zmienną statyczną, na przykład zwiększając ją w innych instancjach, coś takiego jak ten skrypt działa dobrze:
# -*- 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
nad@staticmethod
AFAIK jest to, że zawsze dostajesz nazwę klasy, na której wywołano metodę, nawet jeśli jest to podklasa. Metoda statyczna nie ma tej informacji, więc nie może na przykład wywołać metody przesłoniętej.