Ist es möglich, statische Klassenvariablen oder Methoden in Python zu haben? Welche Syntax ist dazu erforderlich?
Ist es möglich, statische Klassenvariablen oder Methoden in Python zu haben? Welche Syntax ist dazu erforderlich?
Antworten:
Variablen, die innerhalb der Klassendefinition, jedoch nicht innerhalb einer Methode deklariert sind, sind Klassen- oder statische Variablen:
>>> class MyClass:
... i = 3
...
>>> MyClass.i
3
Wie @ millerdev hervorhebt , wird dadurch eine i
Variable auf Klassenebene erstellt , die sich jedoch von allen i
Variablen auf Instanzebene unterscheidet
>>> m = MyClass()
>>> m.i = 4
>>> MyClass.i, m.i
>>> (3, 4)
Dies unterscheidet sich von C ++ und Java, jedoch nicht von C #, bei dem auf ein statisches Mitglied nicht über einen Verweis auf eine Instanz zugegriffen werden kann.
Lesen Sie, was das Python-Tutorial zum Thema Klassen und Klassenobjekte zu sagen hat .
@Steve Johnson hat bereits zu statischen Methoden geantwortet , die auch unter "Integrierte Funktionen" in der Python-Bibliotheksreferenz dokumentiert sind .
class C:
@staticmethod
def f(arg1, arg2, ...): ...
@beidy empfiehlt Klassenmethoden gegenüber statischen Methoden, da die Methode dann den Klassentyp als erstes Argument empfängt, aber ich bin immer noch ein wenig verwirrt über die Vorteile dieses Ansatzes gegenüber statischen Methoden. Wenn Sie auch sind, dann ist es wahrscheinlich egal.
const.py
mit PI = 3.14
und Sie können es überall importieren. from const import PI
i = 3
ist nicht eine statische Variable, sondern um ein Klassenattribut. Da es sich von einem Attribut auf Instanzebene i
unterscheidet , verhält es sich in anderen Sprachen nicht wie eine statische Variable. Siehe millerdev Antwort , Yann Antwort , und meine Antwort unten.
i
(statische Variable) im Speicher sein, selbst wenn ich Hunderte von Instanzen dieser Klasse erstelle?
@Blair Conrad sagte, dass statische Variablen, die in der Klassendefinition, aber nicht in einer Methode deklariert sind, Klassen- oder "statische" Variablen sind:
>>> class Test(object):
... i = 3
...
>>> Test.i
3
Hier gibt es ein paar Fallstricke. Fortsetzung des obigen Beispiels:
>>> 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__
{}
Beachten Sie, dass die Instanzvariable t.i
nicht mehr mit der "statischen" Klassenvariablen synchronisiert wurde, als das Attribut i
direkt aktiviert wurde t
. Dies liegt daran i
, dass innerhalb des t
Namespace, der sich vom Test
Namespace unterscheidet, neu gebunden wurde . Wenn Sie den Wert einer "statischen" Variablen ändern möchten, müssen Sie ihn innerhalb des Bereichs (oder Objekts) ändern, in dem er ursprünglich definiert wurde. Ich habe "statisch" in Anführungszeichen gesetzt, weil Python nicht wirklich statische Variablen in dem Sinne hat, wie es C ++ und Java tun.
Obwohl es nichts Spezifisches über statische Variablen oder Methoden aussagt, enthält das Python-Lernprogramm einige relevante Informationen zu Klassen und Klassenobjekten .
@Steve Johnson antwortete auch in Bezug auf statische Methoden, die ebenfalls unter "Integrierte Funktionen" in der Python-Bibliotheksreferenz dokumentiert sind.
class Test(object):
@staticmethod
def f(arg1, arg2, ...):
...
@beid erwähnte auch die Klassenmethode, die der statischen Methode ähnelt. Das erste Argument einer Klassenmethode ist das Klassenobjekt. Beispiel:
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
. Jetzt können Sie tun x = Test()
, x.i = 12
, assert x.i == Test.i
.
Wie die anderen Antworten angemerkt haben, können statische und Klassenmethoden mit den eingebauten Dekoratoren leicht durchgeführt werden:
class Test(object):
# regular instance method:
def MyMethod(self):
pass
# class method:
@classmethod
def MyClassMethod(klass):
pass
# static method:
@staticmethod
def MyStaticMethod():
pass
Wie üblich ist das erste Argument MyMethod()
an an das Klasseninstanzobjekt gebunden. Im Gegensatz zu dem ersten Argumente MyClassMethod()
wird auf das Klassenobjekt gebunden selbst (zB in diesem Fall Test
). ZumMyStaticMethod()
keines der Argumente ist gebunden, und Argumente sind optional.
Die Implementierung von "statischen Variablen" (also veränderbare statische Variablen, wenn dies kein Widerspruch ist ...) ist jedoch nicht so einfach. Wie millerdev in seiner Antwort hervorhob , besteht das Problem darin, dass Pythons Klassenattribute keine wirklich "statischen Variablen" sind. Erwägen:
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
Dies liegt daran, dass die Zeile x.i = 12
ein neues Instanzattribut hinzugefügt hat i
, x
anstatt den Wert des Test
Klassenattributs zu ändern i
.
Das teilweise erwartete Verhalten statischer Variablen, dh das Synchronisieren des Attributs zwischen mehreren Instanzen (jedoch nicht mit der Klasse selbst; siehe "gotcha" unten), kann erreicht werden, indem das Klassenattribut in eine Eigenschaft umgewandelt wird:
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)
Jetzt können Sie tun:
x1 = Test()
x2 = Test()
x1.i = 50
assert x2.i == x1.i # no error
assert x2.i == 50 # the property is synced
Die statische Variable bleibt nun zwischen allen Klasseninstanzen synchron .
(HINWEIS: Das heißt, es sei denn, eine Klasseninstanz beschließt, ihre eigene Version von zu definieren _i
! Aber wenn sich jemand dafür entscheidet, verdient er das, was er bekommt, nicht wahr ???)
Beachten Sie, dass es sich technisch gesehen i
immer noch überhaupt nicht um eine 'statische Variable' handelt. es ist ein property
, was eine spezielle Art von Deskriptor ist. Das property
Verhalten entspricht jetzt jedoch einer (veränderlichen) statischen Variablen, die über alle Klasseninstanzen hinweg synchronisiert wird.
Lassen Sie für ein unveränderliches Verhalten statischer Variablen einfach den property
Setter weg:
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)
Wenn Sie nun versuchen, das Instanzattribut i
festzulegen, wird Folgendes zurückgegeben AttributeError
:
x = Test()
assert x.i == 3 # success
x.i = 12 # ERROR
Beachten Sie, dass die oben genannten Methoden nur mit Instanzen Ihrer Klasse funktionieren - sie funktionieren nicht , wenn Sie die Klasse selbst verwenden . Also zum Beispiel:
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'
Die Zeile assert Test.i == x.i
erzeugt einen Fehler, da das i
Attribut von Test
und x
zwei verschiedene Objekte sind.
Viele Leute werden dies überraschend finden. Dies sollte jedoch nicht der Fall sein. Wenn wir zurückgehen und unsere Test
Klassendefinition (die zweite Version) überprüfen , beachten wir diese Zeile:
i = property(get_i)
Das Mitglied i
von Test
muss eindeutig ein property
Objekt sein. Dies ist der Objekttyp, der von der property
Funktion zurückgegeben wird.
Wenn Sie das oben Genannte verwirrend finden, denken Sie höchstwahrscheinlich immer noch aus der Perspektive anderer Sprachen (z. B. Java oder C ++) darüber nach. Du solltest das studieren gehenproperty
Objekt untersuchen, über die Reihenfolge, in der Python-Attribute zurückgegeben werden, das Deskriptorprotokoll und die Methodenauflösungsreihenfolge (MRO).
Ich präsentiere eine Lösung für das obige 'Gotcha' unten; Ich würde jedoch mit Nachdruck vorschlagen, dass Sie nicht versuchen, Folgendes zu tun, bis Sie zumindest gründlich verstanden haben, warum assert Test.i = x.i
ein Fehler verursacht wird.
Test.i == x.i
Ich präsentiere die (Python 3) -Lösung unten nur zu Informationszwecken. Ich unterstütze es nicht als "gute Lösung". Ich habe meine Zweifel, ob die Emulation des statischen Variablenverhaltens anderer Sprachen in Python jemals wirklich notwendig ist. Unabhängig davon, ob es tatsächlich nützlich ist, sollte das Folgende zum besseren Verständnis der Funktionsweise von Python beitragen.
UPDATE: Dieser Versuch ist wirklich ziemlich schrecklich ; Wenn Sie darauf bestehen, so etwas zu tun (Hinweis: Bitte nicht; Python ist eine sehr elegante Sprache, und es ist einfach nicht notwendig, sich wie eine andere Sprache zu verhalten), verwenden Sie stattdessen den Code in Ethan Furmans Antwort .
Emulieren des Verhaltens statischer Variablen anderer Sprachen mithilfe einer Metaklasse
Eine Metaklasse ist die Klasse einer Klasse. Die Standard-Metaklasse für alle Klassen in Python (dh die "New Style" -Klassen nach Python 2.3, glaube ich) ist type
. Zum Beispiel:
type(int) # class 'type'
type(str) # class 'type'
class Test(): pass
type(Test) # class 'type'
Sie können Ihre eigene Metaklasse jedoch folgendermaßen definieren:
class MyMeta(type): pass
Und wenden Sie es wie folgt auf Ihre eigene Klasse an (nur Python 3):
class MyClass(metaclass = MyMeta):
pass
type(MyClass) # class MyMeta
Unten ist eine von mir erstellte Metaklasse, die versucht, das Verhalten "statischer Variablen" anderer Sprachen zu emulieren. Grundsätzlich wird der Standard-Getter, -Setter und -Löscher durch Versionen ersetzt, die prüfen, ob das angeforderte Attribut eine "statische Variable" ist.
Ein Katalog der "statischen Variablen" wird im StaticVarMeta.statics
Attribut gespeichert . Es wird zunächst versucht, alle Attributanforderungen mithilfe einer Ersatzauflösungsreihenfolge aufzulösen. Ich habe dies die "statische Auflösungsreihenfolge" oder "SRO" genannt. Dies erfolgt durch Suchen nach dem angeforderten Attribut in der Menge der "statischen Variablen" für eine bestimmte Klasse (oder ihre übergeordneten Klassen). Wenn das Attribut nicht in der "SRO" angezeigt wird, greift die Klasse auf das Standardattribut get / set / delete zurück (dh "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
(bevor Sie es zum Instanziieren von Instanzen verwenden), als im Bereich der Metaprogrammierung befindlich ansehen ? Zum Beispiel ändern Sie das Klassenverhalten, indem Sie dies tun Test.i = 0
(hier zerstören Sie einfach das Eigenschaftsobjekt vollständig). Ich denke, der "Eigenschaftsmechanismus" wird nur beim Eigenschaftszugriff auf Instanzen einer Klasse aktiviert (es sei denn, Sie ändern das zugrunde liegende Verhalten möglicherweise mithilfe einer Metaklasse als Zwischenstufe). Übrigens, bitte beenden Sie diese Antwort :-)
Sie können Klassen auch im laufenden Betrieb Klassenvariablen hinzufügen
>>> 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
Und Klasseninstanzen können Klassenvariablen ändern
class X:
l = []
def __init__(self):
self.l.append(1)
print X().l
print X().l
>python test.py
[1]
[1, 1]
Persönlich würde ich eine Klassenmethode verwenden, wenn ich eine statische Methode benötigte. Hauptsächlich, weil ich die Klasse als Argument bekomme.
class myObj(object):
def myMethod(cls)
...
myMethod = classmethod(myMethod)
oder verwenden Sie einen Dekorateur
class myObj(object):
@classmethod
def myMethod(cls)
Für statische Eigenschaften. Es ist an der Zeit, eine Python-Definition nachzuschlagen. Die Variable kann sich jederzeit ändern. Es gibt zwei Arten von ihnen, die veränderlich und unveränderlich sind. Außerdem gibt es Klassenattribute und Instanzattribute. Nichts wie statische Attribute im Sinne von Java & C ++
Warum statische Methode im pythonischen Sinne verwenden, wenn sie keinerlei Beziehung zur Klasse hat? Wenn ich Sie wäre, würde ich entweder die Klassenmethode verwenden oder die Methode unabhängig von der Klasse definieren.
Eine besondere Anmerkung zu statischen Eigenschaften und Instanzeigenschaften, die im folgenden Beispiel gezeigt wird:
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
Dies bedeutet, dass vor dem Zuweisen des Werts zur Instanzeigenschaft der statische Wert verwendet wird, wenn wir versuchen, über die Instanz auf die Eigenschaft zuzugreifen. Jede in der Python-Klasse deklarierte Eigenschaft verfügt immer über einen statischen Speicherplatz .
Statische Methoden in Python werden aufgerufen Klassenmethoden bezeichnet . Schauen Sie sich den folgenden Code an
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
Beachten Sie, dass beim Aufrufen der Methode myInstanceMethod eine Fehlermeldung angezeigt wird. Dies liegt daran, dass die Methode für eine Instanz dieser Klasse aufgerufen werden muss. Die Methode myStaticMethod wird mit der Methode als Klassenmethode festgelegt Decorator @classmethod festgelegt .
Nur für Tritte und Kichern könnten wir myInstanceMethod für die Klasse aufrufen, indem wir eine Instanz der Klasse übergeben, wie folgt :
>>> MyClass.myInstanceMethod(MyClass())
output from an instance method
@staticmethod
; @classmethod
ist (offensichtlich) für Klassenmethoden (die hauptsächlich zur Verwendung als alternative Konstruktoren vorgesehen sind, aber zur Not als statische Methoden dienen können, die zufällig einen Verweis auf die Klasse erhalten, durch die sie aufgerufen wurden).
Wenn Sie eine Elementvariable außerhalb einer Elementmethode definieren, kann die Variable je nach Ausdruck der Variablen entweder statisch oder nicht statisch sein.
Zum Beispiel:
#!/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()
Die Ergebnisse sind
self.var is 2
A.var is 1
self.var is 2
A.var is 3
Es ist möglich zu haben static
Klassenvariablen , aber die Mühe lohnt sich wahrscheinlich nicht.
Hier ist ein in Python 3 geschriebener Proof-of-Concept. Wenn eines der genauen Details falsch ist, kann der Code so angepasst werden, dass er genau dem entspricht, was Sie mit a meinen 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)
und in Gebrauch:
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')
und einige Tests:
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
Sie können eine Klasse auch mithilfe der Metaklasse als statisch erzwingen.
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
Wenn Sie dann versehentlich versuchen, MyClass zu initialisieren, erhalten Sie einen StaticClassError.
__new__
ihrer Eltern aufzurufen ...
Ein sehr interessanter Punkt bei Pythons Attributsuche ist, dass damit " virtuelle Variablen" erstellt werden können:
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!
Normalerweise gibt es keine Zuordnungen zu diesen, nachdem sie erstellt wurden. Beachten Sie, dass die Suche verwendet wird, self
da der Wert zwar label
statisch in dem Sinne ist, dass er keiner bestimmten Instanz zugeordnet ist, der Wert jedoch immer noch von der (Klasse der) Instanz abhängt.
In Bezug auf diese Antwort können Sie für eine konstante statische Variable einen Deskriptor verwenden. Hier ist ein Beispiel:
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
ergebend ...
small demo 10
small subdemo 100
big demo 10
big subdemo 10
Sie können jederzeit eine Ausnahme auslösen, wenn pass
es nicht Ihre Sache ist , den Einstellwert ( oben) stillschweigend zu ignorieren . Wenn Sie nach einer statischen Klassenvariablen im C ++ - Java-Stil suchen:
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
Weitere Informationen zu Deskriptoren finden Sie in dieser Antwort und in den offiziellen HOWTO- Dokumenten .
@property
, was mit einem Deskriptor identisch ist, aber es ist viel weniger Code.
Absolut Ja, Python selbst hat kein explizites statisches Datenelement, aber wir können dies tun
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())
Ausgabe
0
0
1
1
Erläuterung
here object (x) alone increment the counter variable
from 0 to 1 by not object y. But result it as "static counter"
Ja, es ist definitiv möglich, statische Variablen und Methoden in Python zu schreiben.
Statische Variablen: Auf Klassenebene deklarierte Variablen werden als statische Variablen bezeichnet, auf die direkt über den Klassennamen zugegriffen werden kann.
>>> class A:
...my_var = "shagun"
>>> print(A.my_var)
shagun
Instanzvariablen: Variablen, die von einer Instanz einer Klasse verknüpft sind und auf die zugegriffen wird, sind Instanzvariablen.
>>> a = A()
>>> a.my_var = "pruthi"
>>> print(A.my_var,a.my_var)
shagun pruthi
Statische Methoden: Ähnlich wie bei Variablen kann auf statische Methoden direkt über den Klassennamen zugegriffen werden. Sie müssen keine Instanz erstellen.
Beachten Sie jedoch, dass eine statische Methode in Python keine nicht statische Methode aufrufen kann.
>>> class A:
... @staticmethod
... def my_static_method():
... print("Yippey!!")
...
>>> A.my_static_method()
Yippey!!
Um mögliche Verwirrung zu vermeiden, möchte ich statische Variablen und unveränderliche Objekte gegenüberstellen.
Einige primitive Objekttypen wie Ganzzahlen, Gleitkommazahlen, Zeichenfolgen und Touple sind in Python unveränderlich. Dies bedeutet, dass sich das Objekt, auf das mit einem bestimmten Namen verwiesen wird, nicht ändern kann, wenn es von einem der oben genannten Objekttypen ist. Der Name kann einem anderen Objekt zugewiesen werden, das Objekt selbst darf jedoch nicht geändert werden.
Wenn Sie eine Variable statisch machen, gehen Sie noch einen Schritt weiter, indem Sie nicht zulassen, dass der Variablenname auf ein anderes Objekt als das zeigt, auf das er derzeit verweist. (Hinweis: Dies ist ein allgemeines Softwarekonzept und nicht spezifisch für Python. Informationen zum Implementieren von Statik in Python finden Sie in den Beiträgen anderer.)
Der beste Weg, den ich gefunden habe, ist, eine andere Klasse zu verwenden. Sie können ein Objekt erstellen und es dann für andere Objekte verwenden.
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()
Mit dem obigen Beispiel habe ich eine Klasse mit dem Namen erstellt staticFlag
.
Diese Klasse sollte die statische Variable __success
(Private Static Var) darstellen.
tryIt
Klasse repräsentiert die reguläre Klasse, die wir verwenden müssen.
Jetzt habe ich ein Objekt für eine Flagge gemacht (staticFlag
). Dieses Flag wird als Referenz auf alle regulären Objekte gesendet.
Alle diese Objekte werden der Liste hinzugefügt tryArr
.
Dieses Skript Ergebnisse:
False
False
False
False
False
True
True
True
True
True
Für alle, die eine Klassenfactory mit Python3.6 oder höher verwenden , verwenden Sie das nonlocal
Schlüsselwort, um sie dem Bereich / Kontext der Klasse hinzuzufügen, die wie folgt erstellt wird:
>>> 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')
ist False
. Ich bezweifle, dass dies überhaupt jemand mit einer statischen Variablen meint.
some_var
unveränderlich und statisch definiert, oder nicht? Was hat der externe Getter-Zugriff damit zu tun, dass eine Variable statisch ist oder nicht? Ich habe jetzt so viele Fragen. würde gerne einige Antworten hören, wenn Sie die Zeit bekommen.
some_var
oben überhaupt kein Klassenmitglied. In Python kann von außerhalb der Klasse auf alle Klassenmitglieder zugegriffen werden.
nonlocal
Keywoard "stößt" den Bereich der Variablen an. Der Bereich einer Klassenkörperdefinition ist unabhängig von dem Bereich, in dem sie sich befindet, wenn Sie sagen nonlocal some_var
, dass lediglich ein nicht lokaler ( sprich : NICHT im Bereich der Klassendefinition) Namensreferenz auf ein anderes benanntes Objekt erstellt wird. Daher wird es nicht an die Klassendefinition angehängt, da es nicht im Bereich des Klassenkörpers enthalten ist.
Das ist also wahrscheinlich ein Hack, aber ich habe eval(str)
in Python 3 ein statisches Objekt erhalten, eine Art Widerspruch.
Es gibt eine Records.py-Datei, in der nur class
Objekte mit statischen Methoden und Konstruktoren definiert sind, die einige Argumente speichern. Dann muss ich aus einer anderen .py-Datei import Records
jedes Objekt dynamisch auswählen und es dann bei Bedarf entsprechend der Art der eingelesenen Daten instanziieren.
Also, wo object_name = 'RecordOne'
oder den Klassennamen rufe ich auf cur_type = eval(object_name)
und dann, um ihn zu instanziieren, tun cur_inst = cur_type(args)
Sie dies. Bevor Sie jedoch instanziieren, können Sie statische Methoden aufrufen, cur_type.getName()
beispielsweise wie eine abstrakte Basisklassenimplementierung oder was auch immer das Ziel ist. Im Backend ist es jedoch wahrscheinlich in Python instanziiert und nicht wirklich statisch, da eval ein Objekt zurückgibt, das instanziiert worden sein muss und ein statisches Verhalten ergibt.
Sie können eine Liste oder ein Wörterbuch verwenden, um "statisches Verhalten" zwischen Instanzen zu erhalten.
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
Wenn Sie versuchen, eine statische Variable freizugeben, um sie beispielsweise für andere Instanzen zu erhöhen, funktioniert so etwas wie dieses Skript einwandfrei:
# -*- 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
über@staticmethod
AFAIK ist , dass Sie immer den Namen der Klasse bekommen die Methode aufgerufen wurde auf, auch wenn es sich um eine Unterklasse ist. Einer statischen Methode fehlen diese Informationen, sodass sie beispielsweise keine überschriebene Methode aufrufen kann.