W3Cschool
恭喜您成為首批注冊(cè)用戶(hù)
獲得88經(jīng)驗(yàn)值獎(jiǎng)勵(lì)
你想將某個(gè)實(shí)例的屬性訪(fǎng)問(wèn)代理到內(nèi)部另一個(gè)實(shí)例中去,目的可能是作為繼承的一個(gè)替代方法或者實(shí)現(xiàn)代理模式。
簡(jiǎn)單來(lái)說(shuō),代理是一種編程模式,它將某個(gè)操作轉(zhuǎn)移給另外一個(gè)對(duì)象來(lái)實(shí)現(xiàn)。最簡(jiǎn)單的形式可能是像下面這樣:
class A:
def spam(self, x):
pass
def foo(self):
pass
class B1:
"""簡(jiǎn)單的代理"""
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
如果僅僅就兩個(gè)方法需要代理,那么像這樣寫(xiě)就足夠了。但是,如果有大量的方法需要代理,那么使用 __getattr__()
方法或許或更好些:
class B2:
"""使用__getattr__的代理,代理方法比較多時(shí)候"""
def __init__(self):
self._a = A()
def bar(self):
pass
# Expose all of the methods defined on class A
def __getattr__(self, name):
"""這個(gè)方法在訪(fǎng)問(wèn)的attribute不存在的時(shí)候被調(diào)用
the __getattr__() method is actually a fallback method
that only gets called when an attribute is not found"""
return getattr(self._a, name)
__getattr__
方法是在訪(fǎng)問(wèn)attribute不存在的時(shí)候被調(diào)用,使用演示:
b = B()
b.bar() # Calls B.bar() (exists on B)
b.spam(42) # Calls B.__getattr__('spam') and delegates to A.spam
另外一個(gè)代理例子是實(shí)現(xiàn)代理模式,例如:
# A proxy class that wraps around another object, but
# exposes its public attributes
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)
使用這個(gè)代理類(lèi)時(shí),你只需要用它來(lái)包裝下其他類(lèi)即可:
class Spam:
def __init__(self, x):
self.x = x
def bar(self, y):
print('Spam.bar:', self.x, y)
# Create an instance
s = Spam(2)
# Create a proxy around it
p = Proxy(s)
# Access the proxy
print(p.x) # Outputs 2
p.bar(3) # Outputs "Spam.bar: 2 3"
p.x = 37 # Changes s.x to 37
通過(guò)自定義屬性訪(fǎng)問(wèn)方法,你可以用不同方式自定義代理類(lèi)行為(比如加入日志功能、只讀訪(fǎng)問(wèn)等)。
代理類(lèi)有時(shí)候可以作為繼承的替代方案。例如,一個(gè)簡(jiǎn)單的繼承如下:
class A:
def spam(self, x):
print('A.spam', x)
def foo(self):
print('A.foo')
class B(A):
def spam(self, x):
print('B.spam')
super().spam(x)
def bar(self):
print('B.bar')
使用代理的話(huà),就是下面這樣:
class A:
def spam(self, x):
print('A.spam', x)
def foo(self):
print('A.foo')
class B:
def __init__(self):
self._a = A()
def spam(self, x):
print('B.spam', x)
self._a.spam(x)
def bar(self):
print('B.bar')
def __getattr__(self, name):
return getattr(self._a, name)
當(dāng)實(shí)現(xiàn)代理模式時(shí),還有些細(xì)節(jié)需要注意。首先,__getattr__()
實(shí)際是一個(gè)后備方法,只有在屬性不存在時(shí)才會(huì)調(diào)用。因此,如果代理類(lèi)實(shí)例本身有這個(gè)屬性的話(huà),那么不會(huì)觸發(fā)這個(gè)方法的。另外,__setattr__()
和 __delattr__()
需要額外的魔法來(lái)區(qū)分代理實(shí)例和被代理實(shí)例 _obj
的屬性。一個(gè)通常的約定是只代理那些不以下劃線(xiàn) _
開(kāi)頭的屬性(代理類(lèi)只暴露被代理類(lèi)的公共屬性)。
還有一點(diǎn)需要注意的是,__getattr__()
對(duì)于大部分以雙下劃線(xiàn)(__)開(kāi)始和結(jié)尾的屬性并不適用。比如,考慮如下的類(lèi):
class ListLike:
"""__getattr__對(duì)于雙下劃線(xiàn)開(kāi)始和結(jié)尾的方法是不能用的,需要一個(gè)個(gè)去重定義"""
def __init__(self):
self._items = []
def __getattr__(self, name):
return getattr(self._items, name)
如果是創(chuàng)建一個(gè)ListLike對(duì)象,會(huì)發(fā)現(xiàn)它支持普通的列表方法,如append()和insert(),但是卻不支持len()、元素查找等。例如:
>>> 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
>>>
為了讓它支持這些方法,你必須手動(dòng)的實(shí)現(xiàn)這些方法代理:
class ListLike:
"""__getattr__對(duì)于雙下劃線(xiàn)開(kāi)始和結(jié)尾的方法是不能用的,需要一個(gè)個(gè)去重定義"""
def __init__(self):
self._items = []
def __getattr__(self, name):
return getattr(self._items, name)
# Added special methods to support certain list operations
def __len__(self):
return len(self._items)
def __getitem__(self, index):
return self._items[index]
def __setitem__(self, index, value):
self._items[index] = value
def __delitem__(self, index):
del self._items[index]
11.8小節(jié)還有一個(gè)在遠(yuǎn)程方法調(diào)用環(huán)境中使用代理的例子。
Copyright©2021 w3cschool編程獅|閩ICP備15016281號(hào)-3|閩公網(wǎng)安備35020302033924號(hào)
違法和不良信息舉報(bào)電話(huà):173-0602-2364|舉報(bào)郵箱:jubao@eeedong.com
掃描二維碼
下載編程獅App
編程獅公眾號(hào)
聯(lián)系方式:
更多建議: