python中是否可能有静态类变量或方法?这需要什么语法?
在类定义中声明但不在方法中声明的变量是类或静态变量:
1 2 3 4 5 | >>> class MyClass: ... i = 3 ... >>> MyClass.i 3 |
正如@millerdev所指出的,这创建了一个类级别的
1 2 3 4 | >>> m = MyClass() >>> m.i = 4 >>> MyClass.i, m.i >>> (3, 4) |
这与c++和Java不同,但与c#没有太大区别,在c#中不能使用对实例的引用访问静态成员。
查看Python教程对类和类对象的主题有什么要说的。
@Steve Johnson已经回答了关于静态方法的问题,也在Python库参考的"内置函数"中进行了说明。
1 2 3 | class C: @staticmethod def f(arg1, arg2, ...): ... |
@beidy建议使用classmethods而不是staticmethod,因为该方法将类类型作为第一个参数,但是对于这种方法相对于staticmethod的优势,我仍然有些模糊。如果你也是,那也没什么关系。
@Blair Conrad说在类定义中声明的静态变量,而不是在方法中声明的静态变量是类或"静态"变量:
1 2 3 4 5 | >>> class Test(object): ... i = 3 ... >>> Test.i 3 |
这里有一些问题。从上面的例子继续:
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 | >>> 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__ {} |
注意,当直接在
虽然Python教程没有说明关于静态变量或方法的任何具体内容,但是它提供了一些关于类和类对象的相关信息。
@Steve Johnson还回答了关于静态方法的问题,这些方法也记录在Python库参考中的"内置函数"中。
1 2 3 4 | class Test(object): @staticmethod def f(arg1, arg2, ...): ... |
@beid还提到了classmethod,它类似于staticmethod。类方法的第一个参数是类对象。例子:
1 2 3 4 5 6 7 | 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 |
静态方法和类方法
正如其他答案所指出的,静态方法和类方法很容易实现使用内置装饰:
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 | class Test(object): # regular instance method: def MyMethod(self): pass # class method: @classmethod def MyClassMethod(klass): pass # static method: @staticmethod def MyStaticMethod(): pass |
与往常一样,
"静态变量
然而,实现"静态变量"(嗯,可变静态变量,无论如何,如果这在术语上没有矛盾的话……)并不是那么简单。正如millerdev在他的回答中指出的,问题是Python的类属性并不是真正的"静态变量"。考虑:
1 2 3 4 5 6 7 8 | 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 |
这是因为
部分期望静态变量行为,即,在多个实例之间同步属性(但不与类本身同步;,可以通过将class属性转换为属性来实现:
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 | 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) |
现在你可以:
1 2 3 4 5 | x1 = Test() x2 = Test() x1.i = 50 assert x2.i == x1.i # no error assert x2.i == 50 # the property is synced |
静态变量现在将在所有类实例之间保持同步。
(注意:除非类实例决定定义自己版本的
注意,从技术上讲,
不可变"静态变量"
对于不可变静态变量行为,只需省略
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 | 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) |
现在尝试设置实例
1 2 3 | x = Test() assert x.i == 3 # success x.i = 12 # ERROR |
需要注意
注意,上面的方法只对类的实例起作用——当使用类本身时它们不起作用。举个例子:
1 2 3 4 5 6 | 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' |
行
许多人会对此感到惊讶。然而,这不应该。如果我们回去检查我们的
1 | i = property(get_i) |
显然,
如果您发现上面的内容令人困惑,那么您很可能仍然是从其他语言(例如Java或c++)的角度来考虑它。您应该研究
我在下面提出了一个解决上述"问题"的方法;然而,我强烈建议,至少在完全理解
下面给出的(python3)解决方案仅供参考。我并不认为这是一个"好的解决方案"。我怀疑是否有必要在Python中模仿其他语言的静态变量行为。然而,不管它是否真的有用,下面的内容应该有助于进一步理解Python是如何工作的。
更新:这个尝试非常糟糕;如果你坚持做这样的事情(提示:请不要做;Python是一种非常优雅的语言,不需要硬把它硬塞进另一种语言中),而是使用Ethan Furman的答案中的代码。
使用元类模拟其他语言的静态变量行为
元类是类的类。Python中所有类的默认元类(即,我相信Python 2.3之后的"new style"类)是
1 2 3 4 | type(int) # class 'type' type(str) # class 'type' class Test(): pass type(Test) # class 'type' |
但是,您可以这样定义您自己的元类:
1 | class MyMeta(type): pass |
然后像这样应用到你自己的类中(只适用于python3):
1 2 3 4 | class MyClass(metaclass = MyMeta): pass type(MyClass) # class MyMeta |
下面是我创建的一个元类,它试图模拟其他语言的"静态变量"行为。它的工作原理是用版本替换默认的getter、setter和deleter,这些版本检查被请求的属性是否是"静态变量"。
"静态变量"的目录存储在
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 76 77 78 79 80 81 82 83 84 85 86 87 88 89 90 91 92 93 94 95 96 97 98 99 100 101 102 103 104 105 106 107 108 109 110 111 112 113 114 115 116 117 118 119 | 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 |
您还可以动态地向类添加类变量
1 2 3 4 5 6 7 8 9 10 11 12 13 14 | >>> 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 |
类实例可以改变类变量
1 2 3 4 5 6 7 8 9 10 11 | class X: l = [] def __init__(self): self.l.append(1) print X().l print X().l >python test.py [1] [1, 1] |
就我个人而言,每当我需要静态方法时,我都会使用classmethod。主要是因为我把这个类作为一个参数。
1 2 3 4 | class myObj(object): def myMethod(cls) ... myMethod = classmethod(myMethod) |
或者使用装饰器
1 2 3 | class myObj(object): @classmethod def myMethod(cls) |
静态属性. .现在是您查找一些python定义的时候了。变量总是可以改变的。它们有两种类型:可变的和不可变的。此外,还有类属性和实例属性。没有什么比java中的静态属性更好的了。c++
如果静态方法与类没有任何关系,为什么要使用python意义上的静态方法!如果我是你,我要么使用classmethod,要么定义独立于类的方法。
关于静态属性需要特别注意的一点是;实例属性,如下例所示:
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 | 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类中声明的每个属性在内存中都有一个静态槽。
python中的静态方法称为类方法。看一下下面的代码
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 | 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,方法是传入一个类的实例,就像这样:
1 2 | >>> MyClass.myInstanceMethod(MyClass()) output from an instance method |
当在任何成员方法之外定义某个成员变量时,该变量可以是静态的,也可以是非静态的,这取决于该变量是如何表示的。
类名。var是静态变量都。var不是静态变量。自我。类内的var不是静态变量。类成员函数中的var没有定义。例如:
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 | #!/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() |
结果是
1 2 3 4 | self.var is 2 A.var is 1 self.var is 2 A.var is 3 |
您还可以使用元类强制类成为静态的。
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 | 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。
可以使用
这里有一个用Python 3编写的概念验证——如果有任何确切的细节是错误的,可以调整代码来匹配您所说的
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 | 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) |
在使用:
1 2 3 4 5 6 7 8 9 10 11 | 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') |
和一些测试:
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 | 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 |
Python的属性查找非常有趣的一点是,它可以用来创建"虚拟变量":
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 | 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! |
通常在它们创建之后没有任何赋值给它们。注意,查找使用
绝对是的,Python本身并不显式地拥有任何静态数据成员,但是我们可以通过这样做来拥有
1 2 3 4 5 6 7 8 9 10 11 12 13 | 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()) |
输出
1 2 3 4 | 0 0 1 1 |
解释
1 2 | here object (x) alone increment the counter variable from 0 to 1 by not object y. But result it as"static counter" |
对于这个答案,对于常量静态变量,可以使用描述符。这里有一个例子:
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 | 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 |
导致……
1 2 3 4 | small demo 10 small subdemo 100 big demo 10 big subdemo 10 |
如果您不喜欢悄悄地忽略设置值(
1 2 3 4 5 6 7 8 9 | 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 |
看看这个答案和官方文档如何获得更多关于描述符的信息。
为了避免任何潜在的混淆,我想对比静态变量和不可变对象。
一些基本对象类型,如整数、浮点数、字符串和元组,在Python中是不可变的。这意味着,如果对象属于上述对象类型之一,则由给定名称引用的对象不能更改。可以将名称重新分配给其他对象,但对象本身不能更改。
通过禁止变量名指向除它当前指向的对象之外的任何对象,使变量静态更进一步。(注意:这是一个通用的软件概念,不是特定于Python;有关用Python实现静态的信息,请参阅其他人的文章)。
我发现最好的方法是使用另一个类。您可以创建一个对象,然后在其他对象上使用它。
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 | 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() |
通过上面的示例,我创建了一个名为
这个类应该显示静态var
类表示我们需要使用的常规类。
现在我为一个标志创建了一个对象(
所有这些对象都被添加到列表
这个脚本的结果:
1 2 3 4 5 6 7 8 9 10 | False False False False False True True True True True |
是的,用python编写静态变量和方法是完全可能的。
静态变量:类级声明的变量称为静态变量,可以使用类名直接访问。
1 2 3 4 5 | >>> class A: ...my_var ="shagun" >>> print(A.my_var) shagun |
实例变量:由类的实例关联和访问的变量是实例变量。
1 2 3 4 | >>> a = A() >>> a.my_var ="pruthi" >>> print(A.my_var,a.my_var) shagun pruthi |
静态方法:与变量类似,可以使用类名直接访问静态方法。不需要创建实例。
但是请记住,静态方法不能在python中调用非静态方法。
1 2 3 4 5 6 7 | >>> class A: ... @staticmethod ... def my_static_method(): ... print("Yippey!!") ... >>> A.my_static_method() Yippey!! |
类工厂python3.6中的静态变量
对于任何使用python3.6及以上版本的类工厂的用户,请使用
1 2 3 4 5 6 7 8 9 | >>> 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 |