代理属性访问

代理属性访问通常作为类继承的一种替代,最简单的代理属性访问代码如下:

class A:
    def spam(self, x):
        pass

    def foo(self):
        pass


class B:
    def __init__(self):
        self._a = A()

    def spam(self, x):
        # Delegate to the internal self._a instance
        return self._a.spam(x)

    def foo(self):
        # Delegate to the internal self._a instance
        return self._a.foo()

    def bar(self):
        pass

如果只有几个方法需要代理,可以实现类的__getattr__方法,如下:

class A:
    def spam(self, x):
        pass

    def foo(self):
        pass


class B:
    def __init__(self):
        self._a = A()

    def bar(self):
        pass

    # Expose all of the methods defined on class A
    def __getattr__(self, name):
        return getattr(self._a, name)

>>> b = B()
>>> b.bar()
>>> b.spam(42)

另一个示例:

class Proxy:
    def __init__(self, obj):
        self._obj = obj

    # Delegate attribute lookup to internal obj
    def __getattr__(self, name):
        print('getattr', name)
        return getattr(self._obj, name)

    # Delegate attribute assignment
    def __setattr__(self, name, value):
        if name.startswith('_'):
            super().__setattr__(name, value)
        else:
            print('setattr:', name, value)
            setattr(self._obj, name, value)

    # Delegate attribute deletion
    def __delattr__(self, name):
        if name.startswith('_'):
            super().__delattr__(name)
        else:
            print('delattr:', name)
            delattr(self._obj, name)


class Spam:
    def __init__(self, x):
        self.x = x
    def bar(self, y):
        print('Spam.bar:', self.x, y)


>>> s = Spam(2)
>>> p = Proxy(s)
>>> print(p.x)
getattr x
2
>>> p.bar(3)
getattr bar
Spam.bar: 2 3
>>> p.x = 37
setattr: x 37

常见的约定是,代理只能委派不以下划线开头的属性(即代理仅公开所保留实例的“公共”属性)。
同样重要的是要强调__getattr __()方法通常不适用于大多数以双下划线开头和结尾的特殊方法。如下:

class ListLike:
    def __init__(self):
        self._items = []
    
    def __getattr__(self, name):
        return getattr(self._items, name)


>>> a = ListLike()
>>> a.append(2)
>>> a.insert(0, 1)
>>> a.sort()
>>> len(a)
Traceback (most recent call last):
    File "<stdin>", line 1, in <module>
TypeError: object of type 'ListLike' has no len() 
>>> a[0]
Traceback (most recent call last):
    File "<stdin>", line 1, in <module>
TypeError: 'ListLike' object does not support indexing 
>>>

若要实现如len, 索引等方法,则必须实现如__len__,_getitem_,_setitem_,__delitem__等方法。

posted @ 2020-01-02 22:41  Jeffrey_Yang  阅读(132)  评论(0编辑  收藏  举报