Python描述符笔记


看了几篇关于Python描述符的文章:

然后整理了一些笔记:

0. 什么叫描述符

一个描述器是一个有“监听行为”的属性,它的控制被描述器的方法重写。这些方法是 __get__(), __set__(), 和 __delete__().有这些方法的对象叫做描述器。注意,描述符类的实例必须是类属性。

描述器一般用来拦截对实例属性的访问。

1. __get__, __getattr__, __getattribute__的区别

这三个都是用来访问属性的方法。

访问实例的属性,如果存在,调用__getattribute__,如果不存在,调用__getattr__。这两个属于类中的特殊方法。

至于__get__()的使用,看个例子:

#描述符类的实例必须是类属性。
class RevealAccess(object):
    def __init__(self, initval=None, name='var'):
        self.val = initval
        self.name = name

    def __get__(self, obj, objtype):
        print 'Retrieving', self.name
        return self.val

    def __set__(self, obj, val):
        print 'Updating' , self.name
        self.val = val

class MyClass(object):
    x = RevealAccess(10, 'var "x"')
    y = 5

>>> m = MyClass()
>>> m.x
Retrieving var "x"
10
>>> m.x = 20
Updating var "x"
>>> m.x
Retrieving var "x"
20
>>> m.y
5

2. 怎么实现staticmethod和classmethod

classmethod的__get__方法返回了一个instancemethod对象。

staticmethod类的__get__方法应该是直接返回了对象本身。

利用非资料描述器,我们用Python来实现 staticmethod()

class StaticMethod(object):
    "Emulate PyStaticMethod_Type() in Objects/funcobject.c"

    def __init__(self, f):
        self.f = f

    def __get__(self, obj, objtype=None):
        return self.f

用非资料描述器来给出 classmethod() 的一个Python实现:

class ClassMethod(object):
    "Emulate PyClassMethod_Type() in Objects/funcobject.c"

    def __init__(self, f):
        self.f = f

    def __get__(self, obj, klass=None):
        if klass is None:
            klass = type(obj)
        def newfunc(*args):
            return self.f(klass, *args)
        return newfunc

3. property的使用

调用 property() 是建立访问一个属性的描述器的简洁的方式。

class A(object):
    def __init__(self):
        self.__name = 'xxx'
    
    def getname(self):
        return self.__name
        
    name = property(getname)

b = B()
print b.name # xxx

那么,property是怎么实现的呢?实际property就是一个描述符类。

class myProperty(object):  
    def __init__(self,get):  
        self.get=get  
    def __get__(self,object,type):  
        return self.get(object)  

property可以实现类属性的只读。

4. 查找属性的顺序

对于a.x来说,顺序是这样的:a.dict[‘x’] —> type(a).dict[‘x’] —> type(a)的父类。

5. __dict__

__dict__中存放了实例和类的自定义属性。例如实例的属性,类的方法。