看例子,學 Python(一)
看例子,學 Python(三)html
文件 mymath.py
定義了函數 fib
和 fac
,mymath.py
就是一個模塊。python
A module is a file containing Python definitions and statements.算法
導入模塊:segmentfault
>>> import mymath
mymath.py
須在當前目錄。數據結構
查看模塊幫助:函數
>>> help(mymath) Help on module mymath: NAME mymath FUNCTIONS fib(n) ...
列出模塊屬性:測試
>>> dir(mymath) ['__builtins__', '__cached__', '__doc__', '__file__', '__loader__', '__name__', '__package__', '__spec__', 'fac', 'fib']
訪問模塊屬性:ui
>>> mymath.__name__ 'mymath' >>> mymath.__doc__ >>>
模塊暫時尚未 docstring
,因此 mymath.__doc__
爲空。
訪問函數屬性:命令行
>>> mymath.fac <function fac at 0x00000000026DBA60> >>> mymath.__dict__['fac'] <function fac at 0x00000000026DBA60>
兩種方式效果同樣。__dict__
(字典)隱含於每一個對象中,模塊對象也不例外。code
爲模塊 mymath
添加 docstring
:
"""My math utilities.""" def fib(n): ...
那麼,下次調用 help
就能顯示這個 docstring
了:
>>> help(mymath) Help on module mymath: NAME mymath - My math utilities. ...
Python 自帶了 math
模塊,來研究一下:
>>> import math >>> help(math) Help on built-in module math: NAME math ...
列出屬性:
>>> dir(math) ['__doc__', '__loader__', ..., 'factorial', ...]
可見內建模塊 math
也提供了階乘(factorial),看看它怎麼用:
>>> help(math.factorial) Help on built-in function factorial in module math: factorial(...) factorial(x) -> Integral Find x!. Raise a ValueError if x is negative or non-integral.
調用試試:
>>> math.factorial(4) 24 >>> math.factorial(-1) Traceback (most recent call last): File "<stdin>", line 1, in <module> ValueError: factorial() not defined for negative values >>>
內建函數在異常處理方面作得會比較好。
值得注意的是,內建模塊並不必定由 Python 文件定義,好比 Python 的安裝目錄下其實並無 math.py
。
顧名思義,docstring
裏嵌測試用例,就是 doctest
。
好處是,既可作文檔又可作測試,也避免了註釋裏的示例代碼失效的問題。
爲 fac
添加 doctest
:
def fac(n): """ >>> fac(4) 24 """ ...
這就像交互環境下的輸入輸出同樣。>>>
打頭的是輸入,緊接着的即是輸出。
下面來運行試試:
$ python -m doctest -v mymath.py Trying: fac(4) Expecting: 24 ok 2 items had no tests: mymath mymath.fib 1 items passed all tests: 1 tests in mymath.fac 1 tests in 3 items. 1 passed and 0 failed. Test passed.
-m
指定使用 Python 的內建模塊 doctest
,-v
表示顯示詳細信息。
這只是運行 doctest
的方式之一,還能夠添加代碼到 mymath.py
的 main
函數:
if __name__ == '__main__': import doctest doctest.testmod(verbose=True)
後續就沒必要指定命令行參數 -m
-v
了。
做爲練習,咱們能夠爲 fib
添加 doctest
:
def fib(n): """Get the nth Fibonacci number. >>> fib(0) 0 >>> fib(1) 1 >>> fib(2) 1 >>> fib(6) 8 """ ...
對應於官方教程 5.5. Dictionaries。
前面介紹模塊時,提到了對象的特殊屬性 __dict__
,它其實就是一個字典(dict)。
Python 的字典,相似於 C++ 或 Java 的 map
,是存儲鍵值映射關係的一種數據結構。咱們且不去關心它的實現細節,是平衡二叉樹仍是哈希表,目前並不重要。
第一個例子,實現函數 char_counter
,以字典的形式,返回字符串中每一個字符出現的次數。
函數的 doctest
已經寫出來了,你只要讓它經過便可。
def char_counter(chars): """Count the number of each character. >>> d = char_counter('banana') >>> d['a'] 3 >>> d['b'] 1 >>> d['n'] 2 """ pass
def char_counter(chars): counter = {} # 初始化爲空字典 for c in chars: counter[c] += 1 # KeyError! return counter
counter[c] += 1
一句將引起 KeyError
異常,由於它沒有考慮字典中鍵不存在的狀況。
def char_counter(chars): counter = {} for c in chars: if c in counter: counter[c] += 1 else: counter[c] = 1 return counter
此版處理了鍵不存在的問題,可是 if...else
總歸顯得不夠優雅,Python 是一門簡潔優雅的語言,應該有更好的方法。
def char_counter(chars): counter = {} for c in chars: counter[c] = counter.get(c, 0) + 1 return counter
經過字典的 get
方法,保證當鍵不存在時,也能獲得妥善初始化了的值。
Word Counter 與 Char Counter 相似,以字典形式返回一段文本中每一個單詞出現的次數。
給定一段文本以下:
text = """Remember me when I am gone away, Gone far away into the silent land; When you can no more hold me by the hand, Nor I half turn to go yet turning stay. Remember me when no more day by day You tell me of our future that you plann'd: Only remember me; you understand It will be late to counsel then or pray. Yet if you should forget me for a while And afterwards remember, do not grieve: For if the darkness and corruption leave A vestige of the thoughts that once I had, Better by far you should forget and smile Than that you should remember and be sad."""
首先經過 str.split
把 text
分割成單詞的列表,而後計數的過程就跟 Char Counter 同樣了,各位能夠當作練習。
不難發現,Char Counter 和 Word Counter 在算法上有類似的地方,因此 Python 其實已經提供了這樣一個算法。
>>> import collections >>> help(collections.Counter) Help on class Counter in module collections: class Counter(builtins.dict) | Dict subclass for counting hashable items. Sometimes called a bag | or multiset. Elements are stored as dictionary keys and their counts | are stored as dictionary values. ...
根據幫助信息,Counter
是一個類,而且繼承自 dict
。姑且跳過類定義的語法,篇幅所限。
使用 collections.Counter
:
from collections import Counter counter = Counter(text.split()) print(counter['you']) # 6
既然繼承自字典,Counter
一定提供了一些額外的方法,好比獲取出現頻率最高的幾個鍵值對:
print(counter.most_common(3)) # [('you', 6), ('me', 5), ('the', 4)]
這就涉及了一個對字典以值來排序的問題。
怎麼對字典以值來排序呢?確實有幾種方式,下面以 Char Counter 爲例。
首先,排序用 sorted
。
>>> help(sorted) ... sorted(iterable, key=None, reverse=False)
輸入是一個 iterable
,能夠用 for
迭代的即是 iterable
的,字典固然也是。
可是若是直接對字典排序,即是以鍵來排序:
counter = char_counter('banana') print(sorted(counter)) # ['a', 'b', 'n']
爲了以值來排序,能夠爲 sorted
指定 key
:
counter = char_counter('banana') print(sorted(counter, key=counter.get)) # ['b', 'n', 'a']
這樣 sorted
在比較時,就不是比較鍵,而是比較 counter.get(鍵)
。
然而這樣的排序結果並非咱們想要的,由於它只有鍵而沒有值。
字典的 items
方法返回「鍵值對」列表,咱們能夠對這個列表按值排序。
print(counter.items()) # dict_items([('b', 1), ('a', 3), ('n', 2)])
這個列表裏的每一個元素都是一個鍵值對,由元組(tuple)表示。元組至關於只讀的列表,後面會有介紹。
from operator import itemgetter print(sorted(counter.items(), key=itemgetter(1))) # [('b', 1), ('n', 2), ('a', 3)]
指定 sorted
的 key
爲 itemgetter(1)
,便以每一個鍵值對元組下標爲 1 的元素進行排序。
最後,不妨來看一下 dict.items
的幫助:
>>> help(dict.items) items(...) D.items() -> list of D's (key, value) pairs, as 2-tuples
除了 itemgetter
,還能夠用 lambda
。
簡單來講,lambda
就是匿名函數(沒有名字的函數),短小精悍,臨時建立臨時使用,通常做爲參數傳遞,不必有名字。
方式一:
sorted(counter.items(), key=lambda x: x[1])
x
爲鍵值對元組。
方式二:
sorted(counter.items(), key=lambda (k,v): v)
直接在參數上把元組展開。
方式三:
sorted(counter.items(), key=lambda (k,v): (v,k))
交互鍵值順序,元組在比較時,依次比較裏面的每一個元素。
降序排序,只需指定 reverse
參數便可:
sorted(counter.items(), key=itemgetter(1), reverse=True)
元組(tuple)是隻讀的列表,雖然在底層實現上有很大不一樣。
>>> a = (1, "hello") >>> a[0] 1 >>> a[0] = 2 Traceback (most recent call last): File "<stdin>", line 1, in <module> TypeError: 'tuple' object does not support item assignment
可將其它序列類型(str, list)轉換成元組:
>>> s = tuple("abcde") >>> s ('a', 'b', 'c', 'd', 'e')