请考虑以下代码
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方法设置一些属性。
答案 0 :(得分:7)
好的,所以这里有三点混乱。对象身份,描述符协议和动态属性。
首先,您要为__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
上,我们将失去对其的访问权限(并且没有严重的__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__
方法制作另外一个类,当您调用{{ 1}}的实际操作是调用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'}
这仅在最终由setter返回值的情况下才有效,但在您的情况下,它将返回。
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
您可能想知道为什么我使用了自定义字符串类。好吧,如果您使用基本字符串,就会发现问题
# 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__'
对象像大多数python中的内置类型一样,不允许像自定义python类一样进行随机属性分配(str
是围绕字符串的python类包装器,它允许随机分配)。 / p>
所以最终,如果您最初想要的东西最终是不可能的。但是,使用自定义字符串类可以做到这一点。
答案 1 :(得分:4)
访问public class Project {
private List<Company> companyList;
//... other fieldsand getters/setters
public List<Company> getSortedCompanies(){
return this.companyList.stream()
.sorted((company1, company2) ->
company1.companyName().compareTo(company2.companyName()))
.collect(toList()));
}
}
有点棘手:
首先,您需要获取属性对象:
__dbattr__
然后取回名为p = u.__class__.__dict__['Name']
的setter函数对象,该对象在validate
内定义:
DataMember.__call__
然后从setter_func = p.fset
的结束处获取基础的User.Name(self , value)
函数对象:
setter_func
现在您可以访问ori_func = setter_func.__closure__[0].cell_contents
:
__dbattr__
但这有用吗?我不知道。正如其他答案所指出的那样,也许您可以只在>>> ori_func.__dbattr__
{'required': False, 'type': 'string', 'length': 100}
返回的__dbattr__
函数对象上设置validate
。
答案 2 :(得分:4)
您需要在装饰器类的调用方法返回的 wrapper 函数上设置属性:
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
def validate(obj , value):
#some other code
func(obj , value)
setattr(validate , "__dbattr__" , self.default)
return validate
class DbObject: pass
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
但是请注意,它不是方法,因为类上有一个属性,所以它的实例将永远只返回一个字符串,即由getter返回的字符串。要访问设置器,您必须通过类中的属性间接进行操作:
u = User()
u.Name = "usernameofapp"
print(u.Name)
print(User.Name.fset.__dbattr__)
打印:
usernameofapp
{'required': False, 'type': 'string', 'length': 100}