[英]how to get the attribute of setter method of property in python
請考慮以下代碼
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方法設置一些屬性。
好的,這里有三點困惑。 對象標識,描述符協議和動態屬性。
首先,您將__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的描述符協議 ,它定義了屬性的工作方式。
閱讀文章鏈接為一個完整的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
方法,這些方法是對你定義的方法的引用。班級。
所以我們可以找到你的__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__'
與python中的大多數內置類型一樣, str
對象不允許隨機屬性賦值,如自定義python類( collections.UserString
是一個python類包裝器,它允許隨機分配字符串)。
所以最終,如果你最初想要的東西最終是不可能的。 但是,使用自定義字符串類就可以了。
訪問__dbattr__
有點棘手:
首先,您需要獲取屬性對象:
p = u.__class__.__dict__['Name']
然后返回名為validate
的setter函數對象,該對象在DataMember.__call__
定義:
setter_func = p.fset
然后從setter_func
的閉包中獲取底層的User.Name(self , value)
函數對象:
ori_func = setter_func.__closure__[0].cell_contents
現在你可以訪問__dbattr__
:
>>> ori_func.__dbattr__
{'required': False, 'type': 'string', 'length': 100}
但這有用嗎? 我不知道。 也許你可以在DataMember.__call__
返回的validate
函數對象上設置__dbattr__
,正如其他答案所指出的那樣。
您需要在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
但是請注意,它不是一個方法,因為類上有一個屬性,它實例只會返回一個字符串,即getter返回的字符串。 要訪問setter,您必須通過屬性間接地執行它,該屬性在類上:
u = User()
u.Name = "usernameofapp"
print(u.Name)
print(User.Name.fset.__dbattr__)
打印:
usernameofapp
{'required': False, 'type': 'string', 'length': 100}
聲明:本站的技術帖子網頁,遵循CC BY-SA 4.0協議,如果您需要轉載,請注明本站網址或者原文地址。任何問題請咨詢:yoyou2525@163.com.