【问题标题】:Are static class variables possible in Python?Python中可以使用静态类变量吗?
【发布时间】:2010-09-09 06:41:37
【问题描述】:

Python 中是否可以有静态类变量或方法?执行此操作需要什么语法?

【问题讨论】:

  • 是的。没有关键字“static”可能会产生误导,但是在类中初始化的任何对象(类中只有一个缩进,而不是在构造函数中)都是静态的。它不依赖于实例化(因为它不是构造函数的一部分)。至于方法,您可以使用 @staticmethod 装饰器来实现。
  • 对类的所有实例都存在的东西使用术语静态,对我来说总是很奇怪
  • @TonySuffolk66 怪罪(我认为)C++,它只是从 C 中挪用了现有的关键字“static”(它表明变量的生命周期超出了声明它的范围)。 C++ 将其扩展为一个变量,其值超出了类的单个实例的“范围”。 Python(更合乎逻辑)简单地称它们为类属性,因为它们是与类本身相关联的属性,而不是类的实例。
  • @chepner static 实际上在 C++ 中意味着几件事(由于非常严格的注释长度,缩写定义)。有文件范围为static 从 C 继承,这意味着“这个变量/函数只能在这个文件中使用”,有一个范围为 static 的类,这意味着“这个方法或字段与类型相关联,而不是与该类型的任何实例相关联” (在 C++ 中很少使用,但在 C#/Java/ObjC 中很常见,例如,我认为这是 OP 所要求的),函数中有局部变量 static,这意味着“此变量的值在函数调用之间保留”。
  • 将开关切换到“意见”,我认为很多时候,C#/Java 中的静态方法是因为语言采取强硬的“无函数”立场,在 C#/Java 中你只能有方法(即,作为类的一部分的函数),Python 没有这个限制(在我看来,这是最好的)。我个人宁愿使用 C++ 的命名空间或从文件 (Python) 导入函数,也不愿无缘无故地创建一个类,而不是为了保存函数。 OOP 有其用途,但有时您只需要一个函数。

标签: python class oop static class-variables


【解决方案1】:

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

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

正如@millerdev 指出的那样,这会创建一个类级别的i 变量,但这不同于任何实例级别的i 变量,因此您可以拥有

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

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

what the Python tutorial has to say on the subject of classes and class objects

@Steve Johnson 已经回答了有关 static methods 的问题,也记录在 "Built-in Functions" in the Python Library Reference 下。

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

@beidy 推荐classmethods 而不是 staticmethod,因为该方法随后接收类类型作为第一个参数,但我对这种方法相对于 staticmethod 的优势仍然有些模糊。如果你也是,那可能没关系。

【讨论】:

  • 我只是在学习 Python,但 @classmethod 相对于 @staticmethod AFAIK 的优势在于,您始终可以获得调用该方法的类的名称,即使它是子类。例如,静态方法缺少此信息,因此它不能调用被覆盖的方法。
  • @theJollySin 常量的 Pythonic 方式是不为常量增长一个类。只要有一些const.pyPI = 3.14,你就可以在任何地方导入它。 from const import PI
  • 这个答案可能会混淆静态变量问题。首先,i = 3 不是 一个静态变量,它是一个类属性,并且由于它不同于实例级属性i 它确实 not 在其他语言中的行为类似于静态变量。请参阅下面的 millerdev's answerYann's answermy answer
  • 所以即使我创建了这个类的数百个实例,也只有一个 i(static variable) 的副本会在内存中?
  • 对于@Dubslow 评论中提到的 Daniel 感兴趣的任何人,它是 millerdev (wayback machine)
【解决方案2】:

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

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

虽然没有具体说明静态变量或方法,但Python tutorial 有一些关于classes and class objects 的相关信息。

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

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

@beid 也提到了classmethod,类似于staticmethod。类方法的第一个参数是类对象。示例:

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

【讨论】:

  • 我建议您稍微扩展一下示例:如果在设置 Test.i=6 之后,您然后实例化一个新对象(例如,u=Test()),则新对象将“继承" 新的类值(例如,ui==6)
  • 保持静态变量同步的一种方法是使它们成为属性:class Test(object):_i = 3@propertydef i(self)return type(self)._i@i.setterdef i(self,val):type(self)._i = val。现在你可以做x = Test()x.i = 12assert x.i == Test.i
  • 所以我可以说所有变量最初都是静态的,然后访问实例会在运行时生成实例变量?
  • 也许这很有趣:如果您在 Test 中定义一个更改 Test.i 的方法,这将同时影响 Test.i 和 t.i 值。
  • @millerdev,就像你提到的 Python 没有 C++ 或 JAVA 那样的静态变量。所以可以说,Test.i 更像是一个类变量而不是一个静态变量?
【解决方案3】:

静态和类方法

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

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(),没有任何参数是绑定的,完全有参数是可选的。

“静态变量”

然而,实现“静态变量”(好吧,可变静态变量,无论如何,如果这在术语上不矛盾的话......)并不那么简单。作为 millerdev pointed out in his answer,问题在于 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 行已将新的实例属性i 添加到x,而不是更改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 行为现在等效于跨所有类实例同步的(可变)静态变量。

不可变的“静态变量”

对于不可变的静态变量行为,只需省略 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 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 行产生错误,因为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 是一种非常优雅的语言,没有必要硬着头皮让它表现得像另一种语言),请改用Ethan Furman's answer 中的代码。

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

元类是类的类。 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”中,则该类将使用默认属性获取/设置/删除行为(即“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

【讨论】:

  • 我尝试使用您的方式,但遇到了问题,请在此处查看我的问题stackoverflow.com/questions/29329850/get-static-variable-value
  • @RickTeachey:我想您通常应该将您在类 InstanceTest 上所做的任何事情(在将其用于实例化实例之前)视为属于元编程领域?例如,您可以通过 Test.i = 0 更改类行为(这里您只需完全销毁属性对象)。我猜“属性机制”只在类实例的属性访问中起作用(除非你使用元类作为中间体来改变底层行为,也许)。顺便说一句,请完成这个答案:-)
  • @RickTeachey 谢谢 :-) 最后你的元类很有趣,但实际上对我来说有点太复杂了。在绝对需要这种机制的大型框架/应用程序中,它可能很有用。无论如何,这说明如果真的需要新的(复杂的)非默认元行为,Python 使之成为可能:)
  • @OleThomsenBuus:检查my answer 以获得更简单的元类来完成这项工作。
  • @taper 你是对的;我已经编辑了解决问题的答案(不敢相信它已经错了这么久!)。很抱歉造成混乱。
【解决方案4】:

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

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

【讨论】:

  • 即使将类导入另一个模块,新的类变量是否会保留?
  • 是的。类实际上是单例,无论您从哪个命名空间调用它们。
  • @Gregory 你说“类实例可以改变类变量”实际上这个例子叫做访问而不是修改。修改是由对象本身通过其自己的 append() 函数完成的。
【解决方案5】:

我个人会在需要静态方法时使用类方法。主要是因为我将类作为参数。

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

或使用装饰器

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

对于静态属性.. 是时候查找一些 python 定义了.. 变量总是可以改变的。它们有可变和不可变两种类型。此外,还有类属性和实例属性。在 java 和 c++ 的意义上,没有什么像静态属性那样

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

【讨论】:

  • 变量不是可变的或不可变的;对象是。 (然而,一个对象可以在不同程度上成功地尝试阻止分配给它的某些属性。)
  • Java 和 C++ 使用静态(错误使用这个词,恕我直言),就像您使用实例与类属性一样。类属性/方法在 Java 和 C++ 中是静态的,没有区别,只是在 Python 中,类方法调用的第一个参数是类。
【解决方案6】:

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

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 使用decorator @classmethod 设置为类方法。

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

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

【讨论】:

  • 嗯...静态方法是用@staticmethod制作的; @classmethod (显然)用于类方法(主要用作替代构造函数,但在紧要关头可以用作静态方法,这些方法恰好接收到对它们被调用的类的引用)。
【解决方案7】:

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

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 类中声明的每个属性在内存中总是有一个静态槽

【讨论】:

    【解决方案8】:

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

    • 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
    

    【讨论】:

    • 缩进被破坏了。这不会执行
    【解决方案9】:

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

    这是一个用 Python 3 编写的概念验证——如果任何确切的细节有误,可以调整代码以匹配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')
    

    还有一些测试:

    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
    

    【讨论】:

      【解决方案10】:

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

      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 以将其变成 Java....
      • Borg idiom 是处理此问题的更好方法。
      • @NedBatchelder 这是一个抽象类,仅用于子类化(和实例化子类)
      • 我希望子类不要使用 super() 来调用其父类的__new__...
      【解决方案11】:

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

      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 在不与特定 实例关联的意义上是静态的,但该值仍然取决于实例的(类)。

      【讨论】:

        【解决方案12】:

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

        【讨论】:

          【解决方案13】:

          是的,绝对可以在 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
          

          静态方法: 与变量类似,静态方法可以通过类名直接访问。无需创建实例。

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

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

          【讨论】:

          • 我认为你所谓的“静态”变量是类变量。即:A类():inner_var = 0 B(A)类:通过A.inner_var = 15 B.inner_var = 30 print ("A:static=" + str(A.inner_var)) print ("B:static= " + str(B.inner_var)) # 输出:# A:static=15 # B:static=30
          【解决方案14】:

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

          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)不是你的事,你总是可以引发异常。如果您正在寻找 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
          

          查看this answer 和官方文档HOWTO 了解有关描述符的更多信息。

          【讨论】:

          • 你也可以只使用@property,这与使用描述符相同,但代码要少得多。
          【解决方案15】:

          绝对是的, 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"
          

          【讨论】:

            【解决方案16】:

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

            整数数据类型示例:

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

            基于refdatatypes库的解决方案:

            from refdatatypes.refint import RefInt
            
            
            class AAA:
                static = RefInt(1)
            
            
            class BBB(AAA):
                pass
            
            
            print(f"refint {AAA.static.value}")  # get 1 correctly
            print(f"refint {BBB.static.value}")  # get 1 correctly
            
            AAA.static.value = 5
            print(f"refint {AAA.static.value}")  # get 5 correctly
            print(f"refint {BBB.static.value}")  # get 5 correctly
            
            BBB.static.value = 6
            print(f"refint {AAA.static.value}")  # get 6 correctly
            print(f"refint {BBB.static.value}")  # get 6 correctly
            
            AAA.static.value = 7
            print(f"refint {AAA.static.value}")  # get 7 correctly
            print(f"refint {BBB.static.value}")  # get 7 correctly
            

            【讨论】:

              【解决方案17】:

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

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

              通过禁止变量名指向除当前指向的对象之外的任何对象,将变量设为静态更进一步。 (注意:这是一个通用的软件概念,并非特定于 Python;有关在 Python 中实现静态的信息,请参阅其他人的帖子)。

              【讨论】:

                【解决方案18】:

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

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

                通过上面的例子,我创建了一个名为staticFlag的类。

                这个类应该呈现静态变量__success(私有静态变量)。

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

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

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


                此脚本结果:

                False
                False
                False
                False
                False
                True
                True
                True
                True
                True
                

                【讨论】:

                  【解决方案19】:

                  总结别人的答案并补充,python中声明静态方法或变量的方式有很多种。

                  1。使用staticmethod() 作为装饰器:

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

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

                  2。使用staticmethod()作为参数函数:

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

                  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。使用classmethod()作为装饰器:

                  @classmethod 对函数的影响与@staticmethod 类似,但是 这一次,需要在函数中接受一个额外的参数(类似于实例变量的 self 参数)。例如。

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

                  4。使用classmethod()作为参数函数:

                  @classmethod 也可以用作参数函数,以防不想修改类定义。例如。

                  class Calculator:
                      def divide(cls, n1, n2, *args):
                          Res = 1
                          for num in args: Res *= num
                          return 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 = 0
                      def __init__(self, digits) -> None:
                          Calculator.num = int(''.join(digits))
                      
                      
                      @staticmethod
                      def multiply(n1, n2, *args):
                          Res = 1
                          for num in args: Res *= num
                          return n1 * n2 * Res
                  
                  
                      def add(n1, n2, *args):
                          return n1 + n2 + sum(args)
                      
                  
                      @classmethod
                      def get_digits(cls, num):
                          digits = list(str(num))
                          calc = cls(digits)
                          return calc.num
                  
                  
                      def divide(cls, n1, n2, *args):
                          Res = 1
                          for num in args: Res *= num
                          return 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))              # 24
                  print(Calculator.add(1, 2, 3, 4))                   # 10
                  print(Calculator.get_digits(314159))                # 314159
                  print(Calculator.divide(15, 3, 5))                  # 1.0
                  print(Calculator.subtract(10, 2, 3, 4))             # 1
                  

                  参考Python Documentation掌握python中的OOP。

                  【讨论】:

                    【解决方案20】:

                    类工厂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
                    

                    【讨论】:

                    • 是的,但在这种情况下,hasattr(SomeClass, 'x')False。我怀疑这就是任何人所说的静态变量的意思。
                    • @RickTeachey 哈哈,看到你的静态变量代码,stackoverflow.com/a/27568860/2026508 +1 互联网先生,我认为 hasattr 不是那样工作的? some_var 也是不可变的,并且是静态定义的,或者不是吗?外部 getter 访问与变量是否为静态有什么关系?我现在有很多问题。有时间很想听听一些答案。
                    • 是的,元类很荒谬。我不确定我是否理解这些问题,但在我看来,上面的some_var 根本不是班级成员。在 Python 中,所有类成员都可以从类外部访问。
                    • nonlocal 关键字“碰撞”了变量的范围。类主体定义的范围独立于它所在的范围——当您说nonlocal some_var 时,这只是创建对另一个命名对象的非本地(阅读:不在类定义范围内)名称引用。因此它不会附加到类定义中,因为它不在类主体范围内。
                    【解决方案21】:

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

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

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

                    【讨论】:

                      【解决方案22】:

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

                      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
                      

                      【讨论】:

                        【解决方案23】:

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

                        # -*- 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)
                        

                        【讨论】:

                          【解决方案24】:

                          这样说,静态变量是在用户定义的类出现时创建的,并且定义一个静态变量应该遵循关键字self,

                          class Student:
                          
                              the correct way of static declaration
                              i = 10
                          
                              incorrect
                              self.i = 10
                          

                          【讨论】:

                            【解决方案25】:

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

                            现在你可以像这样访问它了

                            instance = MyClass()
                            print(instance.i)
                            

                            print(MyClass.i)
                            

                            你必须为这些变量赋值

                            我在尝试

                            class MyClass:
                              i: str
                            

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

                            i is not attribute of MyClass
                            

                            【讨论】:

                              猜你喜欢
                              • 2021-05-02
                              • 1970-01-01
                              • 1970-01-01
                              • 1970-01-01
                              • 1970-01-01
                              • 1970-01-01
                              • 1970-01-01
                              • 1970-01-01
                              相关资源
                              最近更新 更多