Class 实例是否可以在不使用_init__;的情况下获得自己的类级可变默认值副本?
我正在编写一个元类来做一些很酷的事情,它的部分处理是在创建类时检查某些属性是否存在。其中一些是可变的,通常会在Class 实例是否可以在不使用_init__;的情况下获得自己的类级可变默认值副本?,class,python-3.x,default-value,mutable,metaclass,Class,Python 3.x,Default Value,Mutable,Metaclass,我正在编写一个元类来做一些很酷的事情,它的部分处理是在创建类时检查某些属性是否存在。其中一些是可变的,通常会在\uuuuu init\uuuuu中设置,但由于\uuuuuu init\uuuuu在创建实例之前不会运行,因此元类不会知道将创建属性,并引发错误。我可以这样做: class Test(meta=Meta): mutable = None def __init__(self): self.mutable = list() class Test(metac
\uuuuu init\uuuuu
中设置,但由于\uuuuuu init\uuuuu
在创建实例之前不会运行,因此元类不会知道将创建属性,并引发错误。我可以这样做:
class Test(meta=Meta):
mutable = None
def __init__(self):
self.mutable = list()
class Test(metaclass=Meta):
mutable = list()
但这种方法有几个问题:
- 它强制创建与实例属性类型不同的类属性
仍然必须对class属性进行阴影处理,并且\uuuu init\uuuu
没有检查这一点Meta
- 如果
没有对class属性进行阴影处理,我仍然会出现错误(例如\uuuu init\uuuuu
),而试图避免此类错误是AttributeError:'NoneType'对象没有属性“append”
功能的一部分Meta
- 最后但并非最不重要的一点是,它是干的
class Test(meta=Meta):
mutable = None
def __init__(self):
self.mutable = list()
class Test(metaclass=Meta):
mutable = list()
但每个实例都会有自己的mutable
副本
设计目标是:
- 该属性必须存在于类中(类型无关紧要——它不被选中)
- 在首次使用该属性之前的某个时间点,会在实例中放置该属性的副本
t1 = Test()
t2 = Test()
t1.mutable.append('one')
t2.mutable.append('two')
t1.mutable # prints ['one']
t2.mutable # prints ['two']
关于如何做到这一点,有什么想法吗?至少有三种方法:
list
,dict
,set
,等等)中的一个,用描述符替换该属性,该描述符将在第一次访问时激活,并用可变表的新副本更新实例\uuuu init\uuu
方法,该方法在运行时:
- 调用原始的
\uuuu init\uuuu
- 然后检查所需的属性是否存在
class ReplaceMutable:
def __init__(self, func):
self.func = func
def __call__(self):
return self
def __get__(self, instance, owner):
if instance is None:
return self
result = self.func()
setattr(instance, self.func.__name__, result)
return result
和测试类:
class Test:
@ReplaceMutable
def mutable():
return list()
工作原理:
与property
一样,ReplaceMutable
是一个描述符对象,其名称与其要替换的属性相同。与属性
不同,它既不定义也不定义\uuuuuuuuuuuuuuuuuuu
,因此当代码尝试在实例中重新绑定名称(上述测试中的可变
)时,Python将允许它这样做。这与缓存描述符背后的想法相同
ReplaceMutable
是修饰一个函数(使用所需属性的名称),该函数只返回应该初始化的实例级属性(上面示例中的空列表
)。因此,第一次在实例上查找属性时,它将不会在实例字典中找到,Python将激活描述符;描述符然后调用函数来检索初始对象/数据/任何内容,将其存储在实例中,然后返回。下次在该实例上访问该属性时,它将位于实例字典中,这就是将要使用的内容
示例代码:
t1 = Test()
t2 = Test()
t1.mutable.append('one')
t2.mutable.append('two')
print(t1.mutable)
print(t2.mutable)
至少有三种方法可以做到这一点:
让元类检查所有属性,如果它们是可变表(list
,dict
,set
,等等)中的一个,用描述符替换该属性,该描述符将在第一次访问时激活,并用可变表的新副本更新实例
提供(1)中的描述符作为编写类时使用的装饰器
让元类向类添加自己的\uuuu init\uuu
方法,该方法在运行时:
- 调用原始的
\uuuu init\uuuu
- 然后检查所需的属性是否存在
缺点(按方法):
如果类具有应在所有实例中共享的可变属性,则需要额外的工作
类中的属性成为类中的函数(可能是思维扭曲;)
将错误点移到类实例化,而不是类定义
我更喜欢(2)是它给类作者提供了完全的控制,简化了类级可变属性应该在所有实例之间共享的情况,并将错误保留在类定义中
以下是装饰器描述符:
class ReplaceMutable:
def __init__(self, func):
self.func = func
def __call__(self):
return self
def __get__(self, instance, owner):
if instance is None:
return self
result = self.func()
setattr(instance, self.func.__name__, result)
return result
和测试类:
class Test:
@ReplaceMutable
def mutable():
return list()
工作原理:
与property
一样,ReplaceMutable
是一个描述符对象,其名称与其要替换的属性相同。与属性
不同,它既不定义也不定义\uuuuuuuuuuuuuuuuuuu
,因此当代码尝试在实例中重新绑定名称(上述测试中的可变
)时,Python将允许它这样做。这与缓存描述符背后的想法相同
ReplaceMutable
是修饰一个函数(使用所需属性的名称),该函数只返回应该初始化的实例级属性(上面示例中的空列表
)。因此,第一次在实例上查找属性时,在实例字典中找不到该属性