代理属性访问通常作为类继承的一种替代,最简单的代理属性访问代码如下:
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 "", line 1, in
TypeError: object of type 'ListLike' has no len()
>>> a[0]
Traceback (most recent call last):
File "", line 1, in
TypeError: 'ListLike' object does not support indexing
>>>
若要实现如len, 索引等方法,则必须实现如__len__,__getitem__,__setitem__,__delitem__等方法。