获取描述符对象的简洁方法

时间:2014-02-07 13:46:39

标签: python python-3.x descriptor python-descriptors

在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__的东西

2 个答案:

答案 0 :(得分:13)

A.attr会导致Python调用SomeDescriptor().__get__(None, A),因此当SomeDescriptor.__get__selfinst返回NoneA.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)

即使selfA子类的实例,这也有效,而

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的定义,你仍然可以将它包装在一个描述符中,当self为None时返回inst

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']

遇到同样的问题。

答案 1 :(得分:8)

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

您可以使用type(self)访问当前的类,vars()可以使用更符合API的方式访问__dict__

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

如果描述符完全是自定义的,如果实例参数为self,则始终可以从SomeDescriptor.__get__()返回None,这在您访问描述符时会发生直接上课。您可以放弃vars()来电并直接进入:

desc = type(self).attr

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