用 Python 的 Descriptor 特性解決一個變態的問題

來源:互聯網
上載者:User

Python 中的 Decorator 大家見得多了,但 Descriptor 用過的估計還少,它主要是 Python 自身來實現庫的一些特性,比如 staticmethod 之類的,今天有機會學習、實驗了 Descriptor,小有所得,跟大家分享。

今天 Jeff 給我們出了一道難題:有個叫 data 的某個 class 的執行個體,它有一個 item 屬性,它可能是一個對象(姑且假設它是個 str object),也可能是一序列對象(比如 list object),在這個前題下,希望做到以下代碼能夠工作:

# 當 data.item 是一個序列<br />data.item = ['lai', 'yong', 'hao']<br />print(data.item) # output: ['lai', 'yong', 'hao']<br />for i in data.item:<br /> print i<br /># output:<br />lai<br />yong<br />hao<br /># 當 data.item 是單個元素<br />data.item = 'lai'<br />print(data.item) # output: lai<br />for i in data.item:<br /> print i<br /># output: lai 

如果你現在覺得沒啥,那肯定是沒看仔細。我來提醒一下你,最後一行的 output 居然不是 lai 三個字母分成三行!

也就是說 data.item 要做到當它是單個元素的時候,普通場合要跟單元元素一樣,而迭代的場合,要跟包含多個元素的序列一樣!這個要求太變態了。這麼有挑戰的問題,我馬上祭出 python documentation,天不負苦心人,我找到了 Descriptor 這個我以前從未使用過的特性,最後解決了這個問題。詳情多講無益,直接上代碼:

#!/usr/bin/env python<br /># -*- coding:utf-8 -*-<br />from __future__ import print_function<br />class ItemDescriptor(object):<br />def __init__(self):<br />self._data = []<br />def __get__(self, instance, type = None):<br />if len(self._data) == 1:<br />tmp = self._data[0]<br />class Wrapper(tmp.__class__): # 注意它的父類<br />def __iter__(obj):<br /># 這裡使用的是 obj 不是 self,因為 self 已經被用了<br />return self._data.__iter__()<br />def next(obj):<br />return self._data.next()<br />return Wrapper(tmp)<br />return self._data<br />def __set__(self, obj, val):<br />if isinstance(val, list):<br />self._data = val<br />return<br />self._data = [val]<br />class Foo(object):<br />item = ItemDescriptor() # 重要!<br />foo = Foo()<br /># 輸出 Jeff,而不是 Jeff 一個字母一行<br />foo.item = 'Jeff'<br />print(foo.item)<br /># 它的行為跟 'Jeff' 一樣<br />print('f' in foo.item)<br /># 但迭代的時候,像 ['jeff'] 一樣<br />for i in foo.item:<br />print(i)<br /># 輸出四行單詞,而不是一行 <br />foo.item = ['lai', 'pan', 'jeff', 'ken']<br />print(foo.item)<br /># 它的行為像 ['lai', 'pan', 'jeff', 'ken'] 一樣<br />print('ken' in foo.item)<br /># 迭代時行為也像 ['lai', 'pan', 'jeff', 'ken'] 一樣<br />for i in foo.item:<br />print(i)<br />print('#' * 30)<br />

輸出:

lai@sd:~$ python test_descriptor.py<br />Jeff<br />True<br />Jeff<br />['lai', 'pan', 'jeff', 'ken']<br />True<br />lai<br />pan<br />jeff<br />ken<br />##############################

最後,多說一句這些代碼在 py2.6 和 py3.1 下測試通過,相容兩大版本。

相關文章

聯繫我們

該頁面正文內容均來源於網絡整理,並不代表阿里雲官方的觀點,該頁面所提到的產品和服務也與阿里云無關,如果該頁面內容對您造成了困擾,歡迎寫郵件給我們,收到郵件我們將在5個工作日內處理。

如果您發現本社區中有涉嫌抄襲的內容,歡迎發送郵件至: info-contact@alibabacloud.com 進行舉報並提供相關證據,工作人員會在 5 個工作天內聯絡您,一經查實,本站將立刻刪除涉嫌侵權內容。

A Free Trial That Lets You Build Big!

Start building with 50+ products and up to 12 months usage for Elastic Compute Service

  • Sales Support

    1 on 1 presale consultation

  • After-Sales Support

    24/7 Technical Support 6 Free Tickets per Quarter Faster Response

  • Alibaba Cloud offers highly flexible support services tailored to meet your exact needs.