类(静态)变量和方法

2545

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


35
是的。缺少关键词“static”可能会产生误导,但是在类中初始化的任何对象(仅在类内部缩进一次,而不是在构造函数中)都是静态的。它不依赖于实例化(因为它不是构造函数的一部分)。至于方法,您可以使用@staticmethod修饰符来实现。 - user11991978
7
对于所有类的实例都存在的东西使用“静态”这个术语,对我来说总是感觉很奇怪。 - Tony Suffolk 66
12
我认为是因为C++将已有的关键字"static"从C中借用,它在C中表示变量的生命周期超出其声明时的作用域。C++将其扩展为指代一个变量其值在类的单个实例的"作用域"之外。Python(更合理地)将其称为类属性,因为它们是与类本身关联的属性,而不是类的一个实例。 - chepner
4
在 C++ 中,“static” 实际上有几个含义(由于评论长度非常严格,以下是缩写的定义)。有来自 C 的文件作用域 static,表示“此变量或函数仅可在此文件中使用”。还有类作用域 static,表示“此方法或字段与类型相关,而不是任何类型的实例”(在 C++ 中很少用,但在 C#、Java 和 ObjC 中很常见,例如,我认为这就是 OP 所问的内容)。还有函数中的局部变量 static,表示“此变量的值在函数调用之间保持不变”。 - jrh
4
转换到“个人观点”的角度,我认为在C#/Java中经常使用静态方法是因为这些语言采用了“禁止函数”的严格立场。在C#/Java中,只能有方法(即属于类的函数),而Python没有这个限制(在我看来,这是最好的)。个人来说,我宁愿使用C++的命名空间或从文件导入函数(Python),也不愿为不必要的原因创建一个类来保存函数。面向对象编程有其用途,但有时你只想要一个函数。 - jrh
显示剩余3条评论
27个回答

2334
类定义内部声明的变量,但不在方法内部声明的变量是类或静态变量:
>>> class MyClass:
...     i = 3
...
>>> MyClass.i
3 

正如 @millerdev 指出的,这将创建一个类级别的 i 变量,但它与任何实例级别的 i 变量是不同的,所以你可以有。
>>> m = MyClass()
>>> m.i = 4
>>> MyClass.i, m.i
>>> (3, 4)

这与C++和Java不同,但与C#并不完全不同,因为静态成员不能使用实例引用访问。
请参阅Python教程中关于类和类对象的内容
@Steve Johnson已经回答了关于静态方法的问题,并在Python库参考中的内置函数中有所记录。
class C:
    @staticmethod
    def f(arg1, arg2, ...): ...

@beidy建议使用classmethod而不是staticmethod,因为该方法将类类型作为第一个参数接收。


25
我正在学习Python,据我所知 @classmethod 相对于 @staticmethod 的优势在于,无论是父类还是子类调用该方法,你始终可以获取到类的名称。而静态方法则缺少这种信息,因此无法调用重写的方法。 - Seb
70
Python中的常量通常不需要为其创建一个类。只需使用一个名为const.py的文件来保存常量,例如PI = 3.14,然后您可以在任何地方导入它。您只需要使用 from const import PI 即可。 - Giszmo
59
这个答案可能会混淆关于静态变量的问题。首先,i = 3 不是一个静态变量,它是一个类属性,并且由于它与实例级别的属性i不同,所以它不像其他语言中的静态变量行为一样。请参见下面 millerdev's answer, Yann's answer, 和 my answer - Rick
9
即使我创建了数百个该类的实例,只有一个i(静态变量)的副本会存在于内存中吗? - Saurabh Jain
3
如果有人对@Dubslow评论中提到的Daniel感兴趣,他指的是millerdevwayback machine)。 - OfirD
显示剩余7条评论

765

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

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

这里有一些需要注意的地方。从上面的例子延伸:

>>> 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__
{}

请注意,当直接在t对象上设置属性i时,实例变量t.i与“静态”类变量不同步。这是因为i在t命名空间内重新绑定,该命名空间不同于Test命名空间。如果要更改“静态”变量的值,则必须在最初定义它的作用域(或对象)内进行更改。我在引号中放置了“静态”,因为Python实际上没有像C ++和Java那样的静态变量。
虽然它没有明确说明静态变量或方法,但Python教程有一些关于类和类对象的相关信息。classes and class objects也有相关信息。
@Steve Johnson也回答了有关静态方法的问题,在Python库参考文档的“Built-in Functions”下有记录。
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 be the same as Test.i = arg1

Pictorial Representation Of Above Example


9
我建议您稍微扩展一下示例:如果在将Test.i设置为6之后,您实例化一个新对象(例如u=Test()),那么新对象将“继承”新的类值(例如u.i==6)。 - Mark
11
保持静态变量同步的一种方法是将它们定义为属性:“class Test(object):”,“_i = 3”,“@property”,“def i(self)”,“return type(self)._i”,“@i.setter”,“def i(self,val)”,“type(self)._i = val”。现在,您可以执行“x = Test()”,“x.i = 12”,“assert x.i == Test.i”。 - Rick
3
我可以说所有变量最初都是静态的,然后在运行时访问实例会生成实例变量吗? - Ali
4
或许这很有趣:如果您在Test中定义了一个改变Test.i的方法,那么它将影响Test.i和t.i的值。 - Pablo
2
@millerdev,就像你提到的那样,Python没有像C++或JAVA那样的静态变量。那么说,Test.i更像是一个类变量而不是静态变量,这样可以吗? - NightOwl19
显示剩余4条评论

285

静态方法和类方法

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

class Test(object):

    # regular instance method:
    def my_method(self):
        pass

    # class method:
    @classmethod
    def my_class_method(cls):
        pass

    # static method:
    @staticmethod
    def my_static_method():
        pass

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

“静态变量”

然而,实现“静态变量”(好吧,至少是可变的静态变量,如果这不是自相矛盾的话……)并不像直接了当。正如millerdev在他的答案中指出的,问题在于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 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

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

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

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)
现在你可以做到:

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

静态变量现在将始终在所有类实例之间保持同步。(注意:除非某个类实例决定定义自己的 _i 版本!但是,如果有人决定这样做,他们就会得到他们想要的东西,不是吗?)

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

不可变的“静态变量”

对于不可变的静态变量行为,请简单省略 property setter:

class Test(object):

    _i = 3

    @property
    def i(self):
        return type(self)._i

## ALTERNATIVE IMPLEMENTATION - FUNCTIONALLY EQUIVALENT TO ABOVE ##
## (except with separate methods for getting i) ##

class Test(object):

    _i = 3

    def get_i(self):
        return type(self)._i

    i = property(get_i)

现在尝试设置实例i属性会返回一个AttributeError

x = Test()
assert x.i == 3  # success
x.i = 12         # ERROR

需要注意的陷阱

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

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会导致错误,因为Testx属性i是两个不同的对象。

许多人会发现这很令人惊讶。然而,事实并非如此。如果我们回到检查我们的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是一种非常优雅的语言,强行将其变成另一种语言是完全没有必要的),请改用Ethan Furman的回答中的代码。

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

元类是一个类的类。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

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

“静态变量”的目录存储在StaticVarMeta.statics属性中。所有属性请求最初都会尝试使用替代解析顺序来解决。我将其称为“静态解析顺序”,或简称“SRO”。这是通过在给定类(或其父类)的“静态变量”集合中寻找所请求的属性来完成的。如果属性不出现在“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

3
我尝试使用你的方式,但遇到了问题,请看看我在这里的问题:http://stackoverflow.com/questions/29329850/get-static-variable-value - Muhammed Refaat
2
@RickTeachey:我想你应该将在类实例“Test”上执行的任何操作(在用它来实例化实例之前)视为元编程的领域。例如,通过执行“Test.i = 0”来更改类行为(这里你只是完全破坏了属性对象)。我想“属性机制”仅在访问类的实例属性时才会启动(除非您使用元类作为中间层来更改底层行为)。顺便说一句,请完成这个答案 :-) - Ole Thomsen Buus
3
@RickTeachey 谢谢 :-) 你最后提供的元类很有意思,但对我来说有点太复杂了。在需要绝对必要的机制的大型框架/应用程序中可能会有用。无论如何,这说明了如果确实需要新的(复杂的)非默认元行为,Python 是可以实现的 :) - Ole Thomsen Buus
3
请查看我的答案,其中提供了一个更简单的元类来完成任务。@OleThomsenBuus:请点击链接查看(https://dev59.com/3nVD5IYBdhLWcg3wKYL-#36216964)。 - Ethan Furman
4
@taper 你说得对,我已经编辑了答案来修复问题(真不敢相信这个错误一直存在那么久!)。抱歉给你带来困惑。 - Rick
显示剩余10条评论

46

你也可以动态地向类添加类变量

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

类的实例可以改变类变量

class X:
  l = []
  def __init__(self):
    self.l.append(1)

print X().l
print X().l

>python test.py
[1]
[1, 1]

4
如果将这个类导入到另一个模块中,新的类变量是否会保留? - zakdances
1
是的。无论从哪个命名空间调用它们,类都有效地成为单例。 - Pedro
1
@Gregory 你说过:“类实例可以改变类变量。” 实际上,这个例子被称为访问而不是修改。修改是由对象自身通过其自己的append()函数完成的。 - Amr ALHOSSARY

28

个人而言,我需要静态方法时通常会使用classmethod。这主要是因为我可以将类作为参数获取。

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

或者使用装饰器

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

对于静态属性..是时候查一下Python定义了..变量总是可以更改的。它们分为可变和不可变两种类型..此外,还有类属性和实例属性..与Java&C ++中的静态属性没有什么真正的相似之处

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


2
变量不可变或可变;对象是可变的。 (但是,一个对象可以尝试以不同程度的成功阻止对其某些属性的赋值。) - Davis Herring
Java和C++使用static(在我看来是不恰当的用词)正如你使用实例与类属性一样。在Java和C++中,类属性/方法是静态的,没有区别,只是在Python中,类方法调用的第一个参数是类。 - Angel O'Sphere

24

需要注意的一点是静态属性和实例属性之间的区别,如下面的示例所示:

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类中声明的每个属性始终都有一个静态内存插槽


23

Python中的静态方法称为classmethod。看一下下面的代码:

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时,会出现错误。这是因为它要求该方法在此类的实例上调用。使用decorator@classmethod将方法myStaticMethod设置为类方法。
只是为了好玩,我们可以通过传递类的实例来调用类上的myInstanceMethod方法,如下所示:
>>> MyClass.myInstanceMethod(MyClass())
output from an instance method

6
静态方法用@staticmethod定义;@classmethod则表示类方法(主要用作备用构造函数,但也可以像静态方法一样接收调用者所属类的引用)。 - ShadowRanger

17

可以有静态类变量,但可能不值得这样做。

这是一个Python 3的概念验证代码--如果任何细节不对,代码可以进行调整以符合你所说的静态变量的含义:


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

还有一些测试:

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

16

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

  • CLASSNAME.var 是静态变量
  • INSTANCENAME.var 不是静态变量。
  • 类内 self.var 不是静态变量。
  • 类成员函数内的 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

缩进有误,代码无法执行。 - Thomas Weller

15

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

from typing import ClassVar
from dataclasses import dataclass

@dataclass
class Test:
    i: ClassVar[int] = 10
    x: int
    y: int
    
    def __repr__(self):
        return f"Test({self.x=}, {self.y=}, {Test.i=})"

使用示例:

> test1 = Test(5, 6)
> test2 = Test(10, 11)

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

网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接