W3Cschool
恭喜您成為首批注冊用戶
獲得88經驗值獎勵
你有很多有用的方法,想使用它們來擴展其他類的功能。但是這些類并沒有任何繼承的關系。因此你不能簡單的將這些方法放入一個基類,然后被其他類繼承。
通常當你想自定義類的時候會碰上這些問題??赡苁悄硞€庫提供了一些基礎類,你可以利用它們來構造你自己的類。
假設你想擴展映射對象,給它們添加日志、唯一性設置、類型檢查等等功能。下面是一些混入類:
class LoggedMappingMixin:
"""
Add logging to get/set/delete operations for debugging.
"""
__slots__ = () # 混入類都沒有實例變量,因為直接實例化混入類沒有任何意義
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)
這些類單獨使用起來沒有任何意義,事實上如果你去實例化任何一個類,除了產生異常外沒任何作用。它們是用來通過多繼承來和其他映射對象混入使用的。例如:
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)結合起來使用,一個接一個。結合后就能發(fā)揮正常功效了。
混入類在標志庫中很多地方都出現過,通常都是用來像上面那樣擴展某些類的功能。它們也是多繼承的一個主要用途。比如,當你編寫網絡代碼時候,你會經常使用 socketserver
模塊中的 ThreadingMixIn
來給其他網絡相關類增加多線程支持。例如,下面是一個多線程的XML-RPC服務:
from xmlrpc.server import SimpleXMLRPCServer
from socketserver import ThreadingMixIn
class ThreadedXMLRPCServer(ThreadingMixIn, SimpleXMLRPCServer):
pass
同時在一些大型庫和框架中也會發(fā)現混入類的使用,用途同樣是增強已存在的類的功能和一些可選特征。
對于混入類,有幾點需要記住。首先是,混入類不能直接被實例化使用。其次,混入類沒有自己的狀態(tài)信息,也就是說它們并沒有定義 __init__()
方法,并且沒有實例屬性。這也是為什么我們在上面明確定義了 __slots__ = ()
。
還有一種實現混入類的方式就是使用類裝飾器,如下所示:
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|閩公網安備35020302033924號
違法和不良信息舉報電話:173-0602-2364|舉報郵箱:jubao@eeedong.com
掃描二維碼
下載編程獅App
編程獅公眾號
聯系方式:
更多建議: