然而,实现"static variables"(好吧,可变的静态变量,无论如何,如果这不是一个矛盾......)并不是那么简单 . 作为millerdev pointed out in his answer,问题是Python的类属性不是真正的"static variables" . 考虑:
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 属性的值 .
部分预期的静态变量行为,即多个实例之间的属性同步(但 not 与类本身;请参阅下面的"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
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)
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
14
您还可以动态地将类变量添加到类中
>>> 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]
24
@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"类变量不同步 . 这是因为 i 在 t 名称空间内重新绑定,这与 Test 名称空间不同 . 如果要更改"static"变量的值,则必须在最初定义它的范围(或对象)内更改它 . 我把"static"放在引号中,因为Python在C和Java的意义上并没有真正的静态变量 .
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 the the same as Test.i = arg1
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')
here object (x) alone increment the counter variable
from 0 to 1 by not object y. But result it as "static counter"
5
您还可以使用元类强制使用静态类 .
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 .
160
我发现的最好方法是使用另一个类 . 您可以创建一个对象,然后在其他对象上使用它 .
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()
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
16 回答
静态和类方法
正如其他答案所指出的那样,使用内置装饰器可以轻松完成静态和类方法:
像往常一样,
MyMethod()
的第一个参数绑定到类实例对象 . 相反,MyClassMethod()
的第一个参数绑定到类对象本身(例如,在这种情况下,Test
) . 对于MyStaticMethod()
,没有绑定任何参数,并且根本没有参数是可选的 .“静态变量”
然而,实现"static variables"(好吧,可变的静态变量,无论如何,如果这不是一个矛盾......)并不是那么简单 . 作为millerdev pointed out in his answer,问题是Python的类属性不是真正的"static variables" . 考虑:
这是因为
x.i = 12
行已将新实例属性i
添加到x
,而不是更改Test
类i
属性的值 .部分预期的静态变量行为,即多个实例之间的属性同步(但 not 与类本身;请参阅下面的"gotcha"),可以通过将class属性转换为属性来实现:
现在你可以这样做:
现在,静态变量将在所有类实例之间保持同步 .
(注意:也就是说,除非一个类实例决定定义自己的
_i
版本!但如果有人决定这样做,他们应该得到他们得到的,不是吗?)请注意,从技术上讲,
i
仍然不是'static variable';它是一个property
,它是一种特殊类型的描述符 . 但是,property
行为现在等效于在所有类实例中同步的(可变)静态变量 .不可变“静态变量”
对于不可变的静态变量行为,只需省略
property
setter:现在尝试设置实例
i
属性将返回AttributeError
:有一点需要意识到
请注意,上述方法仅适用于您的类的实例 - 它们在使用类本身时将会起作用 . 例如:
assert Test.i == x.i
行产生错误,因为Test
和x
的i
属性是两个不同的对象 .很多人会发现这令人惊讶 . 但是,它不应该 . 如果我们返回并检查我们的
Test
类定义(第二个版本),我们会注意到这一行:显然,
Test
的成员i
必须是property
对象,它是从property
函数返回的对象类型 .如果您发现上述令人困惑,您很可能仍会从其他语言(例如Java或c)的角度考虑它 . 您应该研究
property
对象,关于返回Python属性的顺序,描述符协议和方法解析顺序(MRO) .我提出了上述'gotcha'的解决方案;但是我会建议 - 极力 - 你不要尝试做类似下面的事情,直到 - 至少 - 你完全理解为什么
assert Test.i = x.i
会导致错误 .REAL,ACTUAL静态变量 - Test.i == x.i
我在下面提供(Python 3)解决方案仅供参考 . 我并不赞同它是一个“好的解决方案” . 我怀疑是否真的需要在Python中模拟其他语言的静态变量行为 . 但是,无论它是否真的有用,下面的内容都应该有助于进一步了解Python的工作原理 .
更新:这次尝试 is really pretty awful ;如果你坚持做这样的事情(暗示:请不要; Python是一种非常优雅的语言,而且它只是不需要表现得像另一种语言),请使用Ethan Furman's answer中的代码 .
Emulating static variable behavior of other languages using a metaclass
元类是类的类 . Python中所有类的默认元类(即我认为的Python 2.3之后的"new style"类)是
type
. 例如:但是,您可以像这样定义自己的元类:
并将其应用到您自己的类中(仅限Python 3):
下面是我创建的元类,它试图模仿其他语言的“静态变量”行为 . 它基本上可以通过用版本替换默认的getter,setter和deleter来工作,这些版本检查所请求的属性是否是“静态变量” .
"static variables"的目录存储在
StaticVarMeta.statics
属性中 . 最初尝试使用替代分辨率顺序来解析所有属性请求 . 我把它称为"static resolution order",或"SRO" . 这是通过在给定类(或其父类)的"static variables"集中查找所请求的属性来完成的 . 如果该属性未出现在"SRO"中,则该类将回退到默认属性get / set / delete行为(即"MRO") .您还可以动态地将类变量添加到类中
类实例可以更改类变量
@Blair Conrad说在类定义中声明的静态变量,但不在方法内部是类或“静态”变量:
这里有一些问题 . 继续上面的例子:
注意直接在
t
上设置属性i
时,实例变量t.i
如何与"static"类变量不同步 . 这是因为i
在t
名称空间内重新绑定,这与Test
名称空间不同 . 如果要更改"static"变量的值,则必须在最初定义它的范围(或对象)内更改它 . 我把"static"放在引号中,因为Python在C和Java的意义上并没有真正的静态变量 .虽然它没有说明有关静态变量或方法的任何内容,但Python tutorial在classes and class objects上有一些相关信息 .
@Steve Johnson也回答了有关静态方法的问题,也在Python Library Reference的“内置函数”中进行了介绍 .
@beid还提到了classmethod,类似于staticmethod . classmethod的第一个参数是类对象 . 例:
有可能有
static
类变量,但可能不值得努力 .这是用Python 3编写的概念验证 - 如果任何确切的细节都是错误的,可以调整代码以匹配
static variable
的任何意思:并在使用中:
和一些测试:
绝对是的,Python本身没有明确的静态数据成员,但我们可以这样做
产量
说明
您还可以使用元类强制使用静态类 .
然后,每当你偶然尝试初始化 MyClass 时,你会得到一个StaticClassError .
我发现的最好方法是使用另一个类 . 您可以创建一个对象,然后在其他对象上使用它 .
通过上面的例子,我创建了一个名为
staticFlag
的类 .该类应该呈现静态var
__success
(私有静态Var) .tryIt
类代表我们需要使用的常规类 .现在我为一个标志(
staticFlag
)创建了一个对象 . 此标志将作为对所有常规对象的引用发送 .所有这些对象都被添加到列表
tryArr
中 .此脚本结果:
我个人在需要静态方法时会使用classmethod . 主要是因为我把 class 作为一个论点 .
或使用装饰
对于静态属性..它查找一些python定义的时间..变量总是可以改变 . 它们有两种类型,它们是可变的和不可变的 . 此外,还有类属性和实例属性 . 在java和c的意义上,没有什么比静态属性更像
为什么在pythonic意义上使用静态方法,如果它对类没有任何关系!如果我是你,我要么使用classmethod,要么定义独立于类的方法 .
为了避免任何潜在的混淆,我想对比静态变量和不可变对象 .
一些原始对象类型,如整数,浮点数,字符串和操作,在Python中是不可变的 . 这意味着如果给定名称引用的对象是上述对象类型之一,则该对象不能更改 . 可以将名称重新分配给其他对象,但不能更改对象本身 .
通过禁止变量名指向任何对象但它当前指向的对象,使变量静态更进一步 . (注意:这是一般软件概念,并非特定于Python;有关在Python中实现静态的信息,请参阅其他人的帖子) .
python中的静态方法称为classmethod s . 看看下面的代码
请注意,当我们调用方法myInstanceMethod时,我们会收到错误 . 这是因为它要求在此类的实例上调用该方法 . 方法myStaticMethod使用decorator @classmethod设置为classmethod .
只是为了踢和傻笑,我们可以通过传入类的实例来调用类上的myInstanceMethod,如下所示:
关于这个answer,对于常量静态变量,可以使用描述符 . 这是一个例子:
导致 ...
如果静静地忽略设置值(上面的
pass
)不是你的事情,你总是可以引发异常 . 如果您正在寻找C,Java样式的静态类变量:有关描述符的更多信息,请查看this answer和官方文档HOWTO .
在类定义中声明但在方法内部未声明的变量是类或静态变量:
正如@ millerdev指出的那样,这会创建一个类级
i
变量,但这与任何实例级i
变量不同,所以你可以拥有这与C和Java不同,但与C#没有什么不同,在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下 .
@beidy推荐classmethod s over staticmethod,因为该方法然后接收类类型作为第一个参数,但是我很重要 .
关于Python的属性查找的一个非常有趣的观点是它可以用于创建“virtual变量”:
通常在创建它们之后不会对它们进行任何赋值 . 请注意,查找使用
self
,因为尽管label
在未与特定实例关联的意义上是静态的,但该值仍取决于(实例的类) .在任何成员方法之外定义某个成员变量时,该变量可以是静态变量,也可以是非静态变量,具体取决于变量的表达方式 .
CLASSNAME.var是静态变量
INSTANCENAME.var不是静态变量 .
类中的
self.var不是静态变量 .
未定义类成员函数内的
var .
例如:
结果是
类工厂python3.6中的静态变量
对于使用带有 python3.6 的类工厂的任何人,请使用
nonlocal
关键字将其添加到正在创建的类的范围/上下文中,如下所示:有关静态属性和实例属性的一个特别注意事项,如下例所示:
这意味着在将值赋给instance属性之前,如果我们尝试通过'instance访问属性,则使用静态值 . Each property declared in python class always has a static slot in memory .