如何获取 python 中 属性 的 setter 方法的属性
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
这是我的装饰器方法,用于装饰其他 class 的 属性 的 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__)
当 运行 this
时出现以下错误
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
的成员持有,而 validate
又替换了 class 中的 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__
方法制作额外的 class 来工作当你调用 inst.property
时,你实际做的是调用 inst.__class__.property.__get__(inst, inst.__class__)
(和 inst.property = value --> __set__
和 del inst.property --> __del__
() 类似。它们中的每一个依次调用 fget
, fset
和 fdel
方法是对您在 class.
中定义的方法的引用
所以我们可以找到你的 __dbattr__
不在 u.Name
上(这是 User.Name.__get__(u, User)
的结果,而是在 User.Name.fset
方法本身!如果你考虑一下(hard),这个有道理,这是你放上去的方法,你没放上结果的值!
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 returns 值时有效,但在您的情况下确实有效。
# 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'}
您可能想知道为什么我使用自定义字符串 class。好吧,如果你使用基本字符串,你就会看到问题
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 classes (collections.UserString
是 python class 字符串包装器,允许随机分配)。
所以最终,你最初想要的是内置字符串是不可能的,但使用自定义 class 可以让你做到。
访问__dbattr__
有点棘手:
首先,您需要获取 属性 对象:
p = u.__class__.__dict__['Name']
然后取回setter函数对象,命名为validate
,定义在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__
。
您需要在装饰器 class 的调用方法 return 编辑的 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
但是请注意,它不是一种方法,因为 class 上有一个 属性,它的实例只会 return 一个字符串,第一个 return 由 getter 编辑。要访问 setter,您必须通过 属性 间接访问 class:
u = User()
u.Name = "usernameofapp"
print(u.Name)
print(User.Name.fset.__dbattr__)
打印:
usernameofapp
{'required': False, 'type': 'string', 'length': 100}
请考虑以下代码
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
这是我的装饰器方法,用于装饰其他 class 的 属性 的 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__)
当 运行 this
时出现以下错误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
的成员持有,而 validate
又替换了 class 中的 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__
方法制作额外的 class 来工作当你调用 inst.property
时,你实际做的是调用 inst.__class__.property.__get__(inst, inst.__class__)
(和 inst.property = value --> __set__
和 del inst.property --> __del__
() 类似。它们中的每一个依次调用 fget
, fset
和 fdel
方法是对您在 class.
所以我们可以找到你的 __dbattr__
不在 u.Name
上(这是 User.Name.__get__(u, User)
的结果,而是在 User.Name.fset
方法本身!如果你考虑一下(hard),这个有道理,这是你放上去的方法,你没放上结果的值!
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 returns 值时有效,但在您的情况下确实有效。
# 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'}
您可能想知道为什么我使用自定义字符串 class。好吧,如果你使用基本字符串,你就会看到问题
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 classes (collections.UserString
是 python class 字符串包装器,允许随机分配)。
所以最终,你最初想要的是内置字符串是不可能的,但使用自定义 class 可以让你做到。
访问__dbattr__
有点棘手:
首先,您需要获取 属性 对象:
p = u.__class__.__dict__['Name']
然后取回setter函数对象,命名为validate
,定义在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__
。
您需要在装饰器 class 的调用方法 return 编辑的 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
但是请注意,它不是一种方法,因为 class 上有一个 属性,它的实例只会 return 一个字符串,第一个 return 由 getter 编辑。要访问 setter,您必须通过 属性 间接访问 class:
u = User()
u.Name = "usernameofapp"
print(u.Name)
print(User.Name.fset.__dbattr__)
打印:
usernameofapp
{'required': False, 'type': 'string', 'length': 100}