任務
Python 元組可以很方便地被用來將信息分組,但是訪問每個子項都需要使用數字索引,所以這種用法有點不便。你希望能夠創建一種可以通過名字屬性訪問的元組。
解決方案
工廠函數是生成符合要求的元組的子類的最簡單方法:
#若在2.4中可使用operator.itemgetter,若在2.3中則需要實現itemgetter
try:from operator import itemgetter
except ImportError:def itemgetter(i):def getter(self):return self[i]return getterdef superTuple(typename,*attribute_names):"創建并返回擁有名字屬性的元組子類"#給子類適合的__new__和__repr__特殊方法nargs = len(attribute_names)class supertup(tuple):#我們不需要每個實例提供一個字典,節省內存__slots__ = ()def __new__(cls,*args):if len(args) != narqs:raise TypeError,'%s takes exactly %d arquments(%d given)'%(typename,nargs,len(args))return tuple.__new__(cls,args)def __repr__(self):return '%s(%s)'%(typename,','.join(map(repr,self)))#給我們的元組子類添加一些鍵for index,attr_name in enumerate(attribute_names):setattr(supertup,attr_name,property(itemgetter(index)))supertup.__name__ = typenamereturn supertup
討論
你常常需要用元組傳遞數據,就像C語言中的結構,或者其他語言中的簡單記錄一樣。但你總是需要記住每個數字索引對應到哪個城并需要用數字索引來訪問數據,這種用法令人心煩。一些 Python 標準庫模塊,比如 time 和 os,在舊版 Python 中總是返回元組,讓這種煩惱更是雪上加霜。事實上,如果有一個類似于元組的類型,可以允許你通過名字訪問值,就像通過屬性名訪問一樣,同時也提供了數字索引的訪問方式,那一定會是個很受歡迎的方案。本節展示了如何編程來實現這樣的效果,本質上我們只需要自動地創建一個元組的定制子類。
要創建一個新的定制類型有很多方法,定制元類對于此類任務通常是最好的方式。但在本節中,一個簡單的工廠函數足矣,而且你也應該避免使用超出需求的技術。下面展示怎樣在代碼中使用工廠函數superTuple,此處假設你已經將解決方案中的代碼存為一個名為 supertuple.py的模塊,并將它放入了你的Python的sys.path中:
>>> import supertuple
>>> Point = supertuple.superTuple('Point','x','y')
>>> Point
<class 'supertuple.Point'>
>>>p = Point(1,2,3)#故意給錯誤的數字
Traceback (most recent call last):File "",line 1 in?File "C:\Python24\Lib\site-packages\superTuple.py",line 16, in __new__ raise TypeError,'%s takes exactly %d arguments (%d given)' %
TypeError:Point takes exactly 2 arguments(3 given)
>>> p = Point(1,2)#這次給正確的數字
>>> p
Point(1,2)
>>> print p.x,p.y
1 2
函數 superTuple 的實現相當直接易懂。為了創建新的子類,superTuple 使用了一個 class聲明,在聲明的主體部分,它定義了三個特殊方法:一個“空”的__slots__(為了節省內存,我們的 supertuple 類并不需要為每個實例提供一個字典);一個__new__方法,這是為了在托管給 tuple.__new__之前先檢查參數的個數;以及一個__repr__方法。當一個新的類對象被創建之后,我們為每個我們需要的命名屬性設置了一個 property。每個這樣的 property 都只是一個“getter”,這是因為我們的 supertuples 就像元組一樣,是不可改變的——沒有對值域進行設置的方法。最后,我們設置新類的名字并返回類對象。
通過簡單地調用標準庫模塊 operator 的內建的 itemgetter 就可以創建這些 getter 方法。不過由于 operator.itemgetter 在 Python 2.4 中才被引入,在模塊的開頭我們需要確定用什么方式才能獲得可用的itemgetter,即使是在Python2.3中,我們也可以提供自己的實現。