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


当前回答

类变量并允许子类化

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

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

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
    @contextmanager
    def reset_after_test(cls):
        originalfoo = cls.foo
        yield
        cls.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() == 146
        assert s3.times() == 219
        s3.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() == 84
        assert s.times() == 146
        d.updatefoo()
        assert d.times() == 168 #Douglas.Foo was updated
        assert 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 test
        assert s.times() == 146 #and so was Sheldon.foo

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

其他回答

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

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

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

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

或使用装饰器

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

对于静态财产。。是时候查一下python的定义了。。变量始终可以更改。有两种类型是可变的和不可变的。。此外,还有类属性和实例属性。。没有什么东西真正像java&c意义上的静态属性++

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

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

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

@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如何与“static”类变量不同步。这是因为我在t命名空间中重新绑定,这与Test命名空间不同。如果要更改“静态”变量的值,必须在其最初定义的范围(或对象)内更改它。我把“static”放在引号里,因为Python实际上没有C++和Java那样的静态变量。

尽管Python教程没有具体说明静态变量或方法,但它提供了一些关于类和类对象的相关信息。

@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

@数据类定义提供用于定义实例变量和初始化方法__init__()的类级名称。如果要在@dataclass中使用类级变量,则应使用typeing.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)