第02章 Python語(yǔ)法基礎(chǔ),IPython和Jupyter Notebook

資料來(lái)源:https://github.com/BrambleXu/pydata-notebook

第2章 Python語(yǔ)法基礎(chǔ),IPython和Jupyter Notebooks

當(dāng)我在2011年和2012年寫作本書的第一版時(shí),可用的學(xué)習(xí)Python數(shù)據(jù)分析的資源很少。這部分上是一個(gè)雞和蛋的問(wèn)題:我們現(xiàn)在使用的庫(kù),比如pandas、scikit-learn和statsmodels,那時(shí)相對(duì)來(lái)說(shuō)并不成熟。2017年,數(shù)據(jù)科學(xué)、數(shù)據(jù)分析和機(jī)器學(xué)習(xí)的資源已經(jīng)很多,原來(lái)通用的科學(xué)計(jì)算拓展到了計(jì)算機(jī)科學(xué)家、物理學(xué)家和其它研究領(lǐng)域的工作人員。學(xué)習(xí)Python和成為軟件工程師的優(yōu)秀書籍也有了。

因?yàn)檫@本書是專注于Python數(shù)據(jù)處理的,對(duì)于一些Python的數(shù)據(jù)結(jié)構(gòu)和庫(kù)的特性難免不足。因此,本章和第3章的內(nèi)容只夠你能學(xué)習(xí)本書后面的內(nèi)容。

在我來(lái)看,沒(méi)有必要為了數(shù)據(jù)分析而去精通Python。我鼓勵(lì)你使用IPython shell和Jupyter試驗(yàn)示例代碼,并學(xué)習(xí)不同類型、函數(shù)和方法的文檔。雖然我已盡力讓本書內(nèi)容循序漸進(jìn),但讀者偶爾仍會(huì)碰到?jīng)]有之前介紹過(guò)的內(nèi)容。

本書大部分內(nèi)容關(guān)注的是基于表格的分析和處理大規(guī)模數(shù)據(jù)集的數(shù)據(jù)準(zhǔn)備工具。為了使用這些工具,必須首先將混亂的數(shù)據(jù)規(guī)整為整潔的表格(或結(jié)構(gòu)化)形式。幸好,Python是一個(gè)理想的語(yǔ)言,可以快速整理數(shù)據(jù)。Python使用得越熟練,越容易準(zhǔn)備新數(shù)據(jù)集以進(jìn)行分析。

最好在IPython和Jupyter中親自嘗試本書中使用的工具。當(dāng)你學(xué)會(huì)了如何啟動(dòng)Ipython和Jupyter,我建議你跟隨示例代碼進(jìn)行練習(xí)。與任何鍵盤驅(qū)動(dòng)的操作環(huán)境一樣,記住常見(jiàn)的命令也是學(xué)習(xí)曲線的一部分。

筆記:本章沒(méi)有介紹Python的某些概念,如類和面向?qū)ο缶幊?,你可能?huì)發(fā)現(xiàn)它們?cè)赑ython數(shù)據(jù)分析中很有用。 為了加強(qiáng)Python知識(shí),我建議你學(xué)習(xí)官方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)

2.1 Python解釋器

Python是解釋性語(yǔ)言。Python解釋器同一時(shí)間只能運(yùn)行一個(gè)程序的一條語(yǔ)句。標(biāo)準(zhǔn)的交互Python解釋器可以在命令行中通過(guò)鍵入python命令打開:

$ 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。

運(yùn)行Python程序只需調(diào)用Python的同時(shí),使用一個(gè).py文件作為它的第一個(gè)參數(shù)。假設(shè)創(chuàng)建了一個(gè)hello_world.py文件,它的內(nèi)容是:

print('Hello world')

你可以用下面的命令運(yùn)行它(hello_world.py文件必須位于終端的工作目錄):

$ python hello_world.py
Hello world

一些Python程序員總是這樣執(zhí)行Python代碼的,從事數(shù)據(jù)分析和科學(xué)計(jì)算的人卻會(huì)使用IPython,一個(gè)強(qiáng)化的Python解釋器,或Jupyter notebooks,一個(gè)網(wǎng)頁(yè)代碼筆記本,它原先是IPython的一個(gè)子項(xiàng)目。在本章中,我介紹了如何使用IPython和Jupyter,在附錄A中有更深入的介紹。當(dāng)你使用%run命令,IPython會(huì)同樣執(zhí)行指定文件中的代碼,結(jié)束之后,還可以與結(jié)果交互:

$ 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默認(rèn)采用序號(hào)的格式In [2]:,與標(biāo)準(zhǔn)的>>>提示符不同。

2.2 IPython基礎(chǔ)

在本節(jié)中,我們會(huì)教你打開運(yùn)行IPython shell和jupyter notebook,并介紹一些基本概念。

運(yùn)行IPython Shell

你可以用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

你可以通過(guò)輸入代碼并按Return(或Enter),運(yùn)行任意Python語(yǔ)句。當(dāng)你只輸入一個(gè)變量,它會(huì)顯示代表的對(duì)象:

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代碼語(yǔ)句;第二條語(yǔ)句創(chuàng)建一個(gè)名為data的變量,它引用一個(gè)新創(chuàng)建的Python字典。最后一行打印data的值。

許多Python對(duì)象被格式化為更易讀的形式,或稱作pretty-printed,它與普通的print不同。如果在標(biāo)準(zhǔn)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還支持執(zhí)行任意代碼塊(通過(guò)一個(gè)華麗的復(fù)制-粘貼方法)和整段Python腳本的功能。你也可以使用Jupyter notebook運(yùn)行大代碼塊,接下來(lái)就會(huì)看到。

運(yùn)行Jupyter Notebook

notebook是Jupyter項(xiàng)目的重要組件之一,它是一個(gè)代碼、文本(有標(biāo)記或無(wú)標(biāo)記)、數(shù)據(jù)可視化或其它輸出的交互式文檔。Jupyter Notebook需要與內(nèi)核互動(dòng),內(nèi)核是Jupyter與其它編程語(yǔ)言的交互編程協(xié)議。Python的Jupyter內(nèi)核是使用IPython。要啟動(dòng)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.

在多數(shù)平臺(tái)上,Jupyter會(huì)自動(dòng)打開默認(rèn)的瀏覽器(除非指定了--no-browser)。或者,可以在啟動(dòng)notebook之后,手動(dòng)打開網(wǎng)頁(yè)http://localhost:8888/。圖2-1展示了Google Chrome中的notebook。

筆記:許多人使用Jupyter作為本地的計(jì)算環(huán)境,但它也可以部署到服務(wù)器上遠(yuǎn)程訪問(wèn)。這里不做介紹,如果需要的話,鼓勵(lì)讀者自行到網(wǎng)上學(xué)習(xí)。

圖2-1 Jupyter notebook啟動(dòng)頁(yè)面

要新建一個(gè)notebook,點(diǎn)擊按鈕New,選擇“Python3”或“conda[默認(rèn)項(xiàng)]”。如果是第一次,點(diǎn)擊空格,輸入一行Python代碼。然后按Shift-Enter執(zhí)行。

圖2-2 Jupyter新notebook頁(yè)面

當(dāng)保存notebook時(shí)(File目錄下的Save and Checkpoint),會(huì)創(chuàng)建一個(gè)后綴名為.ipynb的文件。這是一個(gè)自包含文件格式,包含當(dāng)前筆記本中的所有內(nèi)容(包括所有已評(píng)估的代碼輸出)。可以被其它Jupyter用戶加載和編輯。要加載存在的notebook,把它放到啟動(dòng)notebook進(jìn)程的相同目錄內(nèi)。你可以用本書的示例代碼練習(xí),見(jiàn)圖2-3。

雖然Jupyter notebook和IPython shell使用起來(lái)不同,本章中幾乎所有的命令和工具都可以通用。

圖2-3 Jupyter查看一個(gè)存在的notebook的頁(yè)面

Tab補(bǔ)全

從外觀上,IPython shell和標(biāo)準(zhǔn)的Python解釋器只是看起來(lái)不同。IPython shell的進(jìn)步之一是具備其它IDE和交互計(jì)算分析環(huán)境都有的tab補(bǔ)全功能。在shell中輸入表達(dá)式,按下Tab,會(huì)搜索已輸入變量(對(duì)象、函數(shù)等等)的命名空間:

In [1]: an_apple = 27

In [2]: an_example = 42

In [3]: an<Tab>
an_apple    and         an_example  any

在這個(gè)例子中,IPython呈現(xiàn)出了之前兩個(gè)定義的變量和Python的關(guān)鍵字和內(nèi)建的函數(shù)any。當(dāng)然,你也可以補(bǔ)全任何對(duì)象的方法和屬性:

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及以上),自動(dòng)補(bǔ)全功能是下拉框的形式。

筆記:注意,默認(rèn)情況下,IPython會(huì)隱藏下劃線開頭的方法和屬性,比如魔術(shù)方法和內(nèi)部的“私有”方法和屬性,以避免混亂的顯示(和讓新手迷惑?。┻@些也可以tab補(bǔ)全,但是你必須首先鍵入一個(gè)下劃線才能看到它們。如果你喜歡總是在tab補(bǔ)全中看到這樣的方法,你可以IPython配置中進(jìn)行設(shè)置。可以在IPython文檔中查找方法。

除了補(bǔ)全命名、對(duì)象和模塊屬性,Tab還可以補(bǔ)全其它的。當(dāng)輸入看似文件路徑時(shí)(即使是Python字符串),按下Tab也可以補(bǔ)全電腦上對(duì)應(yīng)的文件信息:

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

結(jié)合%run,tab補(bǔ)全可以節(jié)省許多鍵盤操作。

另外,tab補(bǔ)全可以補(bǔ)全函數(shù)的關(guān)鍵詞參數(shù)(包括等于號(hào)=)。見(jiàn)圖2-4。

圖2-4 Jupyter notebook中自動(dòng)補(bǔ)全函數(shù)關(guān)鍵詞

后面會(huì)仔細(xì)地學(xué)習(xí)函數(shù)。

自省

在變量前后使用問(wèn)號(hào)?,可以顯示對(duì)象的信息:

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

這可以作為對(duì)象的自省。如果對(duì)象是一個(gè)函數(shù)或?qū)嵗椒ǎx過(guò)的文檔字符串,也會(huì)顯示出信息。假設(shè)我們寫了一個(gè)如下的函數(shù):

def add_numbers(a, b):
    """
    Add two numbers together

    Returns
    -------
    the_sum : type of arguments
    """
    return a + b

然后使用?符號(hào),就可以顯示如下的文檔字符串:

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

使用??會(huì)顯示函數(shù)的源碼:

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

?還有一個(gè)用途,就是像Unix或Windows命令行一樣搜索IPython的命名空間。字符與通配符結(jié)合可以匹配所有的名字。例如,我們可以獲得所有包含load的頂級(jí)NumPy命名空間:

In [13]: np.*load*?
np.__loader__
np.load
np.loads
np.loadtxt
np.pkgload

%run命令

你可以用%run命令運(yùn)行所有的Python程序。假設(shè)有一個(gè)文件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)

可以如下運(yùn)行:

In [14]: %run ipython_script_test.py

這段腳本運(yùn)行在空的命名空間(沒(méi)有import和其它定義的變量),因此結(jié)果和普通的運(yùn)行方式python script.py相同。文件中所有定義的變量(import、函數(shù)和全局變量,除非拋出異常),都可以在IPython shell中隨后訪問(wèn):

In [15]: c
Out [15]: 7.5

In [16]: result
Out[16]: 1.4666666666666666

如果一個(gè)Python腳本需要命令行參數(shù)(在sys.argv中查找),可以在文件路徑之后傳遞,就像在命令行上運(yùn)行一樣。

筆記:如果想讓一個(gè)腳本訪問(wèn)IPython已經(jīng)定義過(guò)的變量,可以使用%run -i。

在Jupyter notebook中,你也可以使用%load,它將腳本導(dǎo)入到一個(gè)代碼格中:

>>> %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)

中斷運(yùn)行的代碼

代碼運(yùn)行時(shí)按Ctrl-C,無(wú)論是%run或長(zhǎng)時(shí)間運(yùn)行命令,都會(huì)導(dǎo)致KeyboardInterrupt。這會(huì)導(dǎo)致幾乎所有Python程序立即停止,除非一些特殊情況。

警告:當(dāng)Python代碼調(diào)用了一些編譯的擴(kuò)展模塊,按Ctrl-C不一定將執(zhí)行的程序立即停止。在這種情況下,你必須等待,直到控制返回Python解釋器,或者在更糟糕的情況下強(qiáng)制終止Python進(jìn)程。

從剪貼板執(zhí)行程序

如果使用Jupyter notebook,你可以將代碼復(fù)制粘貼到任意代碼格執(zhí)行。在IPython shell中也可以從剪貼板執(zhí)行。假設(shè)在其它應(yīng)用中復(fù)制了如下代碼:

x = 5
y = 7
if x > 5:
    x += 1

    y = 8

最簡(jiǎn)單的方法是使用%paste%cpaste函數(shù)。%paste可以直接運(yùn)行剪貼板中的代碼:

In [17]: %paste
x = 5
y = 7
if x > 5:
    x += 1

    y = 8
## -- End pasted text --

%cpaste功能類似,但會(huì)給出一條提示:

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,你可以粘貼任意多的代碼再運(yùn)行。你可能想在運(yùn)行前,先看看代碼。如果粘貼了錯(cuò)誤的代碼,可以用Ctrl-C中斷。

鍵盤快捷鍵

IPython有許多鍵盤快捷鍵進(jìn)行導(dǎo)航提示(類似Emacs文本編輯器或UNIX bash Shell)和交互shell的歷史命令。表2-1總結(jié)了常見(jiàn)的快捷鍵。圖2-5展示了一部分,如移動(dòng)光標(biāo)。

圖2-5 IPython shell中一些快捷鍵的說(shuō)明
表2-1 IPython的標(biāo)準(zhǔn)快捷鍵

Jupyter notebooks有另外一套龐大的快捷鍵。因?yàn)樗目旖萱I比IPython的變化快,建議你參閱Jupyter notebook的幫助文檔。

魔術(shù)命令

IPython中特殊的命令(Python中沒(méi)有)被稱作“魔術(shù)”命令。這些命令可以使普通任務(wù)更便捷,更容易控制IPython系統(tǒng)。魔術(shù)命令是在指令前添加百分號(hào)%前綴。例如,可以用%timeit(這個(gè)命令后面會(huì)詳談)測(cè)量任何Python語(yǔ)句,例如矩陣乘法,的執(zhí)行時(shí)間:

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

魔術(shù)命令可以被看做IPython中運(yùn)行的命令行。許多魔術(shù)命令有“命令行”選項(xiàng),可以通過(guò)?查看:

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.

魔術(shù)函數(shù)默認(rèn)可以不用百分號(hào),只要沒(méi)有變量和函數(shù)名相同。這個(gè)特點(diǎn)被稱為“自動(dòng)魔術(shù)”,可以用%automagic打開或關(guān)閉。

一些魔術(shù)函數(shù)與Python函數(shù)很像,它的結(jié)果可以賦值給一個(gè)變量:

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學(xué)習(xí)下所有特殊命令。表2-2列出了一些可以提高生產(chǎn)率的交互計(jì)算和Python開發(fā)的IPython指令。

表2-2 一些常用的IPython魔術(shù)命令

集成Matplotlib

IPython在分析計(jì)算領(lǐng)域能夠流行的原因之一是它非常好的集成了數(shù)據(jù)可視化和其它用戶界面庫(kù),比如matplotlib。不用擔(dān)心以前沒(méi)用過(guò)matplotlib,本書后面會(huì)詳細(xì)介紹。%matplotlib魔術(shù)函數(shù)配置了IPython shell和Jupyter notebook中的matplotlib。這點(diǎn)很重要,其它創(chuàng)建的圖不會(huì)出現(xiàn)(notebook)或獲取session的控制,直到結(jié)束(shell)。

在IPython shell中,運(yùn)行%matplotlib可以進(jìn)行設(shè)置,可以創(chuàng)建多個(gè)繪圖窗口,而不會(huì)干擾控制臺(tái)session:

In [26]: %matplotlib
Using matplotlib backend: Qt4Agg

在JUpyter中,命令有所不同(圖2-6):

In [26]: %matplotlib inline
圖2-6 Jupyter行內(nèi)matplotlib作圖

2.3 Python語(yǔ)法基礎(chǔ)

在本節(jié)中,我將概述基本的Python概念和語(yǔ)言機(jī)制。在下一章,我將詳細(xì)介紹Python的數(shù)據(jù)結(jié)構(gòu)、函數(shù)和其它內(nèi)建工具。

語(yǔ)言的語(yǔ)義

Python的語(yǔ)言設(shè)計(jì)強(qiáng)調(diào)的是可讀性、簡(jiǎn)潔和清晰。有些人稱Python為“可執(zhí)行的偽代碼”。

使用縮進(jìn),而不是括號(hào)

Python使用空白字符(tab和空格)來(lái)組織代碼,而不是像其它語(yǔ)言,比如R、C++、JAVA和Perl那樣使用括號(hào)??匆粋€(gè)排序算法的for循環(huán):

for x in array:
    if x < pivot:
        less.append(x)
    else:
        greater.append(x)

冒號(hào)標(biāo)志著縮進(jìn)代碼塊的開始,冒號(hào)之后的所有代碼的縮進(jìn)量必須相同,直到代碼塊結(jié)束。不管是否喜歡這種形式,使用空白符是Python程序員開發(fā)的一部分,在我看來(lái),這可以讓python的代碼可讀性大大優(yōu)于其它語(yǔ)言。雖然期初看起來(lái)很奇怪,經(jīng)過(guò)一段時(shí)間,你就能適應(yīng)了。

筆記:我強(qiáng)烈建議你使用四個(gè)空格作為默認(rèn)的縮進(jìn),可以使用tab代替四個(gè)空格。許多文本編輯器的設(shè)置是使用制表位替代空格。某些人使用tabs或不同數(shù)目的空格數(shù),常見(jiàn)的是使用兩個(gè)空格。大多數(shù)情況下,四個(gè)空格是大多數(shù)人采用的方法,因此建議你也這樣做。

你應(yīng)該已經(jīng)看到,Python的語(yǔ)句不需要用分號(hào)結(jié)尾。但是,分號(hào)卻可以用來(lái)給同在一行的語(yǔ)句切分:

a = 5; b = 6; c = 7

Python不建議將多條語(yǔ)句放到一行,這會(huì)降低代碼的可讀性。

萬(wàn)物皆對(duì)象

Python語(yǔ)言的一個(gè)重要特性就是它的對(duì)象模型的一致性。每個(gè)數(shù)字、字符串、數(shù)據(jù)結(jié)構(gòu)、函數(shù)、類、模塊等等,都是在Python解釋器的自有“盒子”內(nèi),它被認(rèn)為是Python對(duì)象。每個(gè)對(duì)象都有類型(例如,字符串或函數(shù))和內(nèi)部數(shù)據(jù)。在實(shí)際中,這可以讓語(yǔ)言非常靈活,因?yàn)楹瘮?shù)也可以被當(dāng)做對(duì)象使用。

注釋

任何前面帶有井號(hào)#的文本都會(huì)被Python解釋器忽略。這通常被用來(lái)添加注釋。有時(shí),你會(huì)想排除一段代碼,但并不刪除。簡(jiǎn)便的方法就是將其注釋掉:

results = []
for line in file_handle:
    # keep the empty lines for now
    # if len(line) == 0:
    #   continue
    results.append(line.replace('foo', 'bar'))

也可以在執(zhí)行過(guò)的代碼后面添加注釋。一些人習(xí)慣在代碼之前添加注釋,前者這種方法有時(shí)也是有用的:

print("Reached this line")  # Simple status report

函數(shù)和對(duì)象方法調(diào)用

你可以用圓括號(hào)調(diào)用函數(shù),傳遞零個(gè)或幾個(gè)參數(shù),或者將返回值給一個(gè)變量:

result = f(x, y, z)
g()

幾乎Python中的每個(gè)對(duì)象都有附加的函數(shù),稱作方法,可以用來(lái)訪問(wèn)對(duì)象的內(nèi)容。可以用下面的語(yǔ)句調(diào)用:

obj.some_method(x, y, z)

函數(shù)可以使用位置和關(guān)鍵詞參數(shù):

result = f(a, b, c, d=5, e='foo')

后面會(huì)有更多介紹。

變量和參數(shù)傳遞

當(dāng)在Python中創(chuàng)建變量(或名字),你就在等號(hào)右邊創(chuàng)建了一個(gè)對(duì)這個(gè)變量的引用。考慮一個(gè)整數(shù)列表:

In [8]: a = [1, 2, 3]

假設(shè)將a賦值給一個(gè)新變量b:

In [9]: b = a

在有些方法中,這個(gè)賦值會(huì)將數(shù)據(jù)[1, 2, 3]也復(fù)制。在Python中,a和b實(shí)際上是同一個(gè)對(duì)象,即原有列表[1, 2, 3](見(jiàn)圖2-7)。你可以在a中添加一個(gè)元素,然后檢查b:

In [10]: a.append(4)

In [11]: b
Out[11]: [1, 2, 3, 4]
圖2-7 對(duì)同一對(duì)象的雙重引用

理解Python的引用的含義,數(shù)據(jù)是何時(shí)、如何、為何復(fù)制的,是非常重要的。尤其是當(dāng)你用Python處理大的數(shù)據(jù)集時(shí)。

筆記:賦值也被稱作綁定,我們是把一個(gè)名字綁定給一個(gè)對(duì)象。變量名有時(shí)可能被稱為綁定變量。

當(dāng)你將對(duì)象作為參數(shù)傳遞給函數(shù)時(shí),新的局域變量創(chuàng)建了對(duì)原始對(duì)象的引用,而不是復(fù)制。如果在函數(shù)里綁定一個(gè)新對(duì)象到一個(gè)變量,這個(gè)變動(dòng)不會(huì)反映到上一層。因此可以改變可變參數(shù)的內(nèi)容。假設(shè)有以下函數(shù):

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]

動(dòng)態(tài)引用,強(qiáng)類型

與許多編譯語(yǔ)言(如JAVA和C++)對(duì)比,Python中的對(duì)象引用不包含附屬的類型。下面的代碼是沒(méi)有問(wèn)題的:

In [12]: a = 5

In [13]: type(a)
Out[13]: int

In [14]: a = 'foo'

In [15]: type(a)
Out[15]: str

變量是在特殊命名空間中的對(duì)象的名字,類型信息保存在對(duì)象自身中。一些人可能會(huì)說(shuō)Python不是“類型化語(yǔ)言”。這是不正確的,看下面的例子:

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

在某些語(yǔ)言中,例如Visual Basic,字符串‘5’可能被默許轉(zhuǎn)換(或投射)為整數(shù),因此會(huì)產(chǎn)生10。但在其它語(yǔ)言中,例如JavaScript,整數(shù)5會(huì)被投射成字符串,結(jié)果是聯(lián)結(jié)字符串‘55’。在這個(gè)方面,Python被認(rèn)為是強(qiáng)類型化語(yǔ)言,意味著每個(gè)對(duì)象都有明確的類型(或類),默許轉(zhuǎn)換只會(huì)發(fā)生在特定的情況下,例如:

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

知道對(duì)象的類型很重要,最好能讓函數(shù)可以處理多種類型的輸入。你可以用isinstance函數(shù)檢查對(duì)象是某個(gè)類型的實(shí)例:

In [21]: a = 5

In [22]: isinstance(a, int)
Out[22]: True

isinstance可以用類型元組,檢查對(duì)象的類型是否在元組中:

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的對(duì)象通常都有屬性(其它存儲(chǔ)在對(duì)象內(nèi)部的Python對(duì)象)和方法(對(duì)象的附屬函數(shù)可以訪問(wèn)對(duì)象的內(nèi)部數(shù)據(jù))??梢杂?code>obj.attribute_name訪問(wèn)屬性和方法:

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函數(shù),通過(guò)名字訪問(wèn)屬性和方法:

In [27]: getattr(a, 'split')
Out[27]: <function str.split>

在其它語(yǔ)言中,訪問(wèn)對(duì)象的名字通常稱作“反射”。本書不會(huì)大量使用getattr函數(shù)和相關(guān)的hasattrsetattr函數(shù),使用這些函數(shù)可以高效編寫原生的、可重復(fù)使用的代碼。

鴨子類型

經(jīng)常地,你可能不關(guān)心對(duì)象的類型,只關(guān)心對(duì)象是否有某些方法或用途。這通常被稱為“鴨子類型”,來(lái)自“走起來(lái)像鴨子、叫起來(lái)像鴨子,那么它就是鴨子”的說(shuō)法。例如,你可以通過(guò)驗(yàn)證一個(gè)對(duì)象是否遵循迭代協(xié)議,判斷它是可迭代的。對(duì)于許多對(duì)象,這意味著它有一個(gè)__iter__魔術(shù)方法,其它更好的判斷方法是使用iter函數(shù):

def isiterable(obj):
    try:
        iter(obj)
        return True
    except TypeError: # not iterable
        return False

這個(gè)函數(shù)會(huì)返回字符串以及大多數(shù)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

我總是用這個(gè)功能編寫可以接受多種輸入類型的函數(shù)。常見(jiàn)的例子是編寫一個(gè)函數(shù)可以接受任意類型的序列(list、tuple、ndarray)或是迭代器。你可先檢驗(yàn)對(duì)象是否是列表(或是NUmPy數(shù)組),如果不是的話,將其轉(zhuǎn)變成列表:

if not isinstance(x, list) and isiterable(x):
    x = list(x)

引入

在Python中,模塊就是一個(gè)有.py擴(kuò)展名、包含Python代碼的文件。假設(shè)有以下模塊:

# some_module.py
PI = 3.14159

def f(x):
    return x + 2

def g(a, b):
    return a + b

如果想從同目錄下的另一個(gè)文件訪問(wèn)some_module.py中定義的變量和函數(shù),可以:

import some_module
result = some_module.f(5)
pi = some_module.PI

或者:

from some_module import f, g, PI
result = g(5, PI)

使用as關(guān)鍵詞,你可以給引入起不同的變量名:

import some_module as sm
from some_module import PI as pi, g as gf

r1 = sm.f(pi)
r2 = gf(6, pi)

二元運(yùn)算符和比較運(yùn)算符

大多數(shù)二元數(shù)學(xué)運(yùn)算和比較都不難想到:

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列出了所有的二元運(yùn)算符。

要判斷兩個(gè)引用是否指向同一個(gè)對(duì)象,可以使用is方法。is not可以判斷兩個(gè)對(duì)象是不同的:

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

因?yàn)?code>list總是創(chuàng)建一個(gè)新的Python列表(即復(fù)制),我們可以斷定c是不同于a的。使用is比較與==運(yùn)算符不同,如下:

In [40]: a == c
Out[40]: True

isis not常用來(lái)判斷一個(gè)變量是否為None,因?yàn)橹挥幸粋€(gè)None的實(shí)例:

In [41]: a = None

In [42]: a is None
Out[42]: True
表2-3 二元運(yùn)算符

可變與不可變對(duì)象

Python中的大多數(shù)對(duì)象,比如列表、字典、NumPy數(shù)組,和用戶定義的類型(類),都是可變的。意味著這些對(duì)象或包含的值可以被修改:

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

記住,可以修改一個(gè)對(duì)象并不意味就要修改它。這被稱為副作用。例如,當(dāng)寫一個(gè)函數(shù),任何副作用都要在文檔或注釋中寫明。如果可能的話,我推薦避免副作用,采用不可變的方式,即使要用到可變對(duì)象。

標(biāo)量類型

Python的標(biāo)準(zhǔn)庫(kù)中有一些內(nèi)建的類型,用于處理數(shù)值數(shù)據(jù)、字符串、布爾值,和日期時(shí)間。這些單值類型被稱為標(biāo)量類型,本書中稱其為標(biāo)量。表2-4列出了主要的標(biāo)量。日期和時(shí)間處理會(huì)另外討論,因?yàn)樗鼈兪菢?biāo)準(zhǔn)庫(kù)的datetime模塊提供的。

表2-4 Python的標(biāo)量

數(shù)值類型

Python的主要數(shù)值類型是intfloat。int可以存儲(chǔ)任意大的數(shù):

In [48]: ival = 17239871

In [49]: ival ** 6
Out[49]: 26254519291092456596965462913230729701102721

浮點(diǎn)數(shù)使用Python的float類型。每個(gè)數(shù)都是雙精度(64位)的值。也可以用科學(xué)計(jì)數(shù)法表示:

In [50]: fval = 7.243

In [51]: fval2 = 6.78e-5

不能得到整數(shù)的除法會(huì)得到浮點(diǎn)數(shù):

In [52]: 3 / 2
Out[52]: 1.5

要獲得C-風(fēng)格的整除(去掉小數(shù)部分),可以使用底除運(yùn)算符//:

In [53]: 3 // 2
Out[53]: 1

字符串

許多人是因?yàn)镻ython強(qiáng)大而靈活的字符串處理而使用Python的。你可以用單引號(hào)或雙引號(hào)來(lái)寫字符串:

a = 'one way of writing a string'
b = "another way"

對(duì)于有換行符的字符串,可以使用三引號(hào),'''或"""都行:

c = """
This is a longer string that
spans multiple lines
"""

字符串c實(shí)際包含四行文本,"""后面和lines后面的換行符??梢杂?code>count方法計(jì)算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'

經(jīng)過(guò)以上的操作,變量a并沒(méi)有被修改:

In [60]: a
Out[60]: 'this is a string'

許多Python對(duì)象使用str函數(shù)可以被轉(zhuǎn)化為字符串:

In [61]: a = 5.6

In [62]: s = str(a)

In [63]: print(s)
5.6

字符串是一個(gè)序列的Unicode字符,因此可以像其它序列,比如列表和元組(下一章會(huì)詳細(xì)介紹兩者)一樣處理:

In [64]: s = 'python'

In [65]: list(s)
Out[65]: ['p', 'y', 't', 'h', 'o', 'n']

In [66]: s[:3]
Out[66]: 'pyt'

語(yǔ)法s[:3]被稱作切片,適用于許多Python序列。后面會(huì)更詳細(xì)的介紹,本書中用到很多切片。

反斜杠是轉(zhuǎn)義字符,意思是它備用來(lái)表示特殊字符,比如換行符\n或Unicode字符。要寫一個(gè)包含反斜杠的字符串,需要進(jìn)行轉(zhuǎn)義:

In [67]: s = '12\\34'

In [68]: print(s)
12\34

如果字符串中包含許多反斜杠,但沒(méi)有特殊字符,這樣做就很麻煩。幸好,可以在字符串前面加一個(gè)r,表明字符就是它自身:

In [69]: s = r'this\has\no\special\characters'

In [70]: s
Out[70]: 'this\\has\\no\\special\\characters'

r表示raw。

將兩個(gè)字符串合并,會(huì)產(chǎn)生一個(gè)新的字符串:

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'

字符串的模板化或格式化,是另一個(gè)重要的主題。Python 3拓展了此類的方法,這里只介紹一些。字符串對(duì)象有format方法,可以替換格式化的參數(shù)為字符串,產(chǎn)生一個(gè)新的字符串:

In [74]: template = '{0:.2f} {1:s} are worth US${2:d}'

在這個(gè)字符串中,

  • {0:.2f}表示格式化第一個(gè)參數(shù)為帶有兩位小數(shù)的浮點(diǎn)數(shù)。
  • {1:s}表示格式化第二個(gè)參數(shù)為字符串。
  • {2:d}表示格式化第三個(gè)參數(shù)為一個(gè)整數(shù)。

要替換參數(shù)為這些格式化的參數(shù),我們傳遞format方法一個(gè)序列:

In [75]: template.format(4.5560, 'Argentine Pesos', 1)
Out[75]: '4.56 Argentine Pesos are worth US$1'

字符串格式化是一個(gè)很深的主題,有多種方法和大量的選項(xiàng),可以控制字符串中的值是如何格式化的。推薦參閱Python官方文檔。

這里概括介紹字符串處理,第8章的數(shù)據(jù)分析會(huì)詳細(xì)介紹。

字節(jié)和Unicode

在Python 3及以上版本中,Unicode是一級(jí)的字符串類型,這樣可以更一致的處理ASCII和Non-ASCII文本。在老的Python版本中,字符串都是字節(jié),不使用Unicode編碼。假如知道字符編碼,可以將其轉(zhuǎn)化為Unicode。看一個(gè)例子:

In [76]: val = "espa?ol"

In [77]: val
Out[77]: 'espa?ol'

可以用encode將這個(gè)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

如果你知道一個(gè)字節(jié)對(duì)象的Unicode編碼,用decode方法可以解碼:

In [81]: val_utf8.decode('utf-8')
Out[81]: 'espa?ol'

雖然UTF-8編碼已經(jīng)變成主流,但因?yàn)闅v史的原因,你仍然可能碰到其它編碼的數(shù)據(jù):

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'

工作中碰到的文件很多都是字節(jié)對(duì)象,盲目地將所有數(shù)據(jù)編碼為Unicode是不可取的。

雖然用的不多,你可以在字節(jié)文本的前面加上一個(gè)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中的布爾值有兩個(gè),True和False。比較和其它條件表達(dá)式可以用True和False判斷。布爾值可以與and和or結(jié)合使用:

In [89]: True and True
Out[89]: True

In [90]: False or True
Out[90]: True

類型轉(zhuǎn)換

str、bool、int和float也是函數(shù),可以用來(lái)轉(zhuǎn)換類型:

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

None是Python的空值類型。如果一個(gè)函數(shù)沒(méi)有明確的返回值,就會(huì)默認(rèn)返回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也常常作為函數(shù)的默認(rèn)參數(shù):

def add_and_maybe_multiply(a, b, c=None):
    result = a + b

    if c is not None:
        result = result * c

    return result

另外,None不僅是一個(gè)保留字,還是唯一的NoneType的實(shí)例:

In [101]: type(None)
Out[101]: NoneType

日期和時(shí)間

Python內(nèi)建的datetime模塊提供了datetime、datetime類型。datetime類型結(jié)合了datetime,是最常使用的:

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

根據(jù)datetime實(shí)例,你可以用datetime提取出各自的對(duì)象:

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可以將字符串轉(zhuǎn)換成datetime對(duì)象:

In [109]: datetime.strptime('20091031', '%Y%m%d')
Out[109]: datetime.datetime(2009, 10, 31, 0, 0)

表2-5列出了所有的格式化命令。

表2-5 Datetime格式化指令(與ISO C89兼容)

當(dāng)你聚類或?qū)r(shí)間序列進(jìn)行分組,替換datetimes的time字段有時(shí)會(huì)很有用。例如,用0替換分和秒:

In [110]: dt.replace(minute=0, second=0)
Out[110]: datetime.datetime(2011, 10, 29, 20, 0)

因?yàn)?code>datetime.datetime是不可變類型,上面的方法會(huì)產(chǎn)生新的對(duì)象。

兩個(gè)datetime對(duì)象的差會(huì)產(chǎn)生一個(gè)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

結(jié)果timedelta(17, 7179)指明了timedelta將17天、7179秒的編碼方式。

timedelta添加到datetime,會(huì)產(chǎn)生一個(gè)新的偏移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有若干內(nèi)建的關(guān)鍵字進(jìn)行條件邏輯、循環(huán)和其它控制流操作。

if、elif和else

if是最廣為人知的控制流語(yǔ)句。它檢查一個(gè)條件,如果為True,就執(zhí)行后面的語(yǔ)句:

if x < 0:
    print('It's negative')

if后面可以跟一個(gè)或多個(gè)elif,所有條件都是False時(shí),還可以添加一個(gè)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')

如果某個(gè)條件為True,后面的elif就不會(huì)被執(zhí)行。當(dāng)使用and和or時(shí),復(fù)合條件語(yǔ)句是從左到右執(zhí)行:

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

在這個(gè)例子中,c > d不會(huì)被執(zhí)行,因?yàn)榈谝粋€(gè)比較是True:

也可以把比較式串在一起:

In [120]: 4 > 3 > 2 > 1
Out[120]: True

for循環(huán)

for循環(huán)是在一個(gè)集合(列表或元組)中進(jìn)行迭代,或者就是一個(gè)迭代器。for循環(huán)的標(biāo)準(zhǔn)語(yǔ)法是:

for value in collection:
    # do something with value

你可以用continue使for循環(huán)提前,跳過(guò)剩下的部分??聪旅孢@個(gè)例子,將一個(gè)列表中的整數(shù)相加,跳過(guò)None:

sequence = [1, 2, None, 4, None, 5]
total = 0
for value in sequence:
    if value is None:
        continue
    total += value

可以用break跳出for循環(huán)。下面的代碼將各元素相加,直到遇到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循環(huán)的最內(nèi)層,其余的for循環(huán)仍會(huì)運(yùn)行:

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循環(huán)將其方便地拆分成變量:

for a, b, c in iterator:
    # do something

While循環(huán)

while循環(huán)指定了條件和代碼,當(dāng)條件為False或用break退出循環(huán),代碼才會(huì)退出:

x = 256
total = 0
while x > 0:
    if total > 500:
        break
    total += x
    x = x // 2

pass

pass是Python中的非操作語(yǔ)句。代碼塊不需要任何動(dòng)作時(shí)可以使用(作為未執(zhí)行代碼的占位符);因?yàn)镻ython需要使用空白字符劃定代碼塊,所以需要pass:

if x < 0:
    print('negative!')
elif x == 0:
    # TODO: put something smart here
    pass
else:
    print('positive!')

range

range函數(shù)返回一個(gè)迭代器,它產(chǎn)生一個(gè)均勻分布的整數(shù)序列:

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的三個(gè)參數(shù)是(起點(diǎn),終點(diǎn),步進(jìn)):

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產(chǎn)生的整數(shù)不包括終點(diǎn)。range的常見(jiàn)用法是用序號(hào)迭代序列:

seq = [1, 2, 3, 4]
for i in range(len(seq)):
    val = seq[i]

可以使用list來(lái)存儲(chǔ)range在其他數(shù)據(jù)結(jié)構(gòu)中生成的所有整數(shù),默認(rèn)的迭代器形式通常是你想要的。下面的代碼對(duì)0到99999中3或5的倍數(shù)求和:

sum = 0
for i in range(100000):
    # % is the modulo operator
    if i % 3 == 0 or i % 5 == 0:
        sum += i

雖然range可以產(chǎn)生任意大的數(shù),但任意時(shí)刻耗用的內(nèi)存卻很小。

三元表達(dá)式

Python中的三元表達(dá)式可以將if-else語(yǔ)句放到一行里。語(yǔ)法如下:

value = true-expr if condition else false-expr

true-exprfalse-expr可以是任何Python代碼。它和下面的代碼效果相同:

if condition:
    value = true-expr
else:
    value = false-expr

下面是一個(gè)更具體的例子:

In [126]: x = 5

In [127]: 'Non-negative' if x >= 0 else 'Negative'
Out[127]: 'Non-negative'

和if-else一樣,只有一個(gè)表達(dá)式會(huì)被執(zhí)行。因此,三元表達(dá)式中的if和else可以包含大量的計(jì)算,但只有True的分支會(huì)被執(zhí)行。因此,三元表達(dá)式中的if和else可以包含大量的計(jì)算,但只有True的分支會(huì)被執(zhí)行。

雖然使用三元表達(dá)式可以壓縮代碼,但會(huì)降低代碼可讀性。

最后編輯于
?著作權(quán)歸作者所有,轉(zhuǎn)載或內(nèi)容合作請(qǐng)聯(lián)系作者
【社區(qū)內(nèi)容提示】社區(qū)部分內(nèi)容疑似由AI輔助生成,瀏覽時(shí)請(qǐng)結(jié)合常識(shí)與多方信息審慎甄別。
平臺(tái)聲明:文章內(nèi)容(如有圖片或視頻亦包括在內(nèi))由作者上傳并發(fā)布,文章內(nèi)容僅代表作者本人觀點(diǎn),簡(jiǎn)書系信息發(fā)布平臺(tái),僅提供信息存儲(chǔ)服務(wù)。

友情鏈接更多精彩內(nèi)容