整洁的方式来获得描述符对象

问题描述:

在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']''怎么样? –

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

是的,要访问类的描述符,阻止descriptor.__get__被调用的唯一方法是通过类__dict__

你可以使用type(self)访问当前类,并vars()访问__dict__更符合API-方式:

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

如果您的描述是完全自定义的,你总是可以从SomeDescriptor.__get__()返回self如果实例参数是None,这是在您直接在类上访问描述符时发生的。您可以删除vars()电话,直奔:

desc = type(self).attr 

property()描述对象正是这样做的。