类(静态)变量和方法

如何在Python中创建类(即静态)变量或方法?

1570908 次浏览

在类定义中声明但不在方法中声明的变量是类或静态变量:

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

正如@Millerdev所指出的,这创建了一个类级i变量,但这与任何实例级i变量不同,因此您可以

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

这与C++和Java不同,但与C#没有太大区别,后者不能使用对实例的引用访问静态成员。

Python教程对类和类对象的主题有什么看法

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

class C:@staticmethoddef f(arg1, arg2, ...): ...

@beidy推荐类方法而不是静态方法,因为该方法然后接收类类型作为第一个参数。

就我个人而言,每当我需要静态方法时,我都会使用类方法。主要是因为我把类作为参数。

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

或使用装饰

class myObj(object):@classmethoddef myMethod(cls)

对于静态属性…它的时间你查找一些python定义…变量总是可以改变。它们有两种类型可变和不可变…此外,还有类属性和实例属性…没有什么真正喜欢java意义上的静态属性&c++

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

python中的静态方法称为类方法。看看下面的代码

class MyClass:
def myInstanceMethod(self):print 'output from an instance method'
@classmethoddef 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

请注意,当我们调用方法实例方法时,我们得到了一个错误。这是因为它要求在该类的实例上调用该方法。方法静态方法使用装饰者@吴兴龙设置为类方法。

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

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

@Blair Conrad说在类定义中声明的静态变量,但不是在方法中声明的是类或“静态”变量:

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

这里有一些问题。继续上面的例子:

>>> t = Test()>>> t.i     # "static" variable accessed via instance3>>> t.i = 5 # but if we assign to the instance ...>>> Test.i  # we have not changed the "static" variable3>>> t.i     # we have overwritten Test.i on t by creating a new attribute t.i5>>> Test.i = 6 # to change the "static" variable we do it by assigning to the class>>> t.i5>>> Test.i6>>> u = Test()>>> u.i6           # 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上时,实例变量t.i是如何与“静态”类变量不同步的。这是因为i被重新绑定在t命名空间内,这与Test命名空间不同。如果你想更改“静态”变量的值,你必须在最初定义它的范围(或对象)内更改它。我把“静态”放在引号中,因为Python并不像C++和Java那样真正拥有静态变量。

虽然它没有具体说明静态变量或方法,但python教程有一些关于类和类对象的相关信息。

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

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

@beid还提到了类方法,它类似于静态方法。类方法的第一个参数是类对象。例子:

class Test(object):i = 3 # class (or static) variable@classmethoddef 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

上述示例的图形表示

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

一些原始对象类型,如整数、浮点数、字符串和触点,在Python中是不可变的。这意味着给定名称引用的对象如果属于上述对象类型之一,则无法更改。可以将名称重新分配给不同的对象,但对象本身不得更改。

使变量成为静态变量更进一步,不允许变量名指向任何对象,但它当前指向的对象。(注意:这是一个通用的软件概念,不是Python特有的;有关在Python中实现静态的信息,请参阅其他人的帖子)。

您还可以动态地将类变量添加到类中

>>> class X:...     pass...>>> X.bar = 0>>> x = X()>>> x.bar0>>> x.fooTraceback (most recent call last):File "<interactive input>", line 1, in <module>AttributeError: X instance has no attribute 'foo'>>> X.foo = 1>>> x.foo1

类实例可以改变类变量

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

您还可以使用元类强制类为静态。

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 = 1b = 3
@staticmethoddef add(x, y):return x+y

然后,每当你偶然尝试初始化MyClass时,你都会得到一个静态类错误。

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

class my_cls:my_prop = 0
#static propertyprint my_cls.my_prop  #--> 0
#assign value to static propertymy_cls.my_prop = 1print my_cls.my_prop  #--> 1
#access static property thru' instancemy_inst = my_cls()print my_inst.my_prop #--> 1
#instance property is different from static property#after being assigned a valuemy_inst.my_prop = 2print my_cls.my_prop  #--> 1print my_inst.my_prop #--> 2

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

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

class staticFlag:def __init__(self):self.__success = Falsedef isSuccess(self):return self.__successdef succeed(self):self.__success = True
class tryIt:def __init__(self, staticFlag):self.isSuccess = staticFlag.isSuccessself.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()

使用上面的示例,我创建了一个名为staticFlag的类。

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

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

现在我为一个标志(staticFlag)创建了一个对象。这个标志将作为对所有常规对象的引用发送。

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


这个脚本结果:

FalseFalseFalseFalseFalseTrueTrueTrueTrueTrue

当在任何成员方法之外定义某个成员变量时,该变量可以是静态的或非静态的,具体取决于变量的表达方式。

  • CLASSNAME.var是静态变量
  • INSTANCENAME.var不是静态变量。
  • 类内部self.var不是静态变量。
  • 类成员函数内部的var未定义。

例如:

#!/usr/bin/python
class A:var=1
def printvar(self):print "self.var is %d" % self.varprint "A.var is %d" % A.var

a = A()a.var = 2a.printvar()
A.var = 3a.printvar()

结果是

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

关于这个回答,对于恒定静态变量,您可以使用描述符。这是一个例子:

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 changedemo.x = 100# should changesubdemo.x = 100print "small demo", demo.xprint "small subdemo", subdemo.xprint "big demo", Demo.xprint "big subdemo", SubDemo.x

导致…

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

如果你不喜欢忽略设置值(上面的pass),你总是可以引发异常。如果你正在寻找一个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

有关描述符的更多信息,请查看这个答案和官方文档HOWTO

静态和类方法

正如其他答案所指出的,静态和类方法可以使用内置装饰器轻松完成:

class Test(object):
# regular instance method:def my_method(self):pass
# class method:@classmethoddef my_class_method(cls):pass
# static method:@staticmethoddef my_static_method():pass

像往常一样,my_method()的第一个参数绑定到类实例对象。相比之下,my_class_method()的第一个参数是绑定到类对象本身(例如,在这种情况下,Test)。对于my_static_method(),没有任何参数被绑定,根本没有参数是可选的。

“静态变量”

然而,实现“静态变量”(好吧,可变静态变量,无论如何,如果这在术语上不是矛盾的话……)并不是那么简单。正如milerdev在他的回答中指出所说,问题在于Python的类属性并不是真正的“静态变量”。考虑:

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 instanceassert x.i == Test.i  # ERRORassert Test.i == 3    # Test.i was not affectedassert x.i == 12      # x.i is a different object than Test.i

这是因为行x.i = 12添加了一个新的实例属性ix,而不是更改Testi属性的值。

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

class Test(object):
_i = 3
@propertydef i(self):return type(self)._i
@i.setterdef 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)

现在你可以做:

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

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

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

请注意,从技术上讲,i根本不是一个“静态变量”;它是property,这是一种特殊类型的描述符。然而,property行为现在等价于跨所有类实例同步的(可变)静态变量。

不可变的“静态变量”

对于不可变的静态变量行为,只需省略property setter:

class Test(object):
_i = 3
@propertydef 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 x.i == 3  # successx.i = 12         # ERROR

一个Gotcha要意识到

请注意,上述方法仅适用于您的类的实例-它们将没有适用于当使用类本身。例如:

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'

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

许多人会觉得这令人惊讶。然而,这不应该。如果我们回去检查我们的Test类定义(第二个版本),我们会注意到这一行:

    i = property(get_i)

显然,Test的成员i必须是property对象,这是从property函数返回的对象类型。

如果你发现上面的问题令人困惑,那么你很可能还在从其他语言的角度考虑它(例如Java或c++)。你应该去研究property对象,关于Python属性的返回顺序、描述符协议和方法解析顺序(MRO)。

我在下面提出了一个解决上述问题的方法;然而,我强烈建议你不要尝试做下面的事情,直到——至少——你彻底理解为什么assert Test.i = x.i会导致错误。

真的真的静态变量-Test.i == x.i

我在下面介绍的Python 3解决方案仅供参考。我并不认为它是一个“好的解决方案”。我怀疑在Python中模拟其他语言的静态变量行为是否真的有必要。然而,不管它是否真的有用,下面的内容应该有助于进一步理解Python的工作原理。

更新:这次尝试真的很可怕;如果你坚持这样做(提示:请不要;Python是一种非常优雅的语言,没有必要让它表现得像另一种语言),请使用伊森·弗曼的回答中的代码。

使用元类模拟其他语言的静态变量行为

元类是类的类。Python中所有类的默认元类(即我认为Python 2.3后的“新风格”类)是type。例如:

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

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

class MyMeta(type): pass

并像这样将其应用于您自己的类(仅限Python 3):

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

下面是我创建的一个元类,它试图模拟其他语言的“静态变量”行为。它基本上是通过将默认的getter、setter和deleter替换为检查被请求的属性是否是“静态变量”的版本来工作的。

“静态变量”的曲库存储在StaticVarMeta.statics属性中。所有属性请求最初都尝试使用替代解析顺序来解析。我称之为“静态解析顺序”,或“SRO”。这是通过在给定类(或其父类)的“静态变量”集中查找请求的属性来完成的。如果该属性没有出现在“SRO”中,该类将回到默认属性get/set/删除行为(即“MRO”)。

from functools import wraps
class StaticVarsMeta(type):'''A metaclass for creating classes that emulate the "static variable" behaviorof 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 attributea = 1  # static var defined (optional)            
class MyParentClass(MyBaseClass):__statics__ = {'d','e','f'}j = 2              # regular attributed, e, f = 3, 4, 5  # Static varsa, 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 objectcls = 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 attributescls.__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 stringsif 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 clsdef __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 wrapperdef __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 wrapperdef __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 wrapperdef __getstatic__(cls,attr):'''Static variable getter'''for c in cls.__sro__:if attr in StaticVarsMeta.statics[c]:try:return getattr(c,attr)except AttributeError:passraise 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)breakdef __delstatic__(cls,attr):'''Static variable deleter'''for c in cls.__sro__:if attr in StaticVarsMeta.statics[c]:try:delattr(c,attr)breakexcept AttributeError:passraise 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 Truereturn False

有可能有static类变量,但可能不值得努力。

这是一个用Python 3编写的概念验证——如果任何确切的细节是错误的,可以调整代码以匹配你所说的static variable


class Static:def __init__(self, value, doc=None):self.deleted = Falseself.value = valueself.__doc__ = docdef __get__(self, inst, cls=None):if self.deleted:raise AttributeError('Attribute not set')return self.valuedef __set__(self, inst, value):self.deleted = Falseself.value = valuedef __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 objdef __setattr__(cls, name, val):# check if object already existsobj = 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')

一些测试:

ms1 = MyStatic()ms2 = MyStatic()ms3 = MyStatic()assert ms1.a == ms2.a == ms3.a == MyStatic.aassert ms1.b == ms2.b == ms3.b == MyStatic.bassert ms1.c == ms2.c == ms3.c == MyStatic.cms1.a = 77assert ms1.a == ms2.a == ms3.a == MyStatic.ams2.b = 99assert ms1.b == ms2.b == ms3.b == MyStatic.bMyStatic.a = 101assert ms1.a == ms2.a == ms3.a == MyStatic.aMyStatic.b = 139assert ms1.b == ms2.b == ms3.b == MyStatic.bdel MyStatic.bfor inst in (ms1, ms2, ms3):try:getattr(inst, 'b')except AttributeError:passelse:print('AttributeError not raised on %r' % attr)ms1.c = 13ms2.c = 17ms3.c = 19assert ms1.c == 13assert ms2.c == 17assert ms3.c == 19MyStatic.c = 43assert ms1.c == 13assert ms2.c == 17assert ms3.c == 19
ys1 = YourStatic()ys2 = YourStatic()ys3 = YourStatic()MyStatic.b = 'burgler'assert ys1.a == ys2.a == ys3.a == YourStatic.a == MyStatic.aassert ys1.b == ys2.b == ys3.b == YourStatic.b == MyStatic.bassert ys1.d == ys2.d == ys3.d == YourStatic.dassert ys1.e == ys2.e == ys3.e == YourStatic.eys1.a = 'blah'assert ys1.a == ys2.a == ys3.a == YourStatic.a == MyStatic.ays2.b = 'kelp'assert ys1.b == ys2.b == ys3.b == YourStatic.b == MyStatic.bys1.d = 'fee'assert ys1.d == ys2.d == ys3.d == YourStatic.dys2.e = 'fie'assert ys1.e == ys2.e == ys3.e == YourStatic.eMyStatic.a = 'aargh'assert ys1.a == ys2.a == ys3.a == YourStatic.a == MyStatic.a

类工厂python3.6中的静态变量

对于任何使用python3.6及以上的类厂的人,使用nonlocal关键字将其添加到正在创建的类的范围/上下文中,如下所示:

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

当然可以。Python本身没有任何静态数据成员,但我们可以通过这样做来拥有

class A:counter =0def callme (self):A.counter +=1def getcount (self):return self.counter>>> x=A()>>> y=A()>>> print(x.getcount())>>> print(y.getcount())>>> x.callme()>>> print(x.getcount())>>> print(y.getcount())

输出

0011

解释

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

关于Python的属性查找的一个非常有趣的点是它可以用来创建“虚拟变量”:

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!

通常在创建后不会对它们进行任何赋值。请注意,查找使用self,因为尽管label在不与特别实例关联的意义上是静态的,但值仍然取决于实例的(类)。

是的,绝对可以在python中编写静态变量和方法。

静态变量:在类级别声明的变量称为静态变量,可以直接使用类名访问。

    >>> class A:...my_var = "shagun"
>>> print(A.my_var)shagun

实例变量:类的实例相关和访问的变量是实例变量。

   >>> a = A()>>> a.my_var = "pruthi">>> print(A.my_var,a.my_var)shagun pruthi

静态方法:与变量类似,静态方法可以使用class Name直接访问。无需创建实例。

但请记住,静态方法不能在python中调用非静态方法。

    >>> class A:...     @staticmethod...     def my_static_method():...             print("Yippey!!")...>>> A.my_static_method()Yippey!!

您可以使用列表或字典来获取实例之间的“静态行为”。

class Fud:
class_vars = {'origin_open':False}
def __init__(self, origin = True):self.origin = originself.opened = Trueif 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 = Falseif 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

所以这可能是一个黑客,但我一直在使用eval(str)来获取一个静态对象,有点矛盾,在python 3中。

有一个Records.py文件,只有class对象,用静态方法和构造函数定义,保存一些参数。然后从另一个. py文件Iimport Records,但我需要动态选择每个对象,然后根据读取的数据类型按需实例化它。

所以object_name = 'RecordOne'或类名,我调用cur_type = eval(object_name)然后实例化它,你做cur_inst = cur_type(args)然而,在实例化之前,你可以从cur_type.getName()调用静态方法,例如,有点像抽象基类实现或任何目标。然而,在后端,它可能在python中实例化,并不是真正的静态,因为ava返回的是一个对象……它一定是实例化的………这给出了类似静态的行为。

如果您试图共享一个静态变量,例如,在其他实例中增加它,像这样的脚本可以正常工作:

# -*- coding: utf-8 -*-class Worker:id = 1
def __init__(self):self.name = ''self.document = ''self.id = Worker.idWorker.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 = nameworker.document = docself.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)

这样说吧,静态变量是在用户定义的类存在时创建的,定义静态变量应该遵循关键字self,

class Student:
the correct way of static declarationi = 10
incorrectself.i = 10

@dataclass定义提供用于定义实例变量和初始化方法的类级名称,__init__()。如果您想在@dataclass中使用类级变量,您应该使用#2类型提示。ClassVar类型的参数定义类级变量的类型。

from typing import ClassVarfrom dataclasses import dataclass
@dataclassclass Test:i: ClassVar[int] = 10x: inty: int    
def __repr__(self):return f"Test({self.x=}, {self.y=}, {Test.i=})"

使用示例:

> test1 = Test(5, 6)> test2 = Test(10, 11)
> test1Test(self.x=5, self.y=6, Test.i=10)> test2Test(self.x=10, self.y=11, Test.i=10)

不像@staticmethod,但类变量是类的静态方法,并与所有实例共享。

现在你可以像这样访问它

instance = MyClass()print(instance.i)

print(MyClass.i)

你必须给这些变量赋值

我在努力

class MyClass:i: str

并在一个方法调用中分配值,在这种情况下,它将不起作用并抛出错误

i is not attribute of MyClass

使用Object数据类型是可能的。但是对于像boolintfloatstr这样的原始类型,ba行为与其他OOP语言不同。因为在继承的类中不存在静态属性。如果属性在继承的类中不存在,Python开始在父类中寻找它。如果在父类中找到,它的值将被返回。当你决定改变继承类中的值时,静态属性将在运行时创建。在下次读取继承的静态属性时,它的值将被返回,因为它已经定义了。对象(列表、字典)用作引用,因此将它们用作静态属性并继承它们是安全的。当您更改其属性值时,对象地址不会更改。

整数数据类型的示例:

class A:static = 1

class B(A):pass

print(f"int {A.static}")  # get 1 correctlyprint(f"int {B.static}")  # get 1 correctly
A.static = 5print(f"int {A.static}")  # get 5 correctlyprint(f"int {B.static}")  # get 5 correctly
B.static = 6print(f"int {A.static}")  # expected 6, but get 5 incorrectlyprint(f"int {B.static}")  # get 6 correctly
A.static = 7print(f"int {A.static}")  # get 7 correctlyprint(f"int {B.static}")  # get unchanged 6

基于引用数据类型库的解决方案:

from refdatatypes.refint import RefInt

class AAA:static = RefInt(1)

class BBB(AAA):pass

print(f"refint {AAA.static.value}")  # get 1 correctlyprint(f"refint {BBB.static.value}")  # get 1 correctly
AAA.static.value = 5print(f"refint {AAA.static.value}")  # get 5 correctlyprint(f"refint {BBB.static.value}")  # get 5 correctly
BBB.static.value = 6print(f"refint {AAA.static.value}")  # get 6 correctlyprint(f"refint {BBB.static.value}")  # get 6 correctly
AAA.static.value = 7print(f"refint {AAA.static.value}")  # get 7 correctlyprint(f"refint {BBB.static.value}")  # get 7 correctly

总结其他人的答案并添加,有很多方法可以在python中声明静态方法或变量。

1.使用静态方法作为装饰器:

可以简单地将装饰器放在声明的方法(函数)之上,使其成为静态方法。例如。

class Calculator:@staticmethoddef multiply(n1, n2, *args):Res = 1for num in args: Res *= numreturn n1 * n2 * Res
print(Calculator.multiply(1, 2, 3, 4))              # 24

2.使用静态方法作为参数函数:

此方法可以接收函数类型的参数,并返回传递的函数的静态版本。例如。

class Calculator:def add(n1, n2, *args):return n1 + n2 + sum(args)
Calculator.add = staticmethod(Calculator.add)print(Calculator.add(1, 2, 3, 4))                   # 10

3.使用类方法作为装饰器:

@类方法在函数上的效果与@static方法相似,但是这一次,需要在函数中接受一个额外的参数(类似于实例变量的self参数)。例如。

class Calculator:num = 0def __init__(self, digits) -> None:Calculator.num = int(''.join(digits))
@classmethoddef get_digits(cls, num):digits = list(str(num))calc = cls(digits)return calc.num
print(Calculator.get_digits(314159))                # 314159

4.使用类方法作为参数函数:

在不想修改类定义的情况下,也可以用作参数函数。例如。

class Calculator:def divide(cls, n1, n2, *args):Res = 1for num in args: Res *= numreturn n1 / n2 / Res
Calculator.divide = classmethod(Calculator.divide)
print(Calculator.divide(15, 3, 5))                  # 1.0

5.直接申报

在所有其他方法之外声明的方法/变量,但在类内部自动是静态的。

class Calculator:def subtract(n1, n2, *args):return n1 - n2 - sum(args)
print(Calculator.subtract(10, 2, 3, 4))             # 1

整个程序

class Calculator:num = 0def __init__(self, digits) -> None:Calculator.num = int(''.join(digits))    
    
@staticmethoddef multiply(n1, n2, *args):Res = 1for num in args: Res *= numreturn n1 * n2 * Res

def add(n1, n2, *args):return n1 + n2 + sum(args)    

@classmethoddef get_digits(cls, num):digits = list(str(num))calc = cls(digits)return calc.num

def divide(cls, n1, n2, *args):Res = 1for num in args: Res *= numreturn n1 / n2 / Res

def subtract(n1, n2, *args):return n1 - n2 - sum(args)    



Calculator.add = staticmethod(Calculator.add)Calculator.divide = classmethod(Calculator.divide)
print(Calculator.multiply(1, 2, 3, 4))              # 24print(Calculator.add(1, 2, 3, 4))                   # 10print(Calculator.get_digits(314159))                # 314159print(Calculator.divide(15, 3, 5))                  # 1.0print(Calculator.subtract(10, 2, 3, 4))             # 1

请参阅python文档以掌握Python中的OOP。

类变量并允许子类化

假设您不是在寻找真正静态变量,而是在寻找Pythonic,它可以为同意的成年人做同样的工作,那么使用类变量。这将为您提供一个所有实例都可以访问(和更新)的变量

注意:许多其他使用类变量的答案会破坏子类化。您应该避免直接通过名称引用类。

from contextlib import contextmanager
class Sheldon(object):foo = 73
def __init__(self, n):self.n = n
def times(self):cls = self.__class__return cls.foo * self.n#self.foo * self.n would give the same result here but is less readable# it will also create a local variable which will make it easier to break your code    
def updatefoo(self):cls = self.__class__cls.foo *= self.n#self.foo *= self.n will not work here# assignment will try to create a instance variable foo
@classmethod@contextmanagerdef reset_after_test(cls):originalfoo = cls.fooyieldcls.foo = originalfoo#if you don't do this then running a full test suite will fail#updates to foo in one test will be kept for later tests

将为您提供与使用Sheldon.foo寻址变量相同的功能,并将通过以下测试:

def test_times():with Sheldon.reset_after_test():s = Sheldon(2)assert s.times() == 146
def test_update():with Sheldon.reset_after_test():s = Sheldon(2)s.updatefoo()assert Sheldon.foo == 146
def test_two_instances():with Sheldon.reset_after_test():s = Sheldon(2)s3 = Sheldon(3)assert s.times() == 146assert s3.times() == 219s3.updatefoo()assert s.times() == 438

它还将允许其他人简单地:

class Douglas(Sheldon):foo = 42

这也将工作:

def test_subclassing():with Sheldon.reset_after_test(), Douglas.reset_after_test():s = Sheldon(2)d = Douglas(2)assert d.times() == 84assert s.times() == 146d.updatefoo()assert d.times() == 168 #Douglas.Foo was updatedassert s.times() == 146 #Seldon.Foo is still 73
def test_subclassing_reset():with Sheldon.reset_after_test(), Douglas.reset_after_test():s = Sheldon(2)d = Douglas(2)assert d.times() == 84 #Douglas.foo was reset after the last testassert s.times() == 146 #and so was Sheldon.foo

有关创建类时要注意的事项的重要建议,请查看Raymond Hettinger的视频https://www.youtube.com/watch?v=HTLu2DFOdTg

您可以创建类变量#0实例变量#1实例方法#2类方法#3静态方法#4,如下所示:

class Person:x = "Hello" # Class variable
def __init__(self, name):self.name = name # Instance variable    
def test1(self): # Instance methodprint("Test1")
@classmethoddef test2(cls): # Class methodprint("Test2")        
@staticmethoddef test3(): # Static methodprint("Test3")

我在<强>我的回答类方法中解释了类变量,在<强>我的回答中解释了静态方法,在<强>我的回答中解释了实例方法