上一次我们讲到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
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