【发布时间】:2018-09-17 06:02:06
【问题描述】:
我有实现描述符协议的用户定义类。描述符被分配给客户端类的属性。目前描述符使用通用变量名,即描述符的类名和键。
我希望根据客户端类上的名称动态定义属性。我知道我需要为此使用元类,所以这是我尝试过的:
class MyDescriptor(object):
__counter = 0
def __init__(self, field=None):
self.field = field or '{}_{}'.format(self.__class__.__name__, self.__counter)
def __get__(self, obj, owner):
print 'getter'
return getattr(obj, self.field)
def __set__(self, obj, val):
print 'setter'
setattr(obj, self.field, val)
class MyMeta(type):
def __new__(mcl, name, bases, nspc):
for k,v in nspc.items():
if isinstance(v, MyDescriptor):
nspc[k] = MyDescriptor(field=k)
return super(MyMeta, mcl).__new__(mcl, name, bases, nspc)
class User(object):
__metaclass__ = MyMeta
desc = MyDescriptor()
def __init__(self, desc):
self.desc = desc
现在这似乎可行,如果我检查我的 User 类的 dict 属性,我会看到 MyDescriptor 对象的字段值具有值“desc”:
>>> User.__dict__['desc'].__dict__
{'field': 'desc'}
但是当我创建一个User 的新对象时,我最终会得到一个递归循环,它会打印“setter”并以异常结束。
>>> User('x')
setter
setter
setter
setter
setter
...
为什么会这样?为什么在我尝试初始化对象时会递归调用__set__ 方法?
如何根据客户端类中的属性名称为 Descriptor 对象分配动态值? (在上面的示例中,我使用了 desc,但假设我还有其他内容,例如名称、位置等。)
【问题讨论】:
-
如果你设置
field等于描述符的名字,setattr(obj, self.field, val)会再次调用描述符。字段名称必须不同于描述符的名称。使用self.field = '_' + field或其他东西。 -
非常感谢@Aran-Fey,这解决了我的问题 :-) 如果您将此作为答案,我会接受它
-
呃,我不知道。对我来说看起来像是一个简单的疏忽,无论如何我们可能有十几个重复的这个问题。我认为我不能证明将其发布为答案。
标签: python python-2.7 metaclass descriptor