當我在2011年和2012年寫做本書的初版時,可用的學習Python數據分析的資源不多。這部分上是一個雞和蛋的問題:咱們如今使用的庫,好比pandas、scikit-learn和statsmodels,那時相對來講並不成熟。2017年,數據科學、數據分析和機器學習的資源已經不少,原來通用的科學計算拓展到了計算機科學家、物理學家和其它研究領域的工做人員。學習Python和成爲軟件工程師的優秀書籍也有了。html
由於這本書是專一於Python數據處理的,對於一些Python的數據結構和庫的特性不免不足。所以,本章和第3章的內容只夠你能學習本書後面的內容。python
在我來看,沒有必要爲了數據分析而去精通Python。我鼓勵你使用IPython shell和Jupyter試驗示例代碼,並學習不一樣類型、函數和方法的文檔。雖然我已盡力讓本書內容按部就班,但讀者偶爾仍會碰到沒有以前介紹過的內容。linux
本書大部份內容關注的是基於表格的分析和處理大規模數據集的數據準備工具。爲了使用這些工具,必須首先將混亂的數據規整爲整潔的表格(或結構化)形式。幸虧,Python是一個理想的語言,能夠快速整理數據。Python使用得越熟練,越容易準備新數據集以進行分析。git
最好在IPython和Jupyter中親自嘗試本書中使用的工具。當你學會了如何啓動Ipython和Jupyter,我建議你跟隨示例代碼進行練習。與任何鍵盤驅動的操做環境同樣,記住常見的命令也是學習曲線的一部分。程序員
筆記:本章沒有介紹Python的某些概念,如類和麪向對象編程,你可能會發現它們在Python數據分析中頗有用。 爲了增強Python知識,我建議你學習官方Python教程,https://docs.python.org/3/,或是通用的Python教程書籍,好比:算法
- Python Cookbook,第3版,David Beazley和Brian K. Jones著(O’Reilly)
- 流暢的Python,Luciano Ramalho著 (O’Reilly)
- 高效的Python,Brett Slatkin著 (Pearson)
Python是解釋性語言。Python解釋器同一時間只能運行一個程序的一條語句。標準的交互Python解釋器能夠在命令行中經過鍵入python
命令打開:shell
$ python Python 3.6.0 | packaged by conda-forge | (default, Jan 13 2017, 23:17:12) [GCC 4.8.2 20140120 (Red Hat 4.8.2-15)] on linux Type "help", "copyright", "credits" or "license" for more information. >>> a = 5 >>> print(a) 5
>>>
提示輸入代碼。要退出Python解釋器返回終端,能夠輸入exit()
或按Ctrl-D。編程
運行Python程序只需調用Python的同時,使用一個.py
文件做爲它的第一個參數。假設建立了一個hello_world.py
文件,它的內容是:api
print('Hello world')
你能夠用下面的命令運行它(hello_world.py
文件必須位於終端的工做目錄):數組
$ python hello_world.py Hello world
一些Python程序員老是這樣執行Python代碼的,從事數據分析和科學計算的人卻會使用IPython,一個強化的Python解釋器,或Jupyter notebooks,一個網頁代碼筆記本,它原先是IPython的一個子項目。在本章中,我介紹瞭如何使用IPython和Jupyter,在附錄A中有更深刻的介紹。當你使用%run
命令,IPython會一樣執行指定文件中的代碼,結束以後,還能夠與結果交互:
$ ipython Python 3.6.0 | packaged by conda-forge | (default, Jan 13 2017, 23:17:12) Type "copyright", "credits" or "license" for more information. IPython 5.1.0 -- An enhanced Interactive Python. ? -> Introduction and overview of IPython's features. %quickref -> Quick reference. help -> Python's own help system. object? -> Details about 'object', use 'object??' for extra details. In [1]: %run hello_world.py Hello world In [2]:
IPython默認採用序號的格式In [2]:
,與標準的>>>
提示符不一樣。
在本節中,咱們會教你打開運行IPython shell和jupyter notebook,並介紹一些基本概念。
你能夠用ipython
在命令行打開IPython Shell,就像打開普通的Python解釋器:
$ ipython Python 3.6.0 | packaged by conda-forge | (default, Jan 13 2017, 23:17:12) Type "copyright", "credits" or "license" for more information. IPython 5.1.0 -- An enhanced Interactive Python. ? -> Introduction and overview of IPython's features. %quickref -> Quick reference. help -> Python's own help system. object? -> Details about 'object', use 'object??' for extra details. In [1]: a = 5 In [2]: a Out[2]: 5
你能夠經過輸入代碼並按Return(或Enter),運行任意Python語句。當你只輸入一個變量,它會顯示錶明的對象:
In [5]: import numpy as np
In [6]: data = {i : np.random.randn() for i in range(7)}
In [7]: data
Out[7]:
{0: -0.20470765948471295,
1: 0.47894333805754824,
2: -0.5194387150567381,
3: -0.55573030434749,
4: 1.9657805725027142,
5: 1.3934058329729904,
6: 0.09290787674371767}
前兩行是Python代碼語句;第二條語句建立一個名爲data
的變量,它引用一個新建立的Python字典。最後一行打印data
的值。
許多Python對象被格式化爲更易讀的形式,或稱做pretty-printed
,它與普通的print
不一樣。若是在標準Python解釋器中打印上述data
變量,則可讀性要下降:
>>> from numpy.random import randn >>> data = {i : randn() for i in range(7)} >>> print(data) {0: -1.5948255432744511, 1: 0.10569006472787983, 2: 1.972367135977295, 3: 0.15455217573074576, 4: -0.24058577449429575, 5: -1.2904897053651216, 6: 0.3308507317325902}
IPython還支持執行任意代碼塊(經過一個華麗的複製-粘貼方法)和整段Python腳本的功能。你也可使用Jupyter notebook運行大代碼塊,接下來就會看到。
notebook是Jupyter項目的重要組件之一,它是一個代碼、文本(有標記或無標記)、數據可視化或其它輸出的交互式文檔。Jupyter Notebook須要與內核互動,內核是Jupyter與其它編程語言的交互編程協議。Python的Jupyter內核是使用IPython。要啓動Jupyter,在命令行中輸入jupyter notebook
:
$ jupyter notebook [I 15:20:52.739 NotebookApp] Serving notebooks from local directory: /home/wesm/code/pydata-book [I 15:20:52.739 NotebookApp] 0 active kernels [I 15:20:52.739 NotebookApp] The Jupyter Notebook is running at: http://localhost:8888/ [I 15:20:52.740 NotebookApp] Use Control-C to stop this server and shut down all kernels (twice to skip confirmation). Created new window in existing browser session.
在多數平臺上,Jupyter會自動打開默認的瀏覽器(除非指定了--no-browser
)。或者,能夠在啓動notebook以後,手動打開網頁http://localhost:8888/
。圖2-1展現了Google Chrome中的notebook。
筆記:許多人使用Jupyter做爲本地的計算環境,但它也能夠部署到服務器上遠程訪問。這裏不作介紹,若是須要的話,鼓勵讀者自行到網上學習。
要新建一個notebook,點擊按鈕New,選擇「Python3」或「conda[默認項]」。若是是第一次,點擊空格,輸入一行Python代碼。而後按Shift-Enter執行。
當保存notebook時(File目錄下的Save and Checkpoint),會建立一個後綴名爲.ipynb
的文件。這是一個自包含文件格式,包含當前筆記本中的全部內容(包括全部已評估的代碼輸出)。能夠被其它Jupyter用戶加載和編輯。要加載存在的notebook,把它放到啓動notebook進程的相同目錄內。你能夠用本書的示例代碼練習,見圖2-3。
雖然Jupyter notebook和IPython shell使用起來不一樣,本章中幾乎全部的命令和工具均可以通用。
從外觀上,IPython shell和標準的Python解釋器只是看起來不一樣。IPython shell的進步之一是具有其它IDE和交互計算分析環境都有的tab補全功能。在shell中輸入表達式,按下Tab,會搜索已輸入變量(對象、函數等等)的命名空間:
In [1]: an_apple = 27 In [2]: an_example = 42 In [3]: an<Tab> an_apple and an_example any
在這個例子中,IPython呈現出了以前兩個定義的變量和Python的關鍵字和內建的函數any
。固然,你也能夠補全任何對象的方法和屬性:
In [3]: b = [1, 2, 3] In [4]: b.<Tab> b.append b.count b.insert b.reverse b.clear b.extend b.pop b.sort b.copy b.index b.remove
一樣也適用於模塊:
In [1]: import datetime In [2]: datetime.<Tab> datetime.date datetime.MAXYEAR datetime.timedelta datetime.datetime datetime.MINYEAR datetime.timezone datetime.datetime_CAPI datetime.time datetime.tzinfo
在Jupyter notebook和新版的IPython(5.0及以上),自動補全功能是下拉框的形式。
筆記:注意,默認狀況下,IPython會隱藏下劃線開頭的方法和屬性,好比魔術方法和內部的「私有」方法和屬性,以免混亂的顯示(和讓新手迷惑!)這些也能夠tab補全,可是你必須首先鍵入一個下劃線才能看到它們。若是你喜歡老是在tab補全中看到這樣的方法,你能夠IPython配置中進行設置。能夠在IPython文檔中查找方法。
除了補全命名、對象和模塊屬性,Tab還能夠補全其它的。當輸入看似文件路徑時(即便是Python字符串),按下Tab也能夠補全電腦上對應的文件信息:
In [7]: datasets/movielens/<Tab> datasets/movielens/movies.dat datasets/movielens/README datasets/movielens/ratings.dat datasets/movielens/users.dat In [7]: path = 'datasets/movielens/<Tab> datasets/movielens/movies.dat datasets/movielens/README datasets/movielens/ratings.dat datasets/movielens/users.dat
結合%run
,tab補全能夠節省許多鍵盤操做。
另外,tab補全能夠補全函數的關鍵詞參數(包括等於號=)。見圖2-4。
後面會仔細地學習函數。
在變量先後使用問號?,能夠顯示對象的信息:
In [8]: b = [1, 2, 3]
In [9]: b?
Type: list
String Form:[1, 2, 3]
Length: 3
Docstring:
list() -> new empty list
list(iterable) -> new list initialized from iterable's items In [10]: print? Docstring: print(value, ..., sep=' ', end='\n', file=sys.stdout, flush=False) Prints the values to a stream, or to sys.stdout by default. Optional keyword arguments: file: a file-like object (stream); defaults to the current sys.stdout. sep: string inserted between values, default a space. end: string appended after the last value, default a newline. flush: whether to forcibly flush the stream. Type: builtin_function_or_method
這能夠做爲對象的自省。若是對象是一個函數或實例方法,定義過的文檔字符串,也會顯示出信息。假設咱們寫了一個以下的函數:
def add_numbers(a, b):
""" Add two numbers together Returns ------- the_sum : type of arguments """
return a + b
而後使用?符號,就能夠顯示以下的文檔字符串:
In [11]: add_numbers?
Signature: add_numbers(a, b)
Docstring:
Add two numbers together
Returns
-------
the_sum : type of arguments
File: <ipython-input-9-6a548a216e27>
Type: function
使用??會顯示函數的源碼:
In [12]: add_numbers??
Signature: add_numbers(a, b)
Source:
def add_numbers(a, b):
""" Add two numbers together Returns ------- the_sum : type of arguments """
return a + b
File: <ipython-input-9-6a548a216e27>
Type: function
?還有一個用途,就是像Unix或Windows命令行同樣搜索IPython的命名空間。字符與通配符結合能夠匹配全部的名字。例如,咱們能夠得到全部包含load的頂級NumPy命名空間:
In [13]: np.*load*?
np.__loader__
np.load
np.loads
np.loadtxt
np.pkgload
你能夠用%run
命令運行全部的Python程序。假設有一個文件ipython_script_test.py
:
def f(x, y, z):
return (x + y) / z
a = 5
b = 6
c = 7.5
result = f(a, b, c)
能夠以下運行:
In [14]: %run ipython_script_test.py
這段腳本運行在空的命名空間(沒有import和其它定義的變量),所以結果和普通的運行方式python script.py
相同。文件中全部定義的變量(import、函數和全局變量,除非拋出異常),均可以在IPython shell中隨後訪問:
In [15]: c
Out [15]: 7.5
In [16]: result
Out[16]: 1.4666666666666666
若是一個Python腳本須要命令行參數(在sys.argv
中查找),能夠在文件路徑以後傳遞,就像在命令行上運行同樣。
筆記:若是想讓一個腳本訪問IPython已經定義過的變量,可使用
%run -i
。
在Jupyter notebook中,你也可使用%load
,它將腳本導入到一個代碼格中:
>>> %load ipython_script_test.py def f(x, y, z): return (x + y) / z a = 5 b = 6 c = 7.5 result = f(a, b, c)
代碼運行時按Ctrl-C,不管是%run或長時間運行命令,都會致使KeyboardInterrupt
。這會致使幾乎全部Python程序當即中止,除非一些特殊狀況。
警告:當Python代碼調用了一些編譯的擴展模塊,按Ctrl-C不必定將執行的程序當即中止。在這種狀況下,你必須等待,直到控制返回Python解釋器,或者在更糟糕的狀況下強制終止Python進程。
若是使用Jupyter notebook,你能夠將代碼複製粘貼到任意代碼格執行。在IPython shell中也能夠從剪貼板執行。假設在其它應用中複製了以下代碼:
x = 5
y = 7
if x > 5:
x += 1
y = 8
最簡單的方法是使用%paste
和%cpaste
函數。%paste
能夠直接運行剪貼板中的代碼:
In [17]: %paste
x = 5
y = 7
if x > 5:
x += 1
y = 8
## -- End pasted text --
%cpaste
功能相似,但會給出一條提示:
In [18]: %cpaste
Pasting code; enter '--' alone on the line to stop or use Ctrl-D.
:x = 5
:y = 7
:if x > 5:
: x += 1
:
: y = 8
:--
使用%cpaste
,你能夠粘貼任意多的代碼再運行。你可能想在運行前,先看看代碼。若是粘貼了錯誤的代碼,能夠用Ctrl-C中斷。
IPython有許多鍵盤快捷鍵進行導航提示(相似Emacs文本編輯器或UNIX bash Shell)和交互shell的歷史命令。表2-1總結了常見的快捷鍵。圖2-5展現了一部分,如移動光標。
Jupyter notebooks有另一套龐大的快捷鍵。由於它的快捷鍵比IPython的變化快,建議你參閱Jupyter notebook的幫助文檔。
IPython中特殊的命令(Python中沒有)被稱做「魔術」命令。這些命令可使普通任務更便捷,更容易控制IPython系統。魔術命令是在指令前添加百分號%前綴。例如,能夠用%timeit
(這個命令後面會詳談)測量任何Python語句,例如矩陣乘法,的執行時間:
In [20]: a = np.random.randn(100, 100)
In [20]: %timeit np.dot(a, a)
10000 loops, best of 3: 20.9 µs per loop
魔術命令能夠被看作IPython中運行的命令行。許多魔術命令有「命令行」選項,能夠經過?查看:
In [21]: %debug? Docstring: :: %debug [--breakpoint FILE:LINE] [statement [statement ...]] Activate the interactive debugger. This magic command support two ways of activating debugger. One is to activate debugger before executing code. This way, you can set a break point, to step through the code from the point. You can use this mode by giving statements to execute and optionally a breakpoint. The other one is to activate debugger in post-mortem mode. You can activate this mode simply running %debug without any argument. If an exception has just occurred, this lets you inspect its stack frames interactively. Note that this will always work only on the last traceback that occurred, so you must call this quickly after an exception that you wish to inspect has fired, because if another one occurs, it clobbers the previous one. If you want IPython to automatically do this on every exception, see the %pdb magic for more details. positional arguments: statement Code to run in debugger. You can omit this in cell magic mode. optional arguments: --breakpoint <FILE:LINE>, -b <FILE:LINE> Set break point at LINE in FILE.
魔術函數默承認以不用百分號,只要沒有變量和函數名相同。這個特色被稱爲「自動魔術」,能夠用%automagic
打開或關閉。
一些魔術函數與Python函數很像,它的結果能夠賦值給一個變量:
In [22]: %pwd Out[22]: '/home/wesm/code/pydata-book In [23]: foo = %pwd In [24]: foo Out[24]: '/home/wesm/code/pydata-book'
IPython的文檔能夠在shell中打開,我建議你用%quickref
或%magic
學習下全部特殊命令。表2-2列出了一些能夠提升生產率的交互計算和Python開發的IPython指令。
IPython在分析計算領域可以流行的緣由之一是它很是好的集成了數據可視化和其它用戶界面庫,好比matplotlib。不用擔憂之前沒用過matplotlib,本書後面會詳細介紹。%matplotlib
魔術函數配置了IPython shell和Jupyter notebook中的matplotlib。這點很重要,其它建立的圖不會出現(notebook)或獲取session的控制,直到結束(shell)。
在IPython shell中,運行%matplotlib
能夠進行設置,能夠建立多個繪圖窗口,而不會干擾控制檯session:
In [26]: %matplotlib Using matplotlib backend: Qt4Agg
在JUpyter中,命令有所不一樣(圖2-6):
In [26]: %matplotlib inline
在本節中,我將概述基本的Python概念和語言機制。在下一章,我將詳細介紹Python的數據結構、函數和其它內建工具。
Python的語言設計強調的是可讀性、簡潔和清晰。有些人稱Python爲「可執行的僞代碼」。
Python使用空白字符(tab和空格)來組織代碼,而不是像其它語言,好比R、C++、JAVA和Perl那樣使用括號。看一個排序算法的for
循環:
for x in array:
if x < pivot:
less.append(x)
else:
greater.append(x)
冒號標誌着縮進代碼塊的開始,冒號以後的全部代碼的縮進量必須相同,直到代碼塊結束。無論是否喜歡這種形式,使用空白符是Python程序員開發的一部分,在我看來,這可讓python的代碼可讀性大大優於其它語言。雖然期初看起來很奇怪,通過一段時間,你就能適應了。
筆記:我強烈建議你使用四個空格做爲默認的縮進,可使用tab代替四個空格。許多文本編輯器的設置是使用製表位替代空格。某些人使用tabs或不一樣數目的空格數,常見的是使用兩個空格。大多數狀況下,四個空格是大多數人採用的方法,所以建議你也這樣作。
你應該已經看到,Python的語句不須要用分號結尾。可是,分號卻能夠用來給同在一行的語句切分:
a = 5; b = 6; c = 7
Python不建議將多條語句放到一行,這會下降代碼的可讀性。
Python語言的一個重要特性就是它的對象模型的一致性。每一個數字、字符串、數據結構、函數、類、模塊等等,都是在Python解釋器的自有「盒子」內,它被認爲是Python對象。每一個對象都有類型(例如,字符串或函數)和內部數據。在實際中,這可讓語言很是靈活,由於函數也能夠被當作對象使用。
任何前面帶有井號#的文本都會被Python解釋器忽略。這一般被用來添加註釋。有時,你會想排除一段代碼,但並不刪除。簡便的方法就是將其註釋掉:
results = []
for line in file_handle:
# keep the empty lines for now
# if len(line) == 0:
# continue
results.append(line.replace('foo', 'bar'))
也能夠在執行過的代碼後面添加註釋。一些人習慣在代碼以前添加註釋,前者這種方法有時也是有用的:
print("Reached this line") # Simple status report
你能夠用圓括號調用函數,傳遞零個或幾個參數,或者將返回值給一個變量:
result = f(x, y, z) g()
幾乎Python中的每一個對象都有附加的函數,稱做方法,能夠用來訪問對象的內容。能夠用下面的語句調用:
obj.some_method(x, y, z)
函數可使用位置和關鍵詞參數:
result = f(a, b, c, d=5, e='foo')
後面會有更多介紹。
當在Python中建立變量(或名字),你就在等號右邊建立了一個對這個變量的引用。考慮一個整數列表:
In [8]: a = [1, 2, 3]
假設將a賦值給一個新變量b:
In [9]: b = a
在有些方法中,這個賦值會將數據[1, 2, 3]也複製。在Python中,a和b其實是同一個對象,即原有列表[1, 2, 3](見圖2-7)。你能夠在a中添加一個元素,而後檢查b:
In [10]: a.append(4)
In [11]: b
Out[11]: [1, 2, 3, 4]
理解Python的引用的含義,數據是什麼時候、如何、爲什麼複製的,是很是重要的。尤爲是當你用Python處理大的數據集時。
筆記:賦值也被稱做綁定,咱們是把一個名字綁定給一個對象。變量名有時可能被稱爲綁定變量。
當你將對象做爲參數傳遞給函數時,新的局域變量建立了對原始對象的引用,而不是複製。若是在函數裏綁定一個新對象到一個變量,這個變更不會反映到上一層。所以能夠改變可變參數的內容。假設有如下函數:
def append_element(some_list, element):
some_list.append(element)
而後有:
In [27]: data = [1, 2, 3]
In [28]: append_element(data, 4)
In [29]: data
Out[29]: [1, 2, 3, 4]
與許多編譯語言(如JAVA和C++)對比,Python中的對象引用不包含附屬的類型。下面的代碼是沒有問題的:
In [12]: a = 5
In [13]: type(a)
Out[13]: int
In [14]: a = 'foo'
In [15]: type(a)
Out[15]: str
變量是在特殊命名空間中的對象的名字,類型信息保存在對象自身中。一些人可能會說Python不是「類型化語言」。這是不正確的,看下面的例子:
In [16]: '5' + 5 --------------------------------------------------------------------------- TypeError Traceback (most recent call last) <ipython-input-16-f9dbf5f0b234> in <module>() ----> 1 '5' + 5 TypeError: must be str, not int
在某些語言中,例如Visual Basic,字符串‘5’可能被默許轉換(或投射)爲整數,所以會產生10。但在其它語言中,例如JavaScript,整數5會被投射成字符串,結果是聯結字符串‘55’。在這個方面,Python被認爲是強類型化語言,意味着每一個對象都有明確的類型(或類),默許轉換隻會發生在特定的狀況下,例如:
In [17]: a = 4.5 In [18]: b = 2 # String formatting, to be visited later In [19]: print('a is {0}, b is {1}'.format(type(a), type(b))) a is <class 'float'>, b is <class 'int'> In [20]: a / b Out[20]: 2.25
知道對象的類型很重要,最好能讓函數能夠處理多種類型的輸入。你能夠用isinstance
函數檢查對象是某個類型的實例:
In [21]: a = 5 In [22]: isinstance(a, int) Out[22]: True
isinstance
能夠用類型元組,檢查對象的類型是否在元組中:
In [23]: a = 5; b = 4.5 In [24]: isinstance(a, (int, float)) Out[24]: True In [25]: isinstance(b, (int, float)) Out[25]: True
Python的對象一般都有屬性(其它存儲在對象內部的Python對象)和方法(對象的附屬函數能夠訪問對象的內部數據)。能夠用obj.attribute_name
訪問屬性和方法:
In [1]: a = 'foo' In [2]: a.<Press Tab> a.capitalize a.format a.isupper a.rindex a.strip a.center a.index a.join a.rjust a.swapcase a.count a.isalnum a.ljust a.rpartition a.title a.decode a.isalpha a.lower a.rsplit a.translate a.encode a.isdigit a.lstrip a.rstrip a.upper a.endswith a.islower a.partition a.split a.zfill a.expandtabs a.isspace a.replace a.splitlines a.find a.istitle a.rfind a.startswith
也能夠用getattr
函數,經過名字訪問屬性和方法:
In [27]: getattr(a, 'split') Out[27]: <function str.split>
在其它語言中,訪問對象的名字一般稱做「反射」。本書不會大量使用getattr
函數和相關的hasattr
和setattr
函數,使用這些函數能夠高效編寫原生的、可重複使用的代碼。
常常地,你可能不關心對象的類型,只關心對象是否有某些方法或用途。這一般被稱爲「鴨子類型」,來自「走起來像鴨子、叫起來像鴨子,那麼它就是鴨子」的說法。例如,你能夠經過驗證一個對象是否遵循迭代協議,判斷它是可迭代的。對於許多對象,這意味着它有一個__iter__
魔術方法,其它更好的判斷方法是使用iter
函數:
def isiterable(obj):
try:
iter(obj)
return True
except TypeError: # not iterable
return False
這個函數會返回字符串以及大多數Python集合類型爲True
:
In [29]: isiterable('a string') Out[29]: True In [30]: isiterable([1, 2, 3]) Out[30]: True In [31]: isiterable(5) Out[31]: False
我老是用這個功能編寫能夠接受多種輸入類型的函數。常見的例子是編寫一個函數能夠接受任意類型的序列(list、tuple、ndarray)或是迭代器。你可先檢驗對象是不是列表(或是NUmPy數組),若是不是的話,將其轉變成列表:
if not isinstance(x, list) and isiterable(x):
x = list(x)
在Python中,模塊就是一個有.py
擴展名、包含Python代碼的文件。假設有如下模塊:
# some_module.py
PI = 3.14159
def f(x):
return x + 2
def g(a, b):
return a + b
若是想從同目錄下的另外一個文件訪問some_module.py
中定義的變量和函數,能夠:
import some_module
result = some_module.f(5)
pi = some_module.PI
或者:
from some_module import f, g, PI
result = g(5, PI)
使用as
關鍵詞,你能夠給引入起不一樣的變量名:
import some_module as sm
from some_module import PI as pi, g as gf
r1 = sm.f(pi)
r2 = gf(6, pi)
大多數二元數學運算和比較都不難想到:
In [32]: 5 - 7
Out[32]: -2
In [33]: 12 + 21.5
Out[33]: 33.5
In [34]: 5 <= 2
Out[34]: False
表2-3列出了全部的二元運算符。
要判斷兩個引用是否指向同一個對象,可使用is
方法。is not
能夠判斷兩個對象是不一樣的:
In [35]: a = [1, 2, 3]
In [36]: b = a
In [37]: c = list(a)
In [38]: a is b
Out[38]: True
In [39]: a is not c
Out[39]: True
由於list
老是建立一個新的Python列表(即複製),咱們能夠判定c是不一樣於a的。使用is
比較與==
運算符不一樣,以下:
In [40]: a == c
Out[40]: True
is
和is not
經常使用來判斷一個變量是否爲None
,由於只有一個None
的實例:
In [41]: a = None
In [42]: a is None
Out[42]: True
Python中的大多數對象,好比列表、字典、NumPy數組,和用戶定義的類型(類),都是可變的。意味着這些對象或包含的值能夠被修改:
In [43]: a_list = ['foo', 2, [4, 5]]
In [44]: a_list[2] = (3, 4)
In [45]: a_list
Out[45]: ['foo', 2, (3, 4)]
其它的,例如字符串和元組,是不可變的:
In [46]: a_tuple = (3, 5, (4, 5))
In [47]: a_tuple[1] = 'four'
---------------------------------------------------------------------------
TypeError Traceback (most recent call last)
<ipython-input-47-b7966a9ae0f1> in <module>()
----> 1 a_tuple[1] = 'four'
TypeError: 'tuple' object does not support item assignment
記住,能夠修改一個對象並不意味就要修改它。這被稱爲反作用。例如,當寫一個函數,任何反作用都要在文檔或註釋中寫明。若是可能的話,我推薦避免反作用,採用不可變的方式,即便要用到可變對象。
Python的標準庫中有一些內建的類型,用於處理數值數據、字符串、布爾值,和日期時間。這些單值類型被稱爲標量類型,本書中稱其爲標量。表2-4列出了主要的標量。日期和時間處理會另外討論,由於它們是標準庫的datetime
模塊提供的。
Python的主要數值類型是int
和float
。int
能夠存儲任意大的數:
In [48]: ival = 17239871
In [49]: ival ** 6
Out[49]: 26254519291092456596965462913230729701102721
浮點數使用Python的float
類型。每一個數都是雙精度(64位)的值。也能夠用科學計數法表示:
In [50]: fval = 7.243
In [51]: fval2 = 6.78e-5
不能獲得整數的除法會獲得浮點數:
In [52]: 3 / 2
Out[52]: 1.5
要得到C-風格的整除(去掉小數部分),可使用底除運算符//:
In [53]: 3 // 2
Out[53]: 1
許多人是由於Python強大而靈活的字符串處理而使用Python的。你能夠用單引號或雙引號來寫字符串:
a = 'one way of writing a string'
b = "another way"
對於有換行符的字符串,可使用三引號,'''或"""都行:
c = """ This is a longer string that spans multiple lines """
字符串c
實際包含四行文本,"""後面和lines後面的換行符。能夠用count
方法計算c
中的新的行:
In [55]: c.count('\n')
Out[55]: 3
Python的字符串是不可變的,不能修改字符串:
In [56]: a = 'this is a string'
In [57]: a[10] = 'f'
---------------------------------------------------------------------------
TypeError Traceback (most recent call last)
<ipython-input-57-5ca625d1e504> in <module>()
----> 1 a[10] = 'f'
TypeError: 'str' object does not support item assignment
In [58]: b = a.replace('string', 'longer string')
In [59]: b
Out[59]: 'this is a longer string'
通過以上的操做,變量a
並無被修改:
In [60]: a
Out[60]: 'this is a string'
許多Python對象使用str
函數能夠被轉化爲字符串:
In [61]: a = 5.6
In [62]: s = str(a)
In [63]: print(s)
5.6
字符串是一個序列的Unicode字符,所以能夠像其它序列,好比列表和元組(下一章會詳細介紹二者)同樣處理:
In [64]: s = 'python'
In [65]: list(s)
Out[65]: ['p', 'y', 't', 'h', 'o', 'n']
In [66]: s[:3]
Out[66]: 'pyt'
語法s[:3]
被稱做切片,適用於許多Python序列。後面會更詳細的介紹,本書中用到不少切片。
反斜槓是轉義字符,意思是它備用來表示特殊字符,好比換行符\n或Unicode字符。要寫一個包含反斜槓的字符串,須要進行轉義:
In [67]: s = '12\\34'
In [68]: print(s)
12\34
若是字符串中包含許多反斜槓,但沒有特殊字符,這樣作就很麻煩。幸虧,能夠在字符串前面加一個r,代表字符就是它自身:
In [69]: s = r'this\has\no\special\characters'
In [70]: s
Out[70]: 'this\\has\\no\\special\\characters'
r表示raw。
strong text
將兩個字符串合併,會產生一個新的字符串:
In [71]: a = 'this is the first half '
In [72]: b = 'and this is the second half'
In [73]: a + b
Out[73]: 'this is the first half and this is the second half'
字符串的模板化或格式化,是另外一個重要的主題。Python 3拓展了此類的方法,這裏只介紹一些。字符串對象有format
方法,能夠替換格式化的參數爲字符串,產生一個新的字符串:
In [74]: template = '{0:.2f} {1:s} are worth US${2:d}'
在這個字符串中,
{0:.2f}
表示格式化第一個參數爲帶有兩位小數的浮點數。{1:s}
表示格式化第二個參數爲字符串。{2:d}
表示格式化第三個參數爲一個整數。要替換參數爲這些格式化的參數,咱們傳遞format
方法一個序列:
In [75]: template.format(4.5560, 'Argentine Pesos', 1)
Out[75]: '4.56 Argentine Pesos are worth US$1'
字符串格式化是一個很深的主題,有多種方法和大量的選項,能夠控制字符串中的值是如何格式化的。推薦參閱Python官方文檔。
這裏歸納介紹字符串處理,第8章的數據分析會詳細介紹。
在Python 3及以上版本中,Unicode是一級的字符串類型,這樣能夠更一致的處理ASCII和Non-ASCII文本。在老的Python版本中,字符串都是字節,不使用Unicode編碼。假如知道字符編碼,能夠將其轉化爲Unicode。看一個例子:
In [76]: val = "español"
In [77]: val
Out[77]: 'español'
能夠用encode
將這個Unicode字符串編碼爲UTF-8:
In [78]: val_utf8 = val.encode('utf-8')
In [79]: val_utf8
Out[79]: b'espa\xc3\xb1ol'
In [80]: type(val_utf8)
Out[80]: bytes
若是你知道一個字節對象的Unicode編碼,用decode
方法能夠解碼:
In [81]: val_utf8.decode('utf-8')
Out[81]: 'español'
雖然UTF-8編碼已經變成主流,但由於歷史的緣由,你仍然可能碰到其它編碼的數據:
In [82]: val.encode('latin1')
Out[82]: b'espa\xf1ol'
In [83]: val.encode('utf-16')
Out[83]: b'\xff\xfee\x00s\x00p\x00a\x00\xf1\x00o\x00l\x00'
In [84]: val.encode('utf-16le')
Out[84]: b'e\x00s\x00p\x00a\x00\xf1\x00o\x00l\x00'
工做中碰到的文件不少都是字節對象,盲目地將全部數據編碼爲Unicode是不可取的。
雖然用的很少,你能夠在字節文本的前面加上一個b:
In [85]: bytes_val = b'this is bytes'
In [86]: bytes_val
Out[86]: b'this is bytes'
In [87]: decoded = bytes_val.decode('utf8')
In [88]: decoded # this is str (Unicode) now
Out[88]: 'this is bytes'
Python中的布爾值有兩個,True和False。比較和其它條件表達式能夠用True和False判斷。布爾值能夠與and和or結合使用:
In [89]: True and True
Out[89]: True
In [90]: False or True
Out[90]: True
str、bool、int和float也是函數,能夠用來轉換類型:
In [91]: s = '3.14159'
In [92]: fval = float(s)
In [93]: type(fval)
Out[93]: float
In [94]: int(fval)
Out[94]: 3
In [95]: bool(fval)
Out[95]: True
In [96]: bool(0)
Out[96]: False
None是Python的空值類型。若是一個函數沒有明確的返回值,就會默認返回None:
In [97]: a = None
In [98]: a is None
Out[98]: True
In [99]: b = 5
In [100]: b is not None
Out[100]: True
None也經常做爲函數的默認參數:
def add_and_maybe_multiply(a, b, c=None):
result = a + b
if c is not None:
result = result * c
return result
另外,None不只是一個保留字,仍是惟一的NoneType的實例:
In [101]: type(None)
Out[101]: NoneType
Python內建的datetime
模塊提供了datetime
、date
和time
類型。datetime
類型結合了date
和time
,是最常使用的:
In [102]: from datetime import datetime, date, time
In [103]: dt = datetime(2011, 10, 29, 20, 30, 21)
In [104]: dt.day
Out[104]: 29
In [105]: dt.minute
Out[105]: 30
根據datetime
實例,你能夠用date
和time
提取出各自的對象:
In [106]: dt.date()
Out[106]: datetime.date(2011, 10, 29)
In [107]: dt.time()
Out[107]: datetime.time(20, 30, 21)
strftime
方法能夠將datetime格式化爲字符串:
In [108]: dt.strftime('%m/%d/%Y %H:%M')
Out[108]: '10/29/2011 20:30'
strptime
能夠將字符串轉換成datetime
對象:
In [109]: datetime.strptime('20091031', '%Y%m%d')
Out[109]: datetime.datetime(2009, 10, 31, 0, 0)
表2-5列出了全部的格式化命令。
當你聚類或對時間序列進行分組,替換datetimes的time字段有時會頗有用。例如,用0替換分和秒:
In [110]: dt.replace(minute=0, second=0)
Out[110]: datetime.datetime(2011, 10, 29, 20, 0)
由於datetime.datetime
是不可變類型,上面的方法會產生新的對象。
兩個datetime對象的差會產生一個datetime.timedelta
類型:
In [111]: dt2 = datetime(2011, 11, 15, 22, 30)
In [112]: delta = dt2 - dt
In [113]: delta
Out[113]: datetime.timedelta(17, 7179)
In [114]: type(delta)
Out[114]: datetime.timedelta
結果timedelta(17, 7179)
指明瞭timedelta
將17天、7179秒的編碼方式。
將timedelta
添加到datetime
,會產生一個新的偏移datetime
:
In [115]: dt
Out[115]: datetime.datetime(2011, 10, 29, 20, 30, 21)
In [116]: dt + delta
Out[116]: datetime.datetime(2011, 11, 15, 22, 30)
Python有若干內建的關鍵字進行條件邏輯、循環和其它控制流操做。
if是最廣爲人知的控制流語句。它檢查一個條件,若是爲True,就執行後面的語句:
if x < 0:
print('It's negative')
if
後面能夠跟一個或多個elif
,全部條件都是False時,還能夠添加一個else
:
if x < 0:
print('It's negative') elif x == 0: print('Equal to zero') elif 0 < x < 5: print('Positive but smaller than 5') else: print('Positive and larger than or equal to 5')
若是某個條件爲True,後面的elif
就不會被執行。當使用and和or時,複合條件語句是從左到右執行:
In [117]: a = 5; b = 7
In [118]: c = 8; d = 4
In [119]: if a < b or c > d:
.....: print('Made it')
Made it
在這個例子中,c > d
不會被執行,由於第一個比較是True:
也能夠把比較式串在一塊兒:
In [120]: 4 > 3 > 2 > 1
Out[120]: True
for循環是在一個集合(列表或元組)中進行迭代,或者就是一個迭代器。for循環的標準語法是:
for value in collection:
# do something with value
你能夠用continue使for循環提早,跳過剩下的部分。看下面這個例子,將一個列表中的整數相加,跳過None:
sequence = [1, 2, None, 4, None, 5]
total = 0
for value in sequence:
if value is None:
continue
total += value
能夠用break
跳出for循環。下面的代碼將各元素相加,直到遇到5:
sequence = [1, 2, 0, 4, 6, 5, 2, 1]
total_until_5 = 0
for value in sequence:
if value == 5:
break
total_until_5 += value
break只中斷for循環的最內層,其他的for循環仍會運行:
In [121]: for i in range(4):
.....: for j in range(4):
.....: if j > i:
.....: break
.....: print((i, j))
.....:
(0, 0)
(1, 0)
(1, 1)
(2, 0)
(2, 1)
(2, 2)
(3, 0)
(3, 1)
(3, 2)
(3, 3)
若是集合或迭代器中的元素序列(元組或列表),能夠用for循環將其方便地拆分紅變量:
for a, b, c in iterator:
# do something
while循環指定了條件和代碼,當條件爲False或用break退出循環,代碼纔會退出:
x = 256
total = 0
while x > 0:
if total > 500:
break
total += x
x = x // 2
pass是Python中的非操做語句。代碼塊不須要任何動做時可使用(做爲未執行代碼的佔位符);由於Python須要使用空白字符劃定代碼塊,因此須要pass:
if x < 0:
print('negative!')
elif x == 0:
# TODO: put something smart here
pass
else:
print('positive!')
range函數返回一個迭代器,它產生一個均勻分佈的整數序列:
In [122]: range(10)
Out[122]: range(0, 10)
In [123]: list(range(10))
Out[123]: [0, 1, 2, 3, 4, 5, 6, 7, 8, 9]
range的三個參數是(起點,終點,步進):
In [124]: list(range(0, 20, 2))
Out[124]: [0, 2, 4, 6, 8, 10, 12, 14, 16, 18]
In [125]: list(range(5, 0, -1))
Out[125]: [5, 4, 3, 2, 1]
能夠看到,range產生的整數不包括終點。range的常見用法是用序號迭代序列:
seq = [1, 2, 3, 4]
for i in range(len(seq)):
val = seq[i]
可使用list來存儲range在其餘數據結構中生成的全部整數,默認的迭代器形式一般是你想要的。下面的代碼對0到99999中3或5的倍數求和:
sum = 0
for i in range(100000):
# % is the modulo operator
if i % 3 == 0 or i % 5 == 0:
sum += i
雖然range能夠產生任意大的數,但任意時刻耗用的內存卻很小。
Python中的三元表達式能夠將if-else語句放到一行裏。語法以下:
value = true-expr if condition else false-expr
true-expr
或false-expr
能夠是任何Python代碼。它和下面的代碼效果相同:
if condition:
value = true-expr
else:
value = false-expr
下面是一個更具體的例子:
In [126]: x = 5
In [127]: 'Non-negative' if x >= 0 else 'Negative'
Out[127]: 'Non-negative'
和if-else同樣,只有一個表達式會被執行。所以,三元表達式中的if和else能夠包含大量的計算,但只有True的分支會被執行。所以,三元表達式中的if和else能夠包含大量的計算,但只有True的分支會被執行。
雖然使用三元表達式能夠壓縮代碼,但會下降代碼可讀性。