W3Cschool
恭喜您成為首批注冊用戶
獲得88經(jīng)驗(yàn)值獎勵
你有很多有用的方法,想使用它們來擴(kuò)展其他類的功能。但是這些類并沒有任何繼承的關(guān)系。因此你不能簡單的將這些方法放入一個基類,然后被其他類繼承。
通常當(dāng)你想自定義類的時候會碰上這些問題。可能是某個庫提供了一些基礎(chǔ)類,你可以利用它們來構(gòu)造你自己的類。
假設(shè)你想擴(kuò)展映射對象,給它們添加日志、唯一性設(shè)置、類型檢查等等功能。下面是一些混入類:
class LoggedMappingMixin:
"""
Add logging to get/set/delete operations for debugging.
"""
__slots__ = () # 混入類都沒有實(shí)例變量,因?yàn)橹苯訉?shí)例化混入類沒有任何意義
def __getitem__(self, key):
print('Getting ' + str(key))
return super().__getitem__(key)
def __setitem__(self, key, value):
print('Setting {} = {!r}'.format(key, value))
return super().__setitem__(key, value)
def __delitem__(self, key):
print('Deleting ' + str(key))
return super().__delitem__(key)
class SetOnceMappingMixin:
'''
Only allow a key to be set once.
'''
__slots__ = ()
def __setitem__(self, key, value):
if key in self:
raise KeyError(str(key) + ' already set')
return super().__setitem__(key, value)
class StringKeysMappingMixin:
'''
Restrict keys to strings only
'''
__slots__ = ()
def __setitem__(self, key, value):
if not isinstance(key, str):
raise TypeError('keys must be strings')
return super().__setitem__(key, value)
這些類單獨(dú)使用起來沒有任何意義,事實(shí)上如果你去實(shí)例化任何一個類,除了產(chǎn)生異常外沒任何作用。它們是用來通過多繼承來和其他映射對象混入使用的。例如:
class LoggedDict(LoggedMappingMixin, dict):
pass
d = LoggedDict()
d['x'] = 23
print(d['x'])
del d['x']
from collections import defaultdict
class SetOnceDefaultDict(SetOnceMappingMixin, defaultdict):
pass
d = SetOnceDefaultDict(list)
d['x'].append(2)
d['x'].append(3)
# d['x'] = 23 # KeyError: 'x already set'
這個例子中,可以看到混入類跟其他已存在的類(比如dict、defaultdict和OrderedDict)結(jié)合起來使用,一個接一個。結(jié)合后就能發(fā)揮正常功效了。
混入類在標(biāo)志庫中很多地方都出現(xiàn)過,通常都是用來像上面那樣擴(kuò)展某些類的功能。它們也是多繼承的一個主要用途。比如,當(dāng)你編寫網(wǎng)絡(luò)代碼時候,你會經(jīng)常使用 socketserver
模塊中的 ThreadingMixIn
來給其他網(wǎng)絡(luò)相關(guān)類增加多線程支持。例如,下面是一個多線程的XML-RPC服務(wù):
from xmlrpc.server import SimpleXMLRPCServer
from socketserver import ThreadingMixIn
class ThreadedXMLRPCServer(ThreadingMixIn, SimpleXMLRPCServer):
pass
同時在一些大型庫和框架中也會發(fā)現(xiàn)混入類的使用,用途同樣是增強(qiáng)已存在的類的功能和一些可選特征。
對于混入類,有幾點(diǎn)需要記住。首先是,混入類不能直接被實(shí)例化使用。其次,混入類沒有自己的狀態(tài)信息,也就是說它們并沒有定義 __init__()
方法,并且沒有實(shí)例屬性。這也是為什么我們在上面明確定義了 __slots__ = ()
。
還有一種實(shí)現(xiàn)混入類的方式就是使用類裝飾器,如下所示:
def LoggedMapping(cls):
"""第二種方式:使用類裝飾器"""
cls_getitem = cls.__getitem__
cls_setitem = cls.__setitem__
cls_delitem = cls.__delitem__
def __getitem__(self, key):
print('Getting ' + str(key))
return cls_getitem(self, key)
def __setitem__(self, key, value):
print('Setting {} = {!r}'.format(key, value))
return cls_setitem(self, key, value)
def __delitem__(self, key):
print('Deleting ' + str(key))
return cls_delitem(self, key)
cls.__getitem__ = __getitem__
cls.__setitem__ = __setitem__
cls.__delitem__ = __delitem__
return cls
@LoggedMapping
class LoggedDict(dict):
pass
這個效果跟之前的是一樣的,而且不再需要使用多繼承了。參考9.12小節(jié)獲取更多類裝飾器的信息,參考8.13小節(jié)查看更多混入類和類裝飾器的例子。
Copyright©2021 w3cschool編程獅|閩ICP備15016281號-3|閩公網(wǎng)安備35020302033924號
違法和不良信息舉報電話:173-0602-2364|舉報郵箱:jubao@eeedong.com
掃描二維碼
下載編程獅App
編程獅公眾號
聯(lián)系方式:
更多建議: