我正在编写一个元类来做一些很酷的事情,它的部分处理是在创建类时检查某些属性是否存在。其中一些是可变的,通常会在__init__
中设置,但由于__init__
在创建实例之前未运行,因此元类将不知道属性将被创建,并引发错误。我可以做类似的事情:
class Test(meta=Meta):
mutable = None
def __init__(self):
self.mutable = list()
但这种方法有几个问题:
__init__
仍然需要隐藏class属性,而Meta
不会检查__init__
没有影响类属性,我仍会得到错误(例如AttributeError: 'NoneType' object has no attribute 'append'
),并且试图避免此类错误是{{1的函数的一部分}} 我需要的是一种类似的方式:
Meta
但每个实例最终都会有自己的class Test(metaclass=Meta):
mutable = list()
副本。
设计目标是:
所需的样本运行:
mutable
关于如何实现这一目标的任何想法?
答案 0 :(得分:1)
至少有三种方法可以做到这一点:
让元类检查所有属性,如果它们是可变项之一(list
,dict
,set
等,则将属性替换为描述符这将在第一次访问时激活,并使用可变的新副本更新实例。
提供(1)中的描述符作为编写类时使用的装饰器。
__init__
方法添加到运行时的类中:
__init__
下行(按方法):
如果该类具有应在所有实例之间共享的可变属性,则需要额外的工作。
类中的属性成为类中的一个函数(可能是mind-warp;)
我更喜欢(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
是一个描述符对象,其名称与它所替换的属性相同。与property
不同,它不定义__set__
也不定义__delete__
,因此当代码尝试重新绑定实例中的名称(上面的测试中为mutable
)时,Python将允许它这样做。这与缓存描述符背后的想法相同。
ReplaceMutable
正在装饰一个函数(带有所需属性的名称),它只返回应初始化实例级别属性的任何内容(上例中的空list
)。因此,第一次在实例上查找属性时,它将无法在实例字典中找到,Python将激活描述符;然后,描述符调用函数来检索初始对象/数据/任何内容,将其存储在实例中,然后返回它。下次在该实例上访问该属性时,它将在实例字典中,这就是将要使用的内容。
示例代码:
t1 = Test()
t2 = Test()
t1.mutable.append('one')
t2.mutable.append('two')
print(t1.mutable)
print(t2.mutable)