2014-02-07 28 views
5

在Python 3整潔的方式來獲得描述符對象

class A(object): 
    attr = SomeDescriptor() 
    ... 
    def somewhere(self): 
     # need to check is type of self.attr is SomeDescriptor() 
     desc = self.__class__.__dict__[attr_name] 
     return isinstance(desc, SomeDescriptor) 

有沒有更好的方式來做到這一點?我不喜歡這個self.__class__.__dict__東西

+0

'type(self).__ dict __ ['attr']''怎麼樣? –

回答

8

A.attr導致Python來調用SomeDescriptor().__get__(None, A)所以如果你有SomeDescriptor.__get__返回selfinstNone,然後A.attr將返回描述:

class SomeDescriptor(): 
    def __get__(self, inst, instcls): 
     if inst is None: 
      # instance attribute accessed on class, return self 
      return self 

然後你

desc = type(self).attr 

如果訪問描述符該屬性的名稱只作爲字符串被知道,attr_name,那麼您將使用

desc = getattr(type(self), attr_name) 

這個工程即使self子類的A的實例,而

desc = self.__class__.__dict__[attr_name] 

如果selfA實例只會工作。


class SomeDescriptor(): 
    def __get__(self, inst, instcls): 
     if inst is None: 
      # instance attribute accessed on class, return self 
      return self 
     return 4 

class A(): 
    attr = SomeDescriptor() 
    def somewhere(self): 
     attr_name = 'attr' 
     desc = getattr(type(self), attr_name) 
     # desc = self.__class__.__dict__[attr_name] # b.somewhere() would raise KeyError 
     return isinstance(desc, SomeDescriptor) 

這說明A.attr返回描述符,並a.somewhere()按預期工作:

a = A() 
print(A.attr) 
# <__main__.SomeDescriptor object at 0xb7395fcc>  
print(a.attr) 
# 4  
print(a.somewhere()) 
# True 

這說明它的工作原理爲A子了。如果您取消註釋 desc = self.__class__.__dict__[attr_name],你會看到 b.somewhere()拋出一個KeyError:

class B(A): pass 
b = B() 
print(B.attr) 
# <__main__.SomeDescriptor object at 0xb7395fcc> 
print(b.attr) 
# 4  
print(b.somewhere()) 
# True 

順便說一句,即使你沒有完全控制SomeDescriptor的定義,你仍然可以把它包裝它返回一個描述符selfinst是無:

def wrapper(Desc): 
    class Wrapper(Desc): 
     def __get__(self, inst, instcls): 
      if inst is None: return self 
      return super().__get__(inst, instcls) 
    return Wrapper 

class A(): 
    attr = wrapper(SomeDescriptor)() 
    def somewhere(self): 
     desc = type(self).attr 
     # desc = self.__class__.__dict__[attr_name] # b.somewhere() would raise KeyError 
     return isinstance(desc, SomeDescriptor) 

所以沒有必要使用

desc = self.__class__.__dict__[attr_name] 

desc = vars(type(self))['attr'] 

從相同問題的困擾。

+0

這將獲得實例屬性,如果它們與類的屬性不同,對吧?否則,我比其他方法更喜歡'getattr'。 – 2rs2ts

+0

請詳細說明。我不明白你在考慮什麼樣的情況。 – unutbu

+0

'a = A(); a.attr = 5; getattr(類型(a),'attr',None)'。那不是'5'而不是'4'? – 2rs2ts

8

是的,要訪問類的描述符,阻止descriptor.__get__被調用的唯一方法是通過類__dict__

你可以使用type(self)訪問當前類,並vars()訪問__dict__更符合API-方式:

desc = vars(type(self))['attr'] 

如果您的描述是完全自定義的,你總是可以從SomeDescriptor.__get__()返回self如果實例參數是None,這是在您直接在類上訪問描述符時發生的。您可以刪除vars()電話,直奔:

desc = type(self).attr 

property()描述對象正是這樣做的。