gpt4 book ai didi

python - 如何在Python中完全模拟其他语言静态类变量的行为

转载 作者:太空宇宙 更新时间:2023-11-04 03:33:14 24 4
gpt4 key购买 nike

众所周知Python does not have "static class variables"在与其他语言相同的意义上。相反,它具有类属性,这些属性相似但又非常不同。

例如,类属性(在类定义中定义的变量)可以被同名的实例属性“覆盖”:

class A():
i = 1

assert A.i == 1
a = A()
assert a.i == 1
a.i = 2 # has A.i been updated to 2 as well?
assert A.i == 2 # ERROR: no, it hasn't
assert a.i == 2 # A.i and a.i are two different variables
del a.i
assert a.i == 1 # If a.i doesn't exist, falls back in A.i

实际上,并没有发生“重写”——类字典只是在属性查找顺序中比实例字典晚出现。

使用 property 装饰器可以部分克服这种行为:

class A():
_i = 1
@property
def i(self):
return type(self)._i
@i.setter
def i(self,value):
type(self)._i = value

现在,属性 将在多个实例之间保持“同步”:

a1 = A()
a2 = A()
a1.i = 2
assert a1.i == a2.i # i attribute remained in sync

但是,这里的“陷阱”是您不能通过类本身访问或设置属性:

assert A.i == a.i # ERROR
type(A.i) # class 'property'
A.i = 10 # if I try to set via the class...
assert a.i == 10 # it appears to have worked!
a.i = 5 # but...
assert A.i == a.i # ERROR! It didn't work.
type(A.i) # int - setting the property via the class actually overwrote it

这个困难可以使用一个完整的描述符来克服(其中 property 只是一种类型):

class MyProperty():
def __init__(self,name):
self.name = name
def __get__(self,inst,cls):
return getattr(cls,self.name)
def __set__(self,inst,value):
setattr(type(inst),self.name,value)
def __delete__(self,inst):
delattr(type(inst),self.name)

class A():
i = MyProperty('i')

现在,通过类和实例获取和设置属性都可以,通过实例删除也可以:

a = A()
A.i = 5 # Can set via the class
assert a.i == A.i
a.i = 10
assert a.i == A.i # Still in sync!
del a.i
assert A.i # Error, as expected
A.i = 2
assert a.i == 2

但是,当你尝试通过类删除时仍然存在问题:

del A.i # The MyProperty descriptor has been deleted
A.i = 2
a.i = 4
assert A.i == a.i # ERROR!

如何在 Python 中实现对“静态变量”的完全模拟?

最佳答案

我在下面展示 (Python 3) 解决方案仅供引用。我不赞同它是一个“好的解决方案”。我怀疑是否真的有必要在 Python 中模拟其他语言的静态变量行为。但是,不管它是否真的有用,创建下面的代码帮助我进一步了解 Python 的工作原理,因此它也可能对其他人有所帮助。

我在下面创建的元类试图模拟其他语言的“静态变量”行为。它非常复杂,但它基本上是通过将普通的 getter、setter 和 deleter 替换为检查所请求的属性是否为“静态变量”的版本来工作的。 “静态变量”的目录存储在 StaticVarMeta.statics 属性中。如果请求的属性不是“静态变量”,类将退回到默认属性获取/设置/删除行为。如果它是一个“静态变量”,它会尝试使用替代解析顺序(我称之为 __sro__,或“静态解析顺序”)来解析属性请求。

我确信有更简单的方法可以完成此任务,并期待看到其他答案。

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 = 1 # regular attribute

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 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, 14, 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 - 如何在Python中完全模拟其他语言静态类变量的行为,我们在Stack Overflow上找到一个类似的问题: https://stackoverflow.com/questions/30245195/

24 4 0
Copyright 2021 - 2024 cfsdn All Rights Reserved 蜀ICP备2022000587号
广告合作:1813099741@qq.com 6ren.com