类(静态)变量和方法

2024-11-20 08:44:00
admin
原创
9
摘要:问题描述:如何在 Python 中创建类(即静态)变量或方法?解决方案 1:在类定义内部声明但不在方法内部的变量是类变量或静态变量:>>> class MyClass: ... i = 3 ... >>> MyClass.i 3 正如 @ millerdev指出的...

问题描述:

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


解决方案 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# 差别不大,在 C# 中不能使用对实例的引用来访问静态成员。

看看Python 教程关于类和类对象主题的讨论。

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

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

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

解决方案 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__
{}

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

尽管没有具体提及静态变量或方法,但Python 教程中包含一些有关类和类对象的相关信息。

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

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

上述示例的图示

解决方案 3:

静态方法和类方法

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

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添加了一个新的实例属性,i而不是改变类属性x的值。Test`i`

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

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现在的行为相当于一个在所有类实例之间同步的(可变)静态变量。

不可变的“静态变量”

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

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产生错误,因为和i的属性是两个不同的对象。Test`x`

很多人会对此感到惊讶。然而,其实不应该这样。如果我们回过头来检查我们的Test类定义(第二个版本),我们会注意到这一行:

    i = property(get_i) 

i显然,的成员Test必须是一个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”中,则该类将恢复默认的属性获取/设置/删除行为(即“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

解决方案 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]

解决方案 5:

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

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

或者使用装饰器

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

对于静态属性,是时候查找一些 Python 定义了,变量总是可以改变的,有两种类型,可变和不可变,此外,还有类属性和实例属性,没有什么比 Java 和 C++ 意义上的静态属性更棒的了

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

解决方案 6:

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

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

解决方案 7:

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时,会出现错误。这是因为它要求在此类的实例上调用该方法。使用装饰器@classmethod将方法myStaticMethod设置为类方法。

只是为了好玩,我们可以通过传入类的一个实例来调用该类的myInstanceMethod ,如下所示:

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

解决方案 8:

可以使用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

解决方案 9:

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

  • 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

解决方案 10:

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

解决方案 11:

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

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。

解决方案 12:

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

解决方案 13:

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

整数数据类型的示例:

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

解决方案 14:

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

解决方案 15:

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

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

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

解决方案 16:

当然可以,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"

解决方案 17:

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

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(Private Static Var)。

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

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

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


此脚本结果:

False
False
False
False
False
True
True
True
True
True

解决方案 18:

在 python 中,有很多种方法可以声明Static Methods或。Variables

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 类似,但这次需要在函数中接受一个附加参数(类似于实例变量的自身参数)。例如。

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 文档以掌握 Python 中的 OOP。

解决方案 19:

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

Python 中的一些基本对象类型(如整数、浮点数、字符串和元组)是不可变的。这意味着,如果给定名称所引用的对象属于上述对象类型之一,则该对象不能更改。该名称可以重新分配给其他对象,但对象本身不能更改。

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

解决方案 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

解决方案 21:

所以这可能是一个黑客攻击,但我一直在使用它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:

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

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

解决方案 23:

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

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

解决方案 24:

您可以创建类变量x实例变量name实例方法test1(self)类方法test2(cls)静态方法test3(),如下所示:

class Person:
    x = "Hello" # Class variable

    def __init__(self, name):
        self.name = name # Instance variable
    
    def test1(self): # Instance method
        print("Test1")

    @classmethod
    def test2(cls): # Class method
        print("Test2")
        
    @staticmethod
    def test3(): # Static method
        print("Test3")

我在我的答案中解释了类变量,在我的答案中解释了类方法静态方法,在我的答案中解释了实例方法

解决方案 25:

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

class Student:

    the correct way of static declaration
    i = 10

    incorrect
    self.i = 10

解决方案 26:

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

现在你可以像这样访问它

instance = MyClass()
print(instance.i)

或者

print(MyClass.i)

你必须为这些变量赋值

我尝试

class MyClass:
  i: str

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

i is not attribute of MyClass

解决方案 27:

类变量并允许子类化

假设您不是在寻找真正的静态变量,而是寻找一些 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

相关推荐
  为什么项目管理通常仍然耗时且低效?您是否还在反复更新电子表格、淹没在便利贴中并参加每周更新会议?这确实是耗费时间和精力。借助软件工具的帮助,您可以一目了然地全面了解您的项目。如今,国内外有足够多优秀的项目管理软件可以帮助您掌控每个项目。什么是项目管理软件?项目管理软件是广泛行业用于项目规划、资源分配和调度的软件。它使项...
项目管理软件   601  
  华为IPD与传统研发模式的8大差异在快速变化的商业环境中,产品研发模式的选择直接决定了企业的市场响应速度和竞争力。华为作为全球领先的通信技术解决方案供应商,其成功在很大程度上得益于对产品研发模式的持续创新。华为引入并深度定制的集成产品开发(IPD)体系,相较于传统的研发模式,展现出了显著的差异和优势。本文将详细探讨华为...
IPD流程是谁发明的   7  
  如何通过IPD流程缩短产品上市时间?在快速变化的市场环境中,产品上市时间成为企业竞争力的关键因素之一。集成产品开发(IPD, Integrated Product Development)作为一种先进的产品研发管理方法,通过其结构化的流程设计和跨部门协作机制,显著缩短了产品上市时间,提高了市场响应速度。本文将深入探讨如...
华为IPD流程   9  
  在项目管理领域,IPD(Integrated Product Development,集成产品开发)流程图是连接创意、设计与市场成功的桥梁。它不仅是一个视觉工具,更是一种战略思维方式的体现,帮助团队高效协同,确保产品按时、按质、按量推向市场。尽管IPD流程图可能初看之下显得错综复杂,但只需掌握几个关键点,你便能轻松驾驭...
IPD开发流程管理   8  
  在项目管理领域,集成产品开发(IPD)流程被视为提升产品上市速度、增强团队协作与创新能力的重要工具。然而,尽管IPD流程拥有诸多优势,其实施过程中仍可能遭遇多种挑战,导致项目失败。本文旨在深入探讨八个常见的IPD流程失败原因,并提出相应的解决方法,以帮助项目管理者规避风险,确保项目成功。缺乏明确的项目目标与战略对齐IP...
IPD流程图   8  
热门文章
项目管理软件有哪些?
云禅道AD
禅道项目管理软件

云端的项目管理软件

尊享禅道项目软件收费版功能

无需维护,随时随地协同办公

内置subversion和git源码管理

每天备份,随时转为私有部署

免费试用