请考虑以下代码
class DataMember():
def __init__(self, **args):
self.default = {"required" : False , "type" : "string" , "length": -1}
self.default.update(args)
def __call__(self , func):
#Here I want to set the attribute to method
setattr(func , "__dbattr__" , self.default)
def validate(obj , value):
#some other code
func(obj , value)
return validate
这是我的decorator方法,用来修饰其他类的property的setter方法,我想给这个方法设置一些属性。但这不允许我这么做。在
我试了一下
^{pr2}$运行此时出现以下错误
Traceback (most recent call last):
File "datatypevalidator.py", line 41, in <module>
print(u.Name.__dbattr__)
AttributeError: 'str' object has no attribute '__dbattr__'
我做错了什么?我如何为setter方法设置一些属性。在
访问
__dbattr__
有点棘手:首先,需要获取property对象:
然后取回名为
^{pr2}$validate
的setter函数对象,它在DataMember.__call__
中定义:然后从
setter_func
的闭包取回底层的User.Name(self , value)
函数对象:现在您可以访问
__dbattr__
:但这有用吗?我不知道。也许您可以在
DataMember.__call__
返回的validate
函数对象上设置__dbattr__
,正如其他答案所指出的那样。在好吧,这里有三个混淆点。对象标识、描述符协议和动态属性。在
首先,将
__dbattr__
分配给func
。在但这是将属性赋给}又会替换类中的{}(这就是装饰器最终要做的,用另一个函数替换一个函数)。因此,通过将这些数据放在
^{pr2}$func
,它只作为validate
的一个成员持有,而{func
上,我们就失去了对它的访问权(如果没有一些严重的黑客攻击__closure__
访问)。相反,我们应该把数据放在validate
。在现在,
u.Name.__dbattr__
有用吗?不,您仍然会得到相同的错误,但是数据现在可以访问了。要找到它,我们需要理解python的descriptor protocol,它定义了属性如何工作。在请阅读链接的文章以获得完整的解释,但实际上,}方法创建一个附加的类,当您调用}和{}()类似)。这些方法依次调用}方法,它们是对类中定义的方法的引用。在
@property
的工作原理是使用__get__
、__set__
和{inst.property
时,您实际要做的是调用inst.__class__.property.__get__(inst, inst.__class__)
(和{fget
、fset
和{所以我们可以找到你的
__dbattr__
,而不是u.Name
(这是User.Name.__get__(u, User)
的结果,而是User.Name.fset
方法本身!如果你仔细想想,这是有道理的。这就是你使用的方法。你没有把它放在结果的价值上!在对,所以我们可以看到这个数据存在,但它不在我们想要的对象上。我们怎么把它放到那个物体上?其实很简单。在
这只在setter最终返回值时有效,但在您的例子中确实如此。在
你可能想知道我为什么使用自定义字符串类。如果你用一个基本的字符串,你会发现问题的
str
对象和python中的大多数内置类型一样,不允许像自定义python类那样随机分配属性(collections.UserString
是一个python类包装器,它允许随机赋值)。在因此,最终,您最初想要的东西是不可能的内置字符串,但使用自定义类允许您这样做。在
您需要在decorator类的call方法返回的包装器函数上设置属性:
但是请注意,它不是一个方法,因为类上有一个属性,它的实例只会返回一个字符串,即getter返回的字符串。要访问setter,必须通过类上的属性间接地执行此操作:
^{pr2}$哪个打印:
相关问题 更多 >
编程相关推荐