[英]how to get the attribute of setter method of property in python
Please consider the below code 请考虑以下代码
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
This is my decorator method to decorate the setter method of property of other class, I want to set some attribute to the method. 这是我的装饰器方法来装饰其他类的属性的setter方法,我想为方法设置一些属性。 but it doesn't allow me.
但它不允许我。
I tried as below 我尝试如下
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__)
Got the below error when ran this 运行此时出现以下错误
Traceback (most recent call last):
File "datatypevalidator.py", line 41, in <module>
print(u.Name.__dbattr__)
AttributeError: 'str' object has no attribute '__dbattr__'
What am I doing wrong, and how can I set some attribute to setter method. 我做错了什么,如何为setter方法设置一些属性。
OK so there are three points of confusion here. 好的,这里有三点困惑。 Object identity, descriptor protocols and dynamic attributes.
对象标识,描述符协议和动态属性。
First off, you are assigning __dbattr__
to func
. 首先,您将
__dbattr__
分配给func
。
def __call__(self , func):
func.__dbattr__ = self.default # you don't need setattr
def validate(obj , value):
func(obj , value)
return validate
But this is assigning the attribute to func
, which is then only held as a member of validate
which in turn replaces func
in the class (this is what decorators do ultimately, replace one function with another). 但这是将属性
func
,然后只将其作为validate
的成员保存,而后者又替换了类中的func
(这是装饰者最终做的,将一个函数替换为另一个函数)。 So by placing this data on func
, we lose access to it (well without some serious hacky __closure__
access). 因此,通过将此数据放在
func
,我们将失去对它的访问权限(没有一些严重的hacky __closure__
访问权限)。 Instead, we should put the data on validate
. 相反,我们应该将数据放在
validate
。
def __call__(self , func):
def validate(obj , value):
# other code
func(obj , value)
validate.__dbattr__ = self.default
return validate
Now, does u.Name.__dbattr__
work? 现在,
u.Name.__dbattr__
工作吗? No, you still get the same error, but the data is now accessible. 不,您仍然会收到相同的错误,但现在可以访问数据了。 To find it, we need to understand python's descriptor protocol which defines how properties work.
要找到它,我们需要了解python的描述符协议 ,它定义了属性的工作方式。
Read the linked article for a full explination but effectively, @property
works by making an additional class with __get__
, __set__
and __del__
methods which when you call inst.property
what you actually do is call inst.__class__.property.__get__(inst, inst.__class__)
(and similar for inst.property = value --> __set__
and del inst.property --> __del__
(). Each of these in turn call the fget
, fset
and fdel
methods which are references to the methods you defined in the class. 阅读文章链接为一个完整的explination但有效,
@property
作品通过与附加类__get__
, __set__
和__del__
方法,当你调用inst.property
你真正要做的是呼吁inst.__class__.property.__get__(inst, inst.__class__)
inst.property = value --> __set__
inst.__class__.property.__get__(inst, inst.__class__)
(类似于inst.property = value --> __set__
和del inst.property --> __del__
()。其中每一个依次调用fget
, fset
和fdel
方法,这些方法是对你定义的方法的引用。班级。
So we can find your __dbattr__
not on u.Name
(which is the result of the User.Name.__get__(u, User)
but on the User.Name.fset
method itself! If you think about it (hard), this makes sense. This is the method you put it on. You didn't put it on the value of the result! 所以我们可以找到你的
__dbattr__
不是u.Name
(这是User.Name.__get__(u, User)
但是在User.Name.fset
方法本身!如果你考虑它(硬),这使得感觉。这是你把它放在上面的方法 。你没有把它放在结果的价值上!
User.Name.fset.__dbattr__
Out[223]: {'length': 100, 'required': False, 'type': 'string'}
Right, so we can see this data exists, but it's not on the object we want. 是的,所以我们可以看到这些数据存在,但它不在我们想要的对象上。 How do we get it onto that object?
我们如何将它放到那个对象上? Well, it's actually quite simple.
嗯,实际上非常简单。
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
This only works if ultimately the setter returns the value, but in your case it does. 这只有在最终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'}
You're probably wondering why I used a custom string class. 您可能想知道为什么我使用自定义字符串类。 Well if you use a basic string, you'll see the issue
好吧,如果您使用基本字符串,您将看到问题
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
objects, like most in-built types in python, do not allow random attribute assignment like custom python classes ( collections.UserString
is a python class wrapper around string that does allow random assignment). 与python中的大多数内置类型一样,
str
对象不允许随机属性赋值,如自定义python类( collections.UserString
是一个python类包装器,它允许随机分配字符串)。
So ultimately, if what you originally wanted was ultimately impossible. 所以最终,如果你最初想要的东西最终是不可能的。 However using a custom string class makes it so.
但是,使用自定义字符串类就可以了。
access __dbattr__
is a bit tricky: 访问
__dbattr__
有点棘手:
first, you need get the property object: 首先,您需要获取属性对象:
p = u.__class__.__dict__['Name']
then get back the setter function object, named validate
, which is defined inside DataMember.__call__
: 然后返回名为
validate
的setter函数对象,该对象在DataMember.__call__
定义:
setter_func = p.fset
then get back the underlying User.Name(self , value)
function object from the closure of setter_func
: 然后从
setter_func
的闭包中获取底层的User.Name(self , value)
函数对象:
ori_func = setter_func.__closure__[0].cell_contents
now you could access __dbattr__
: 现在你可以访问
__dbattr__
:
>>> ori_func.__dbattr__
{'required': False, 'type': 'string', 'length': 100}
but is that useful? 但这有用吗? I don't know.
我不知道。 maybe you could just set
__dbattr__
on the validate
function object that returned by DataMember.__call__
, as other answers have pointed out. 也许你可以在
DataMember.__call__
返回的validate
函数对象上设置__dbattr__
,正如其他答案所指出的那样。
You need to set the attribute on the wrapper function that is being returned by your decorator class's call method: 您需要在decorator类的调用方法返回的包装函数上设置该属性:
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
But note, it is not a method, since there is a property on the class, it instances will only ever return a string, the one returned by the getter. 但是请注意,它不是一个方法,因为类上有一个属性,它实例只会返回一个字符串,即getter返回的字符串。 To access the setter, you have to do it indirectly through the property, which is on the class:
要访问setter,您必须通过属性间接地执行它,该属性在类上:
u = User()
u.Name = "usernameofapp"
print(u.Name)
print(User.Name.fset.__dbattr__)
which prints: 打印:
usernameofapp
{'required': False, 'type': 'string', 'length': 100}
声明:本站的技术帖子网页,遵循CC BY-SA 4.0协议,如果您需要转载,请注明本站网址或者原文地址。任何问题请咨询:yoyou2525@163.com.