繁体   English   中英

如何在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

这是我的装饰器方法来装饰其他类的属性的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__ ()。其中​​每一个依次调用fgetfsetfdel方法,这些方法是对你定义的方法的引用。班级。

所以我们可以找到你的__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.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM