簡體   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