Python中的静态类variables

是否有可能在Python中有静态类variables或方法? 需要什么语法来做到这一点?

在类定义中声明的variables,而不是在一个方法中声明的是类或静态variables:

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

正如@ millerdev指出的那样,这会创build一个类级别的variables,但是这与任何实例级别的variables不同,所以您可以

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

这与C ++和Java不同,但与C#没有太大区别,静态成员无法通过对实例的引用来访问。

看看Python教程对于类和类对象的主题要说些什么 。

@Steve Johnson已经回答了有关静态方法的问题 ,也在Python库参考的“内置函数”中有logging 。

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

@beidy推荐使用静态方法的类方法,因为该方法接收类types作为第一个参数,但是我仍然对这种方法相对于静态方法的优点有些模糊。 如果你也是,那可能没关系。

@Blair Conrad表示在类定义里声明的静态variables,而不是在一个方法里面是类或“静态”variables:

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

这里有几个难题。 从上面的例子进行:

 >>> t = Test() >>> ti # static variable accessed via instance 3 >>> ti = 5 # but if we assign to the instance ... >>> Test.i # we have not changed the static variable 3 >>> ti # we have overwritten Test.i on t by creating a new attribute ti 5 >>> Test.i = 6 # to change the static variable we do it by assigning to the class >>> ti 5 >>> Test.i 6 >>> u = Test() >>> ui 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__ {} 

注意,当属性i直接在t上设置时,实例variablesti与“static”类variables不同步。 这是因为it名称空间内重新绑定,这与Test名称空间不同。 如果要更改“静态”variables的值,则必须在最初定义的范围(或对象)内对其进行更改。 我把“静态”放在引号中,因为在C ++和Java的意义上,Python并没有真正的静态variables。

尽pipe它没有提及任何有关静态variables或方法的具体信息,但是Python教程还是提供了一些关于类和类对象的相关信息。

@Steve Johnson也回答了有关静态方法的问题,这些方法也在“Python库参考”中的“内置函数”中有logging。

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

@beid也提到了classmethod,这与staticmethod类似。 classmethod的第一个参数是类对象。 例:

 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 the the same as Test.i = arg1 

以上例子的图示

静态和类方法

正如其他答案所指出的,静态和类的方法很容易使用内置的装饰器来完成:

 class Test(object): # regular instance method: def MyMethod(self): pass # class method: @classmethod def MyClassMethod(klass): pass # static method: @staticmethod def MyStaticMethod(): pass 

像往常一样, MyMethod()的第一个参数绑定到类实例对象。 相反, MyClassMethod()的第一个参数绑定到类对象本身 (例如,在这种情况下, Test )。 对于MyStaticMethod() ,没有任何参数被绑定,并且有参数是可选的。

“静态variables”

然而,实现“静态variables”(好吧, 可变的静态variables,反正如果这不是矛盾的话……)并不是那么直截了当。 正如millerdev 在他的回答中指出的 ,问题是Python的类属性并不是真正的“静态variables”。 考虑:

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

这是因为行xi = 12已将新的实例属性i添加到x而不是更改Test class i属性的值。

部分期望的静态variables行为,即多个实例之间的属性同步(但与类本身同步;参见下面的“gotcha”)可以通过将class属性转换为属性来实现:

 class Test(object): _i = 3 @property def i(self): return self._i @i.setter def i(self,val): 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 self._i def set_i(self,val): self._i = val i = property(get_i, set_i) 

现在你可以做:

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

静态variables现在将在所有类实例之间保持同步。

(注意:除非一个类实例决定定义它自己的_i !版本,但是如果有人决定这样做,他们应该得到他们得到的东西,不是吗?)

请注意,从技术上讲, i依然不是一个“静态variables” 它是一个property ,它是一种特殊types的描述符。 但是, property行为现在等同于跨所有类实例同步的(可变)静态variables。

不变的“静态variables”

对于不可变的静态variables行为,只需省略property设置器:

 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) 

现在试图设置实例i属性将返回一个AttributeError

 x = Test() assert xi == 3 # success xi = 12 # ERROR 

一个需要注意的问题

请注意,上述方法只适用于你的类的实例 – 它们在使用类本身时不起作用。 举个例子:

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

assert Test.i == xi会产生一个错误,因为Testxi属性是两个不同的对象。

许多人会发现这个令人惊讶的。 但是,它不应该。 如果我们回过头来检查我们的Test类定义(第二个版本),我们注意到这一行:

  i = property(get_i) 

显然, Test的成员i必须是property对象,它是property函数返回的对象的types。

如果你发现上面的混淆,你很可能仍然从其他语言(例如Java或c ++)的angular度来思考它。 您应该研究property对象,关于Python属性返回的顺序,描述符协议和方法parsing顺序(MRO)。

我提出了一个解决scheme,以上的“疑难杂症”下面; 但是我会build议 – 强烈地 – 你不要试图做下面的事情,至less – 你完全理解为什么assert Test.i = xi会导致错误。

实际的静态variables – Test.i == xi

我只提供下面的(Python 3)解决scheme,仅供参考。 我不赞成这是一个“好的解决办法”。 我对使用Python模拟其他语言的静态variables行为是否真的有必要抱有怀疑。 但是,不pipe它是否真的有用,下面的内容应该有助于进一步理解Python的工作原理。

更新:这个尝试真的很糟糕 ; 如果你坚持要做这样的事情(提示:请不要; Python是一种非常优雅的语言,而且它可以像其他语言一样运行),请使用Ethan Furman的代码来代替。

使用元类来模拟其他语言的静态variables行为

元类是一个类的类。 Python中所有类的默认元类(也就是我相信Python 2.3中的“新样式”类)是type 。 例如:

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

但是,您可以像这样定义自己的元类:

 class MyMeta(type): pass 

并把它应用到你自己的类(Python 3):

 class MyClass(metaclass = MyMeta): pass type(MyClass) # class MyMeta 

下面是我创build的一个元类,它尝试模拟其他语言的“静态variables”行为。 它基本上是通过replace缺省的getter,setter和deleter来检查被请求的属性是否为“静态variables”的版本。

“静态variables”的目录存储在StaticVarMeta.statics属性中。 所有属性请求最初都是尝试使用替代parsing顺序来parsing的。 我称之为“静态parsing顺序”,或“SRO”。 这是通过在给定类(或其父类)的“静态variables”集合中查找请求的属性来完成的。 如果该属性没有出现在“SRO”中,则该类将回退到默认属性get / set / delete行为(即“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 

您也可以将类variables添加到类中

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

而类实例可以改变类variables

 class X: l = [] def __init__(self): self.l.append(1) print X().l print X().l >python test.py [1] [1, 1] 

就我个人而言,当我需要一个静态方法时,我会使用一个classmethod。 主要是因为我把课堂作为一个论点。

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

或使用装饰器

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

对于静态属性..它的时间你查了一些python的定义..variables总是可以改变的。 有两种types的可变和不可变的。此外,还有类属性和实例属性..没有什么真正像java和c + +的意义上的静态属性

为什么在pythonic意义上使用静态方法,如果它与类没有任何关系! 如果我是你,我会使用classmethod或者定义独立于类的方法。

python中的静态方法叫做classmethod s。 看看下面的代码

 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 

注意当我们调用方法myInstanceMethod时 ,我们得到一个错误。 这是因为它要求在这个类的一个实例上调用该方法。 myStaticMethod方法被设置为使用装饰器 @classmethod的类方法。

只是为了踢和咯咯,我们可以通过传入类的实例来调用类的myInstanceMethod ,如下所示:

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

一个特别的事情要注意关于静态属性和实例属性,如下面的例子所示:

 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 

这意味着在将值赋给实例属性之前,如果我们试图通过实例访问属性,则使用静态值。 在python类中声明的每个属性在内存中总是有一个静态槽

你也可以使用元类强制一个类是静态的。

 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 

然后,每当偶然你尝试初始化MyClass你会得到一个StaticClassError。

当在任何成员方法之外定义一些成员variables时,variables可以是静态的也可以是非静态的,这取决于variables如何表示。

  • CLASSNAME.var是静态variables
  • INSTANCENAME.var不是静态variables。
  • 类里面的self.var不是静态variables。
  • 类成员函数里面的var没有定义。

例如:

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

结果是

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

有可能有static类variables,但可能不值得的努力。

这里有一个用Python 3编写的概念certificate – 如果任何确切的细节都是错误的,那么可以调整代码来匹配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) 

并在使用中:

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

和一些testing:

 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 

为了避免任何潜在的混淆,我想对比静态variables和不可变对象。

一些原始的对象types,如整数,浮点数,string和通知在Python中是不可变的。 这意味着如果它是前述对象types之一,则由给定名称引用的对象不能改变。 该名称可以重新分配给不同的对象,但对象本身可能不会更改。

做一个variables静态进一步通过不允许variables名称指向任何对象,但它目前指向。 (注意:这是一个通用的软件概念,不是特定于Python的;请参阅其他人的post,了解关于在Python中实现静态的信息)。

绝对是的,Python本身没有明确的静态数据成员,但我们可以这样做

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

产量

 0 0 1 1 

说明

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

关于这个答案 ,对于一个常量静态variables,你可以使用一个描述符。 这是一个例子:

 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 

导致 …

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

如果安静地忽略设置值( pass上面的)不是你的事情,你总是可以提出exception。 如果你正在寻找一个C ++,Java风格的静态类variables:

 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 

看看这个答案和官方文档HOWTO关于描述符的更多信息。

我发现的最好的方法是使用另一个类。 您可以创build一个对象,然后在其他对象上使用它。

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

用上面的例子,我创build了一个名为staticFlag的类。

这个类应该呈现静态var __success (Private Static Var)。

tryIt类代表了我们需要使用的常规类。

现在我为一个标志( staticFlag )做了一个对象。 该标志将作为所有常规对象的参考发送。

所有这些对象都被添加到tryArr列表中。


这个脚本结果:

 False False False False False True True True True True 

关于Python属性查找的一个很有意思的地方是它可以用来创build“ 虚拟variables”:

 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! 

通常这些创build后没有任何分配。 请注意,查找使用self因为尽pipelabel在不与特定实例关联的意义label是静态的,但值仍然取决于实例的(类)。

类工厂python3.6中的静态variables

对于任何使用python3.6以上的类工厂的人来说,使用nonlocal关键字将其添加到正在创build的类的作用域/上下文中,如下所示:

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