请考虑下面的代码
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
这是我的装饰方法来装饰其他类的属性的setter方法,我想给这个方法设置一些属性。但它不允许我。
我试过如下
class User(DbObject):
def __init__(self):
super(User , self)
self._username = None
@property
def Name(self):
return self._username
@Name.setter
@DataMember(length=100)
def Name(self , value):
self._username = value
u = User()
u.Name = "usernameofapp"
print(u.Name)
print(u.Name.__dbattr__)
运行时出现以下错误
Traceback (most recent call last):
File "datatypevalidator.py", line 41, in <module>
print(u.Name.__dbattr__)
AttributeError: 'str' object has no attribute '__dbattr__'
我做错了什么,我怎样才能将一些属性设置为 setter 方法。
最佳答案
好吧,这里有三点混淆。对象标识、描述符协议(protocol)和动态属性。
首先,您要分配 __dbattr__
至 func
.
def __call__(self , func):
func.__dbattr__ = self.default # you don't need setattr
def validate(obj , value):
func(obj , value)
return validate
但这是将属性分配给 func
, 然后仅作为 validate
的成员持有这又取代了 func
在类中(这是装饰器最终要做的,用一个函数替换另一个函数)。因此,通过将此数据放在 func
上,我们失去了对它的访问权限(没有一些严重的 hacky __closure__
访问)。相反,我们应该将数据放在 validate
上.
def __call__(self , func):
def validate(obj , value):
# other code
func(obj , value)
validate.__dbattr__ = self.default
return validate
现在,u.Name.__dbattr__
工作?不,您仍然会遇到同样的错误,但数据现在可以访问了。要找到它,我们需要了解python的descriptor protocol它定义了属性如何工作。
阅读链接文章以获得完整的解释,但有效,@property
通过使用 __get__
创建一个额外的类来工作, __set__
和 __del__
当您调用 inst.property
时使用的方法你实际上做的是打电话inst.__class__.property.__get__(inst, inst.__class__)
(与 inst.property = value --> __set__
和 del inst.property --> __del__
() 类似。它们中的每一个依次调用 fget
、fset
和 fdel
方法,这些方法是对您在类中定义的方法的引用。
所以我们可以找到您的 __dbattr__
不在 u.Name
(这是 User.Name.__get__(u, User)
的结果,但在 User.Name.fset
方法本身!如果你仔细想想,这是有道理的。这是你把它放在的方法。你没有'把它放在结果的值上!
User.Name.fset.__dbattr__
Out[223]: {'length': 100, 'required': False, 'type': 'string'}
是的,所以我们可以看到这个数据存在,但它不在我们想要的对象上。我们如何将它放到那个对象上?嗯,其实很简单。
def __call__(self , func):
def validate(obj , value):
# Set the attribute on the *value* we're going to pass to the setter
value.__dbattr__ = self.default
func(obj , value)
return validate
这仅在 setter 最终返回值时有效,但在您的情况下确实如此。
# Using a custom string class (will explain later)
from collections import UserString
u = User()
u.Name = UserString('hello')
u.Name # --> 'hello'
u.Name.__dbattr__ # -->{'length': 100, 'required': False, 'type': 'string'}
您可能想知道为什么我使用自定义字符串类。好吧,如果您使用基本字符串,就会发现问题
u.Name = 'hello'
---------------------------------------------------------------------------
AttributeError Traceback (most recent call last)
<ipython-input-238-1feeee60651f> in <module>()
----> 1 u.Name = 'hello'
<ipython-input-232-8529bc6984c8> in validate(obj, value)
6
7 def validate(obj , value):
----> 8 value.__dbattr__ = self.default
9 func(obj , value)
10 return validate
AttributeError: 'str' object has no attribute '__dbattr__'
str
像 python 中的大多数内置类型一样,对象不允许像自定义 python 类那样进行随机属性分配(collections.UserString
是一个 python 类包装器,它允许随机分配字符串)。
因此最终,您最初想要的东西使用内置字符串是不可能的,但使用自定义类可以让您做到。
关于python - 如何在python中获取property的setter方法的属性,我们在Stack Overflow上找到一个类似的问题: https://stackoverflow.com/questions/52025193/