Descriptor Review

上一次我们讲到Descriptor是作为类属性出现的,也就是说,对于f.a,这样的属性存取,其实是会被变换为

type(f).__dict__['a'].__get__(f, type(f))

也就是说,对于Descriptor a,他是被存储在类的字典 __dict__中的。而如果我们通过类对象来存取a,其实就变换为

F.__dict__['a'].__get__(None, F)

在实现Descriptor时需要注意的一点就是,如果该Descriptor对象名为foo,那么很显然,我们在对象实例中存取变量时不能再用foo,因为这会造成递归。所以以下代码是错误的:

class AClass(object):
    foo = Foo()
    
class Foo(object):
    def __get__(self, instance, owner):
        return instance.foo

instance.foo会再次去调用Foo.__get__,从而导致递归,最简单的解决方法就是在实例变量前加上下划线_,变成_foo,这样就能解决这个问题。

 因为Descriptor其实是实现__get__, __set__, __delete__中任意一个方法的对象,所以,如果一个Descriptor没有实现__get__,但是我们却以f.a来获取属性a的话,那么此时如果f.__dict__中有属性a,那么就返回这个属性,否则,descriptor对象本身将被返回。所以,如果我们要实现data descriptor,那么我们应该实现__set__, __get__,而不仅仅实现__set__(python定义实现__set__的是data descriptor)。

对于Non-data descriptor,也就是只实现了__get__方法的descriptor,那么如果实例中有属性a的话,将会override __get__的返回值。顾名思义,non-data descriptor应该不关心数据。

class MyProp(object):
    def __get__(self, instance, owner):
        return 1

class Foo(object):
    bar = MyProp()

f = Foo()
print f.bar # return 1
f.__dict__['bar'] = 2
print f.bar # return 2

还有一点我们需要注意,就是如果我们使用property装饰器,而property实现的是data descriptor,所以如果我们只定义了getter,此时由于是data-descriptor,返回值还是会由getter提供。但是如果我们只提供了setter,没有提供getter,将会抛出AttributeError异常。

class Foo(object):
    def bar(self):
        return 1
    bar = property(fset=bar)

f = Foo()
try:
    f.bar
except AttributeError as e:
    print str(e)

Reference:

  [1] Python manual - Implementing Descriptors

你可能感兴趣的:(object,python,Class,reference,getter,Descriptor)