如何在python中获取属性的setter方法的属性

时间:2018-08-26 10:07:02

标签: python python-3.x python-2.7 python-decorators python-object

请考虑以下代码

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方法设置一些属性。

3 个答案:

答案 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__fgetfset方法,它们是您在类中定义的方法的引用。

因此我们可以不在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}