你想将某个实例的属性访问代理到内部另一个实例中去,目的可能是作为继承的一个替代方法或者实现代理模式。

  1. class A:
  2. def spam(self, x):
  3. print('This is A spam is:', x)
  4. def foo(self):
  5. print('This is A foo')
  6. class B1:
  7. '''简单代理'''
  8. def __init__(self):
  9. self._a = A()
  10. def spam(self, x):
  11. return self._a.spam(x)
  12. def foo(self):
  13. return self._a.foo()
  14. def bar(self):
  15. pass
  16. class B2:
  17. '''使用 __getattr__ 的代理, 代理方法比较多的时候'''
  18. def __init__(self):
  19. self._a = A()
  20. def bar(self):
  21. print('This is B2 bar')
  22. def __getattr__(self, name):
  23. '''这个方法在访问 attribute 不存在的时候被调用'''
  24. return getattr(self._a, name)
  1. b = B2()
  2. b.bar()
  3. b.spam(42)
  4. b.foo()
  5. This is B2 bar
  6. This is A spam is: 42
  7. This is A foo

1. 代理模式

  1. class Proxy:
  2. def __init__(self, obj):
  3. self._obj = obj
  4. def __getattr__(self, name):
  5. print('getattr', name)
  6. return getattr(self._obj, name)
  7. def __setattr__(self, name, value):
  8. if name.startswith('_'):
  9. super().__setattr__(name, value)
  10. else:
  11. print('setter:', name, value)
  12. setattr(self._obj, name, value)
  13. def __delattr__(self, name):
  14. if name.startswith('_'):
  15. super().__delattr__(name)
  16. else:
  17. print('delattr', name)
  18. delattr(self._obj, name)
  19. class Spam:
  20. def __init__(self, x):
  21. self.x = x
  22. def bar(self, y):
  23. print('Spam.bar', self.x, y)
  1. s = Spam(2)
  2. p = Proxy(s)
  3. print(p.x)
  4. p.bar(3)
  5. p.x = 37
  6. getattr x
  7. 2
  8. getattr bar
  9. Spam.bar 2 3
  10. setter: x 37

2. 继承的替代方案

  1. class A:
  2. def spam(self, x):
  3. print('A.spam', x)
  4. def foo(self):
  5. print('A.foo')
  6. class B:
  7. def __init__(self):
  8. self._a = A()
  9. def spam(self, x):
  10. print('B.spam', x)
  11. self._a.spam(x)
  12. def bar(self):
  13. print('B.bar')
  14. def __getattr__(self, name):
  15. return getattr(self._a, name)
  16. b = B()
  17. b.spam(3)
  18. b.bar()
  19. b.foo()
  20. B.spam 3
  21. A.spam 3
  22. B.bar
  23. A.foo

当实现代理模式时,还有些细节需要注意。 首先,__getattr__() 实际是一个后备方法,只有在属性不存在时才会调用。 因此,如果代理类实例本身有这个属性的话,那么不会触发这个方法的。 另外,__setattr__()__delattr__() 需要额外的魔法来区分代理实例和被代理实例 _obj 的属性。 一个通常的约定是只代理那些不以下划线 _ 开头的属性 (代理类只暴露被代理类的公共属性)。

还有一点需要注意的是,__getattr__() 对于大部分以双下划线 (__) 开始和结尾的属性并不适用。 比如,考虑如下的类:

  1. class ListLike:
  2. """__getattr__对于双下划线开始和结尾的方法是不能用的,需要一个个去重定义"""
  3. def __init__(self):
  4. self._items = []
  5. def __getattr__(self, name):
  6. return getattr(self._items, name)

如果是创建一个 ListLike 对象,会发现它支持普通的列表方法,如 append () 和 insert (), 但是却不支持 len ()、元素查找等。例如:

  1. >>> a = ListLike()
  2. >>> a.append(2)
  3. >>> a.insert(0, 1)
  4. >>> a.sort()
  5. >>> len(a)
  6. Traceback (most recent call last):
  7. File "<stdin>", line 1, in <module>
  8. TypeError: object of type 'ListLike' has no len()
  9. >>> a[0]
  10. Traceback (most recent call last):
  11. File "<stdin>", line 1, in <module>
  12. TypeError: 'ListLike' object does not support indexing
  13. >>>

为了让它支持这些方法,你必须手动的实现这些方法代理:

  1. class ListLike:
  2. """__getattr__对于双下划线开始和结尾的方法是不能用的,需要一个个去重定义"""
  3. def __init__(self):
  4. self._items = []
  5. def __getattr__(self, name):
  6. return getattr(self._items, name)
  7. # Added special methods to support certain list operations
  8. def __len__(self):
  9. return len(self._items)
  10. def __getitem__(self, index):
  11. return self._items[index]
  12. def __setitem__(self, index, value):
  13. self._items[index] = value
  14. def __delitem__(self, index):
  15. del self._items[index]