python静态变量静态函数 - python静态局部变量




静态类变量可能吗? (11)

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

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

>>> def SomeFactory(some_var=None):
...     class SomeClass(object):
...         nonlocal some_var
...         def print():
...             print(some_var)
...     return SomeClass
... 
>>> SomeFactory(some_var="hello world").print()
hello world

是否有可能在Python中有静态类变量或方法? 需要什么语法才能做到这一点?


静态和类方法

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

class 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 在他的回答中指出的那样 ,问题是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

这是因为行xi = 12已将新的实例属性i添加到x而不是更改Test class i属性的值。

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

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 == xi会产生一个错误,因为Testxi属性是两个不同的对象。

许多人会发现这令人惊讶。 但是,它不应该。 如果我们回顾并检查我们的Test类定义(第二个版本),我们注意到这一行:

    i = property(get_i) 

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

如果您发现上述混淆,您很可能仍然从其他语言(例如Java或c ++)的角度思考它。 您应该研究property对象,关于返回Python属性的顺序,描述符协议和方法解析顺序(MRO)。

我为下面的'gotcha'提出了一个解决方案; 但是我会建议 - 非常努力 - 你不会尝试做类似于以下的事情,直到 - 至少 - 你彻底明白为什么assert Test.i = xi会导致错误。

REAL,ACTUAL静态变量 - Test.i == xi

我只提供下面的(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

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

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

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

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

制作一个变量static通过禁止变量名称指向任何对象,而不是它当前指向的对象来进一步进行。 (注意:这是一个通用的软件概念,并非特定于Python;请参阅其他人的帖子以获取有关在Python中实现静态的信息)。


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


关于这个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

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


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

  • 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

您也可以将类变量随时添加到类中

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

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

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的类。

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

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

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

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

这个脚本结果:

False
False
False
False
False
True
True
True
True
True

有可能有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

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

>>> 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推荐使用静态方法的类方法,因为该方法接收类类型作为第一个参数,但我仍然对这种方法相对于静态方法的优点有些模糊。 如果你也是,那么它可能并不重要。





class-variables