中文字幕理论片,69视频免费在线观看,亚洲成人app,国产1级毛片,刘涛最大尺度戏视频,欧美亚洲美女视频,2021韩国美女仙女屋vip视频

打開APP
userphoto
未登錄

開通VIP,暢享免費(fèi)電子書等14項(xiàng)超值服

開通VIP
Python知識(shí)點(diǎn)匯總
*/ * Copyright (c) 2016,煙臺(tái)大學(xué)計(jì)算機(jī)與控制工程學(xué)院 * All rights reserved. * 文件名:text.cpp * 作者:常軒 ** 完成日期:2016年7月31日 * 版本號(hào):V1.0 * 程序輸入:無 * 程序輸出:見運(yùn)行結(jié)果 */Python知識(shí)點(diǎn)總結(jié)數(shù)據(jù)類型

計(jì)算機(jī)顧名思義就是可以做數(shù)學(xué)計(jì)算的機(jī)器,因此,計(jì)算機(jī)程序理所當(dāng)然地可以處理各種數(shù)值。但是,計(jì)算機(jī)能處理的遠(yuǎn)不止數(shù)值,還可以處理文本、圖形、音頻、視頻、網(wǎng)頁等各種各樣的數(shù)據(jù),不同的數(shù)據(jù),需要定義不同的數(shù)據(jù)類型。在Python中,能夠直接處理的數(shù)據(jù)類型有以下幾種:

一、整數(shù)

Python可以處理任意大小的整數(shù),當(dāng)然包括負(fù)整數(shù),在Python程序中,整數(shù)的表示方法和數(shù)學(xué)上的寫法一模一樣,例如:1100,-8080,0,等等。

計(jì)算機(jī)由于使用二進(jìn)制,所以,有時(shí)候用十六進(jìn)制表示整數(shù)比較方便,十六進(jìn)制用0x前綴和0-9,a-f表示,例如:0xff000xa5b4c3d2,等等。

二、浮點(diǎn)數(shù)

浮點(diǎn)數(shù)也就是小數(shù),之所以稱為浮點(diǎn)數(shù),是因?yàn)榘凑湛茖W(xué)記數(shù)法表示時(shí),一個(gè)浮點(diǎn)數(shù)的小數(shù)點(diǎn)位置是可變的,比如,1.23x10^9和12.3x10^8是相等的。浮點(diǎn)數(shù)可以用數(shù)學(xué)寫法,如1.233.14,-9.01,等等。但是對(duì)于很大或很小的浮點(diǎn)數(shù),就必須用科學(xué)計(jì)數(shù)法表示,把10用e替代,1.23x10^9就是1.23e9,或者12.3e8,0.000012可以寫成1.2e-5,等等。

整數(shù)和浮點(diǎn)數(shù)在計(jì)算機(jī)內(nèi)部存儲(chǔ)的方式是不同的,整數(shù)運(yùn)算永遠(yuǎn)是精確的(除法難道也是精確的?是的?。?,而浮點(diǎn)數(shù)運(yùn)算則可能會(huì)有四舍五入的誤差。

三、字符串

字符串是以''''括起來的任意文本,比如'abc''xyz'等等。請(qǐng)注意,''''本身只是一種表示方式,不是字符串的一部分,因此,字符串'abc'只有a,b,c這3個(gè)字符。

注:在PHP中也存在對(duì)字符串可以用’’或者””括起來,但是在雙引號(hào)中的變量會(huì)打印出來,而單引號(hào)中的變量則變成一個(gè)普通的字符串。例如:

$foo = 2;

echo 'foo is $foo'; // 打印結(jié)果: foo is 2 echo 'foo is $foo'; // 打印結(jié)果: foo is $foo

四、布爾值

布爾值和布爾代數(shù)的表示完全一致,一個(gè)布爾值只有True、False兩種值,要么是True,要么是False,在Python中,可以直接用True、False表示布爾值(請(qǐng)注意大小寫),也可以通過布爾運(yùn)算計(jì)算出來。

布爾值可以用and、ornot運(yùn)算。

and運(yùn)算是與運(yùn)算,只有所有都為 True,and運(yùn)算結(jié)果才是 True。

or運(yùn)算是或運(yùn)算,只要其中有一個(gè)為 True,or 運(yùn)算結(jié)果就是 True。

not運(yùn)算是非運(yùn)算,它是一個(gè)單目運(yùn)算符,把 True 變成 False,F(xiàn)alse 變成 True。

注:C++中也有布爾值,而在C中則沒有這一變量

五、空值

空值是Python里一個(gè)特殊的值,用None表示。None不能理解為0,因?yàn)?是有意義的,而None是一個(gè)特殊的空值。

此外,Python還提供了列表、字典等多種數(shù)據(jù)類型,還允許創(chuàng)建自定義數(shù)據(jù)類型,我們后面會(huì)繼續(xù)講到

print語句

print語句可以向屏幕上輸出指定的文字。比如輸出'hello, world',用代碼實(shí)現(xiàn)如下:

>>> print 'hello, world'

注意:

1.當(dāng)我們?cè)赑ython交互式環(huán)境下編寫代碼時(shí),Python解釋器的提示符,不是代碼的一部分。

2.當(dāng)我們?cè)谖谋揪庉嬈髦芯帉懘a時(shí),千萬不要自己添加 >>>。

print語句也可以跟上多個(gè)字符串,用逗號(hào)“,”隔開,就可以連成一串輸出:

>>> print 'The quick brown fox', 'jumps over', 'the lazy dog'

The quick brown fox jumps over the lazy dog

print會(huì)依次打印每個(gè)字符串,遇到逗號(hào)“,”會(huì)輸出一個(gè)空格,因此,輸出的字符串是這樣拼起來的:

print也可以打印整數(shù),或者計(jì)算結(jié)果:

>>> print 300

300 #運(yùn)行結(jié)果

>>> print 100 + 200

300 #運(yùn)行結(jié)果

因此,我們可以把計(jì)算100 + 200的結(jié)果打印得更漂亮一點(diǎn):

>>> print '100 + 200 =', 100 + 200

100 + 200 = 300 #運(yùn)行結(jié)果

注意: 對(duì)于100 + 200,Python解釋器自動(dòng)計(jì)算出結(jié)果300,但是,'100 + 200 ='是字符串而非數(shù)學(xué)公式,Python把它視為字符串,請(qǐng)自行解釋上述打印結(jié)果。

注釋

任何時(shí)候,我們都可以給程序加上注釋。注釋是用來說明代碼的,給自己或別人看,而程序運(yùn)行的時(shí)候,Python解釋器會(huì)直接忽略掉注釋,所以,有沒有注釋不影響程序的執(zhí)行結(jié)果,但是影響到別人能不能看懂你的代碼。給自己的代碼寫上注釋,這是一個(gè)非常好的工作習(xí)慣。

HTML

PHP中有多種注釋符號(hào):

1.使用#,#這是注釋

2.使用/* */,/*這是注釋*/

3.使用//,//這是注釋

后兩種與C語言中的注釋符號(hào)相同

Python的注釋以 # 開頭,后面的文字直到行尾都算注釋

# 這一行全部都是注釋...

print 'hello' # 這也是注釋

注釋還有一個(gè)巧妙的用途,就是一些代碼我們不想運(yùn)行,但又不想刪除,就可以用注釋暫時(shí)屏蔽掉:

# 暫時(shí)不想運(yùn)行下面一行代碼:

# print 'hello, python.'

什么是變量

在Python中,變量的概念基本上和初中代數(shù)的方程變量是一致的。

例如,對(duì)于方程式 y=x*x ,x就是變量。當(dāng)x=2時(shí),計(jì)算結(jié)果是4,當(dāng)x=5時(shí),計(jì)算結(jié)果是25。

只是在計(jì)算機(jī)程序中,變量不僅可以是數(shù)字,還可以是任意數(shù)據(jù)類型。

在Python程序中,變量是用一個(gè)變量名表示,變量名必須是大小寫英文、數(shù)字和下劃線(_)的組合,且不能用數(shù)字開頭,比如:

a = 1

變量a是一個(gè)整數(shù)。

t_007 = 'T007'

變量t_007是一個(gè)字符串。

在Python中,等號(hào)=是賦值語句,可以把任意數(shù)據(jù)類型賦值給變量,同一個(gè)變量可以反復(fù)賦值,而且可以是不同類型的變量,例如:

a = 123 # a是整數(shù)

print a

a = 'xuan' # a變?yōu)樽址?/span>

print a

這種變量本身類型不固定的語言稱之為動(dòng)態(tài)語言,與之對(duì)應(yīng)的是靜態(tài)語言。

靜態(tài)語言在定義變量時(shí)必須指定變量類型,如果賦值的時(shí)候類型不匹配,就會(huì)報(bào)錯(cuò)。例如Java是靜態(tài)語言,賦值語句如下(// 表示注釋):

int a = 123; // a是整數(shù)類型變量

a = 'xuan'; // 錯(cuò)誤:不能把字符串賦給整型變量

和靜態(tài)語言相比,動(dòng)態(tài)語言更靈活,就是這個(gè)原因。

請(qǐng)不要把賦值語句的等號(hào)等同于數(shù)學(xué)的等號(hào)。比如下面的代碼:

x = 10

x = x + 2

如果從數(shù)學(xué)上理解x = x + 2那無論如何是不成立的,在程序中,賦值語句先計(jì)算右側(cè)的表達(dá)式x + 2,得到結(jié)果12,再賦給變量x。由于x之前的值是10,重新賦值后,x的值變成12。

最后,理解變量在計(jì)算機(jī)內(nèi)存中的表示也非常重要。當(dāng)我們寫:a = 'ABC'時(shí),Python解釋器干了兩件事情:

1. 在內(nèi)存中創(chuàng)建了一個(gè)'ABC'的字符串;

2. 在內(nèi)存中創(chuàng)建了一個(gè)名為a的變量,并把它指向'ABC'。

也可以把一個(gè)變量a賦值給另一個(gè)變量b,這個(gè)操作實(shí)際上是把變量b指向變量a所指向的數(shù)據(jù),例如下面的代碼:

a = 'ABC'

b = a

a = 'XYZ'

print b

最后一行打印出變量b的內(nèi)容到底是'ABC'呢還是'XYZ'?如果從數(shù)學(xué)意義上理解,就會(huì)錯(cuò)誤地得出b和a相同,也應(yīng)該是'XYZ',但實(shí)際上b的值是'ABC',讓我們一行一行地執(zhí)行代碼,就可以看到到底發(fā)生了什么事:

執(zhí)行a = 'ABC',解釋器創(chuàng)建了字符串 'ABC'和變量 a,并把a(bǔ)指向 'ABC':

執(zhí)行b = a,解釋器創(chuàng)建了變量 b,并把b指向 a 指向的字符串'ABC':

執(zhí)行a = 'XYZ',解釋器創(chuàng)建了字符串'XYZ',并把a(bǔ)的指向改為'XYZ',但b并沒有更改:

所以,最后打印變量b的結(jié)果自然是'ABC'了。

定義字符串

前面我們講解了什么是字符串。字符串可以用''或者''括起來表示。

如果字符串本身包含'怎么辦?比如我們要表示字符串 I'm OK ,這時(shí),可以用' '括起來表示:

'I'm OK'

類似的,如果字符串包含',我們就可以用' '括起來表示:

'Learn 'Python' in mooc'

如果字符串既包含'又包含'怎么辦?

這個(gè)時(shí)候,就需要對(duì)字符串的某些特殊字符進(jìn)行“轉(zhuǎn)義”,Python字符串用\進(jìn)行轉(zhuǎn)義。

要表示字符串 Bob said 'I'm OK'.

由于 ' 和 ' 會(huì)引起歧義,因此,我們?cè)谒懊娌迦胍粋€(gè)\表示這是一個(gè)普通字符,不代表字符串的起始,因此,這個(gè)字符串又可以表示為

'Bob said \'I\'m OK\'.'

注意:轉(zhuǎn)義字符 \ 不計(jì)入字符串的內(nèi)容中。轉(zhuǎn)義字符在其他腳本語言中也有此用法

常用的轉(zhuǎn)義字符還有:

\n 表示換行

\t 表示一個(gè)制表符

\\ 表示 \ 字符本身

raw字符串與多行字符串

如果一個(gè)字符串包含很多需要轉(zhuǎn)義的字符,對(duì)每一個(gè)字符都進(jìn)行轉(zhuǎn)義會(huì)很麻煩。為了避免這種情況,我們可以在字符串前面加個(gè)前綴r ,表示這是一個(gè) raw 字符串,里面的字符就不需要轉(zhuǎn)義了。例如:

r'\(~_~)/ \(~_~)/'

但是r'...'表示法不能表示多行字符串,也不能表示包含''的字符串

如果要表示多行字符串,可以用'''...'''表示:

'''Line 1

Line 2

Line 3'''

上面這個(gè)字符串的表示方法和下面的是完全一樣的:

'Line 1\nLine 2\nLine 3'

還可以在多行字符串前面添加 r ,把這個(gè)多行字符串也變成一個(gè)raw字符串

r'''Python is created by 'Guido'.

It is free and easy to learn.

Let's start learn Python in imooc!'''

Unicode字符串

字符串還有一個(gè)編碼問題。

因?yàn)橛?jì)算機(jī)只能處理數(shù)字,如果要處理文本,就必須先把文本轉(zhuǎn)換為數(shù)字才能處理。最早的計(jì)算機(jī)在設(shè)計(jì)時(shí)采用8個(gè)比特(bit)作為一個(gè)字節(jié)(byte),所以,一個(gè)字節(jié)能表示的最大的整數(shù)就是255(二進(jìn)制11111111=十進(jìn)制255),0 - 255被用來表示大小寫英文字母、數(shù)字和一些符號(hào),這個(gè)編碼表被稱為ASCII編碼,比如大寫字母 A 的編碼是65,小寫字母 z 的編碼是122

如果要表示中文,顯然一個(gè)字節(jié)是不夠的,至少需要兩個(gè)字節(jié),而且還不能和ASCII編碼沖突,所以,中國制定了GB2312編碼,用來把中文編進(jìn)去。

類似的,日文和韓文等其他語言也有這個(gè)問題。為了統(tǒng)一所有文字的編碼,Unicode應(yīng)運(yùn)而生。Unicode把所有語言都統(tǒng)一到一套編碼里,這樣就不會(huì)再有亂碼問題了。

Unicode通常用兩個(gè)字節(jié)表示一個(gè)字符,原有的英文編碼從單字節(jié)變成雙字節(jié),只需要把高字節(jié)全部填為0就可以。

因?yàn)镻ython的誕生比Unicode標(biāo)準(zhǔn)發(fā)布的時(shí)間還要早,所以最早的Python只支持ASCII編碼,普通的字符串'ABC'在Python內(nèi)部都是ASCII編碼的。

Python在后來添加了對(duì)Unicode的支持,以Unicode表示的字符串用u'...'表示,比如:

print u'中文'

中文

注意: 不加 u ,中文就不能正常顯示。

Unicode字符串除了多了一個(gè) u 之外,與普通字符串沒啥區(qū)別,轉(zhuǎn)義字符和多行表示法仍然有效:

轉(zhuǎn)義:

u'中文\n日文\n韓文'

多行:

u'''第一行

第二行'''

raw+多行:

ur'''Python的Unicode字符串支持'中文',

'日文',

'韓文'等多種語言'''

如果中文字符串在Python環(huán)境下遇到 UnicodeDecodeError,這是因?yàn)?py文件保存的格式有問題。可以在第一行添加注釋

# -*- coding: utf-8 -*-

目的是告訴Python解釋器,用UTF-8編碼讀取源代碼。然后用Notepad++ 另存為... 并選擇UTF-8格式保存。

整數(shù)和浮點(diǎn)數(shù)

Python支持對(duì)整數(shù)和浮點(diǎn)數(shù)直接進(jìn)行四則混合運(yùn)算,運(yùn)算規(guī)則和數(shù)學(xué)上的四則運(yùn)算規(guī)則完全一致。

基本的運(yùn)算:

1 + 2 + 3 # ==> 6

4 * 5 - 6 # ==> 14

7.5 / 8 + 2.1 # ==> 3.0375

使用括號(hào)可以提升優(yōu)先級(jí),這和數(shù)學(xué)運(yùn)算完全一致,注意只能使用小括號(hào),但是括號(hào)可以嵌套很多層:

(1 + 2) * 3 # ==> 9

(2.2 + 3.3) / (1.5 * (9 - 0.3)) # ==> 0.42145593869731807

和數(shù)學(xué)運(yùn)算不同的地方是,Python的整數(shù)運(yùn)算結(jié)果仍然是整數(shù),浮點(diǎn)數(shù)運(yùn)算結(jié)果仍然是浮點(diǎn)數(shù):

1 + 2 # ==> 整數(shù) 3

1.0 + 2.0 # ==> 浮點(diǎn)數(shù) 3.0

但是整數(shù)和浮點(diǎn)數(shù)混合運(yùn)算的結(jié)果就變成浮點(diǎn)數(shù)了:

1 + 2.0 # ==> 浮點(diǎn)數(shù) 3.0

為什么要區(qū)分整數(shù)運(yùn)算和浮點(diǎn)數(shù)運(yùn)算呢?這是因?yàn)檎麛?shù)運(yùn)算的結(jié)果永遠(yuǎn)是精確的,而浮點(diǎn)數(shù)運(yùn)算的結(jié)果不一定精確,因?yàn)橛?jì)算機(jī)內(nèi)存再大,也無法精確表示出無限循環(huán)小數(shù),比如 0.1 換成二進(jìn)制表示就是無限循環(huán)小數(shù)。

那整數(shù)的除法運(yùn)算遇到除不盡的時(shí)候,結(jié)果難道不是浮點(diǎn)數(shù)嗎?我們來試一下:

11 / 4 # ==> 2

令很多初學(xué)者驚訝的是,Python的整數(shù)除法,即使除不盡,結(jié)果仍然是整數(shù),余數(shù)直接被扔掉。不過,Python提供了一個(gè)求余的運(yùn)算 % 可以計(jì)算余數(shù):C語言與C++中用同樣的方法求余

11 % 4 # ==> 3

如果我們要計(jì)算 11 / 4 的精確結(jié)果,按照“整數(shù)和浮點(diǎn)數(shù)混合運(yùn)算的結(jié)果是浮點(diǎn)數(shù)”的法則,把兩個(gè)數(shù)中的一個(gè)變成浮點(diǎn)數(shù)再運(yùn)算就沒問題了:

11.0 / 4 # ==> 2.75

布爾類型

我們已經(jīng)了解了Python支持布爾類型的數(shù)據(jù),布爾類型只有TrueFalse兩種值,但是布爾類型有以下幾種運(yùn)算:

與運(yùn)算:只有兩個(gè)布爾值都為 True 時(shí),計(jì)算結(jié)果才為 True。

True and True # ==> True

True and False # ==> False

False and True # ==> False

False and False # ==> False

或運(yùn)算:只要有一個(gè)布爾值為 True,計(jì)算結(jié)果就是 True。

True or True # ==> True

True or False # ==> True

False or True # ==> True

False or False # ==> False

非運(yùn)算:把True變?yōu)镕alse,或者把False變?yōu)門rue:

not True # ==> False

not False # ==> True

布爾運(yùn)算在計(jì)算機(jī)中用來做條件判斷,根據(jù)計(jì)算結(jié)果為True或者False,計(jì)算機(jī)可以自動(dòng)執(zhí)行不同的后續(xù)代碼。

在Python中,布爾類型還可以與其他數(shù)據(jù)類型做 and、or和not運(yùn)算,請(qǐng)看下面的代碼:

a = True

print a and 'a=T' or 'a=F'

計(jì)算結(jié)果不是布爾類型,而是字符串 'a=T',這是為什么呢?

因?yàn)镻ython把0空字符串''None看成 False,其他數(shù)值非空字符串都看成 True,所以:

True and 'a=T' 計(jì)算結(jié)果是 'a=T'

繼續(xù)計(jì)算 'a=T' or 'a=F' 計(jì)算結(jié)果還是 'a=T'

要解釋上述結(jié)果,又涉及到 and 和 or 運(yùn)算的一條重要法則:短路計(jì)算。

1. 在計(jì)算 a and b 時(shí),如果 a 是 False,則根據(jù)與運(yùn)算法則,整個(gè)結(jié)果必定為 False,因此返回 a;如果 a 是 True,則整個(gè)計(jì)算結(jié)果必定取決與 b,因此返回 b。

2. 在計(jì)算 a or b 時(shí),如果 a 是 True,則根據(jù)或運(yùn)算法則,整個(gè)計(jì)算結(jié)果必定為 True,因此返回 a;如果 a 是 False,則整個(gè)計(jì)算結(jié)果必定取決于 b,因此返回 b。

所以Python解釋器在做布爾運(yùn)算時(shí),只要能提前確定計(jì)算結(jié)果,它就不會(huì)往后算了,直接返回結(jié)果。

創(chuàng)建list

Python內(nèi)置的一種數(shù)據(jù)類型是列表:list。list是一種有序的集合,可以隨時(shí)添加和刪除其中的元素。

比如,列出班里所有同學(xué)的名字,就可以用一個(gè)list表示:

>>> ['Michael', 'Bob', 'Tracy']

['Michael', 'Bob', 'Tracy']

list是數(shù)學(xué)意義上的有序集合,也就是說,list中的元素是按照順序排列的。

構(gòu)造list非常簡單,按照上面的代碼,直接用 把list的所有元素都括起來,就是一個(gè)list對(duì)象。通常,我們會(huì)把list賦值給一個(gè)變量,這樣,就可以通過變量來引用list:

>>> classmates = ['Michael', 'Bob', 'Tracy']

>>> classmates # 打印classmates變量的內(nèi)容

['Michael', 'Bob', 'Tracy']

由于Python是動(dòng)態(tài)語言,所以list中包含的元素并不要求都必須是同一種數(shù)據(jù)類型,我們完全可以在list中包含各種數(shù)據(jù):

>>> L = ['Michael', 100, True]

一個(gè)元素也沒有的list,就是空list:

>>> empty_list =

按照索引訪問list

由于list是一個(gè)有序集合,所以,我們可以用一個(gè)list按分?jǐn)?shù)從高到低表示出班里的3個(gè)同學(xué):

>>> L = ['Adam', 'Lisa', 'Bart']

那我們?nèi)绾螐膌ist中獲取指定第 N 名的同學(xué)呢?方法是通過索引來獲取list中的指定元素。

需要特別注意的是,索引從 0 開始,也就是說,第一個(gè)元素的索引是0,第二個(gè)元素的索引是1,以此類推。

因此,要打印第一名同學(xué)的名字,用 L[0]:

>>> print L[0]

Adam

要打印第二名同學(xué)的名字,用 L[1]:

>>> print L[1]

Lisa

要打印第三名同學(xué)的名字,用 L[2]:

>>> print L[2]

Bart

要打印第四名同學(xué)的名字,用 L[3]:

>>> print L[3]

File '', line 1, in

IndexError: list index out of range

報(bào)錯(cuò)了!IndexError意思就是索引超出了范圍,因?yàn)樯厦娴膌ist只有3個(gè)元素,有效的索引是 0,1,2。

所以,使用索引時(shí),千萬注意不要越界

倒序訪問list

我們還是用一個(gè)list按分?jǐn)?shù)從高到低表示出班里的3個(gè)同學(xué):

>>> L = ['Adam', 'Lisa', 'Bart']

這時(shí),老師說,請(qǐng)分?jǐn)?shù)最低的同學(xué)站出來。

要寫代碼完成這個(gè)任務(wù),我們可以先數(shù)一數(shù)這個(gè) list,發(fā)現(xiàn)它包含3個(gè)元素,因此,最后一個(gè)元素的索引是2:

>>> print L[2]

Bart

有沒有更簡單的方法?

有!

Bart同學(xué)是最后一名,俗稱倒數(shù)第一,所以,我們可以用 -1 這個(gè)索引來表示最后一個(gè)元素:

>>> print L[-1]

Bart

Bart同學(xué)表示躺槍。

類似的,倒數(shù)第二用 -2 表示,倒數(shù)第三用 -3 表示,倒數(shù)第四用 -4 表示:

>>> print L[-2]

Lisa

>>> print L[-3]

Adam

>>> print L[-4]

Traceback (most recent call last):

File '', line 1, in

IndexError: list index out of range

L[-4] 報(bào)錯(cuò)了,因?yàn)榈箶?shù)第四不存在,一共只有3個(gè)元素。

使用倒序索引時(shí),也要注意不要越界。

添加新元素

現(xiàn)在,班里有3名同學(xué):

>>> L = ['Adam', 'Lisa', 'Bart']

今天,班里轉(zhuǎn)來一名新同學(xué) Paul,如何把新同學(xué)添加到現(xiàn)有的 list 中呢?

第一個(gè)辦法是用 list 的 append 方法,把新同學(xué)追加到 list 的末尾:

>>> L = ['Adam', 'Lisa', 'Bart']

>>> L.append('Paul')

>>> print L

['Adam', 'Lisa', 'Bart', 'Paul']

append總是把新的元素添加到 list 的尾部。

如果 Paul 同學(xué)表示自己總是考滿分,要求添加到第一的位置,怎么辦?

方法是用list的 insert方法,它接受兩個(gè)參數(shù),第一個(gè)參數(shù)是索引號(hào),第二個(gè)參數(shù)是待添加的新元素:

>>> L = ['Adam', 'Lisa', 'Bart']

>>> L.insert(0, 'Paul')

>>> print L

['Paul', 'Adam', 'Lisa', 'Bart']

L.insert(0, 'Paul') 的意思是,'Paul'將被添加到索引為 0 的位置上(也就是第一個(gè)),而原來索引為 0 的Adam同學(xué),以及后面的所有同學(xué),都自動(dòng)向后移動(dòng)一位。

從list刪除元素

Paul同學(xué)剛來幾天又要轉(zhuǎn)走了,那么我們?cè)趺窗裀aul 從現(xiàn)有的list中刪除呢?

如果Paul同學(xué)排在最后一個(gè),我們可以用list的pop方法刪除:

>>> L = ['Adam', 'Lisa', 'Bart', 'Paul']

>>> L.pop

'Paul'

>>> print L

['Adam', 'Lisa', 'Bart']

pop方法總是刪掉list的最后一個(gè)元素,并且它還返回這個(gè)元素,所以我們執(zhí)行 L.pop 后,會(huì)打印出 'Paul'。聯(lián)想append只在最后添加,pop只刪除最后

如果Paul同學(xué)不是排在最后一個(gè)怎么辦?比如Paul同學(xué)排在第三:

>>> L = ['Adam', 'Lisa', 'Paul', 'Bart']

要把Paul踢出list,我們就必須先定位Paul的位置。由于Paul的索引是2,因此,用 pop(2)把Paul刪掉:

>>> L.pop(2)

'Paul'

>>> print L

替換元素

假設(shè)現(xiàn)在班里仍然是3名同學(xué):

>>> L = ['Adam', 'Lisa', 'Bart']

現(xiàn)在,Bart同學(xué)要轉(zhuǎn)學(xué)走了,碰巧來了一個(gè)Paul同學(xué),要更新班級(jí)成員名單,我們可以先把Bart刪掉,再把Paul添加進(jìn)來。

另一個(gè)辦法是直接用Paul把Bart給替換掉:

>>> L[2] = 'Paul'

>>> print L

L = ['Adam', 'Lisa', 'Paul']

對(duì)list中的某一個(gè)索引賦值,就可以直接用新的元素替換掉原來的元素,list包含的元素個(gè)數(shù)保持不變。

由于Bart還可以用 -1 做索引,因此,下面的代碼也可以完成同樣的替換工作:

>>> L[-1] = 'Paul'

創(chuàng)建tuple

tuple是另一種有序的列表,中文翻譯為“ 元組 ”。tuple 和 list 非常類似,但是,tuple一旦創(chuàng)建完畢,就不能修改了。list創(chuàng)建后還是可以修改的哦!

同樣是表示班里同學(xué)的名稱,用tuple表示如下:

>>> t = ('Adam', 'Lisa', 'Bart')

創(chuàng)建tuple和創(chuàng)建list唯一不同之處是用替代了。

現(xiàn)在,這個(gè) t 就不能改變了,tuple沒有 append方法,也沒有insert和pop方法。所以,新同學(xué)沒法直接往 tuple 中添加,老同學(xué)想退出 tuple 也不行。

獲取 tuple 元素的方式和 list 是一模一樣的,我們可以正常使用 t[0],t[-1]等索引方式訪問元素,但是不能賦值成別的元素,不信可以試試:

>>> t[0] = 'Paul'

File '', line 1, in

TypeError: 'tuple' object does not support item assignment

創(chuàng)建單元素tuple

tuple和list一樣,可以包含 0 個(gè)、1個(gè)和任意多個(gè)元素。

包含多個(gè)元素的 tuple,前面我們已經(jīng)創(chuàng)建過了。

包含 0 個(gè)元素的 tuple,也就是空tuple,直接用 表示:

>>> t =

>>> print t

創(chuàng)建包含1個(gè)元素的 tuple 呢?來試試:

>>> t = (1)

>>> print t

1

好像哪里不對(duì)!t 不是 tuple ,而是整數(shù)1。為什么呢?

因?yàn)?/span>既可以表示tuple,又可以作為括號(hào)表示運(yùn)算時(shí)的優(yōu)先級(jí),結(jié)果 (1) 被Python解釋器計(jì)算出結(jié)果 1,導(dǎo)致我們得到的不是tuple,而是整數(shù) 1。

正是因?yàn)橛枚x單元素的tuple有歧義,所以 Python 規(guī)定,單元素 tuple 要多加一個(gè)逗號(hào)“,”,這樣就避免了歧義:

>>> t = (1,)

>>> print t

(1,)

Python在打印單元素tuple時(shí),也自動(dòng)添加了一個(gè)“,”,為了更明確地告訴你這是一個(gè)tuple。

多元素 tuple 加不加這個(gè)額外的“,”效果是一樣的:

>>> t = (1, 2, 3,)

>>> print t

(1, 2, 3)

“可變”的tuple

前面我們看到了tuple一旦創(chuàng)建就不能修改。現(xiàn)在,我們來看一個(gè)“可變”的tuple:

>>> t = ('a', 'b', ['A', 'B'])

注意到 t 有 3 個(gè)元素:'a','b'和一個(gè)list:['A', 'B']。list作為一個(gè)整體是tuple的第3個(gè)元素。list對(duì)象可以通過 t[2] 拿到:

>>> print t

('a', 'b', ['X', 'Y'])

不是說tuple一旦定義后就不可變了嗎?怎么現(xiàn)在又變了?

別急,我們先看看定義的時(shí)候tuple包含的3個(gè)元素:

當(dāng)我們把list的元素'A'和'B'修改為'X'和'Y'后,tuple變?yōu)椋?/span>

表面上看,tuple的元素確實(shí)變了,但其實(shí)變的不是 tuple 的元素,而是list的元素。

tuple一開始指向的list并沒有改成別的list,所以,tuple所謂的“不變”是說,tuple的每個(gè)元素,指向永遠(yuǎn)不變。即指向'a',就不能改成指向'b',指向一個(gè)list,就不能改成指向其他對(duì)象,但指向的這個(gè)list本身是可變的!

理解了“指向不變”后,要?jiǎng)?chuàng)建一個(gè)內(nèi)容也不變的tuple怎么做?那就必須保證tuple的每一個(gè)元素本身也不能變。

if語句

計(jì)算機(jī)之所以能做很多自動(dòng)化的任務(wù),因?yàn)樗梢宰约鹤鰲l件判斷。

比如,輸入用戶年齡,根據(jù)年齡打印不同的內(nèi)容,在Python程序中,可以用if語句實(shí)現(xiàn):

age = 20

if age >= 18:

print 'your age is', age

print 'adult'

print 'END'

注意: Python代碼的縮進(jìn)規(guī)則。具有相同縮進(jìn)的代碼被視為代碼塊,上面的3,4行 print 語句就構(gòu)成一個(gè)代碼塊(但不包括第5行的print)。如果 if 語句判斷為 True,就會(huì)執(zhí)行這個(gè)代碼塊。

縮進(jìn)請(qǐng)嚴(yán)格按照Python的習(xí)慣寫法:4個(gè)空格,不要使用Tab,更不要混合Tab和空格,否則很容易造成因?yàn)榭s進(jìn)引起的語法錯(cuò)誤。

注意: if 語句后接表達(dá)式,然后用:表示代碼塊開始。

如果你在Python交互環(huán)境下敲代碼,還要特別留意縮進(jìn),并且退出縮進(jìn)需要多敲一行回車

>>> age = 20

>>> if age >= 18:

... print 'your age is', age

... print 'adult'

...

your age is 20

adult

if-else

print 'adult'

如果我們想判斷年齡在18歲以下時(shí),打印出 'teenager',怎么辦?

print 'teenager'

或者用 not 運(yùn)算:

if not age >= 18:

細(xì)心的同學(xué)可以發(fā)現(xiàn),這兩種條件判斷是“非此即彼”的,要么符合條件1,要么符合條件2,因此,完全可以用一個(gè) if ... else ... 語句把它們統(tǒng)一起來:

if age >= 18:

print 'adult'

else:

利用 if ... else ... 語句,我們可以根據(jù)條件表達(dá)式的值為 True 或者False ,分別執(zhí)行 if 代碼塊或者 else 代碼塊。

注意: else 后面有個(gè)“:”。

if-elif-else

有的時(shí)候,一個(gè) if ... else ... 還不夠用。比如,根據(jù)年齡的劃分:

條件1:18歲或以上:adult

條件2:6歲或以上:teenager

條件3:6歲以下:kid

我們可以用一個(gè) if age >= 18 判斷是否符合條件1,如果不符合,再通過一個(gè) if 判斷 age >= 6 來判斷是否符合條件2,否則,執(zhí)行條件3:

else:

print 'kid'

這樣寫出來,我們就得到了一個(gè)兩層嵌套的 if ... else ... 語句。這個(gè)邏輯沒有問題,但是,如果繼續(xù)增加條件,比如3歲以下是 baby:

if age >= 18:

else:

print 'baby'

這種縮進(jìn)只會(huì)越來越多,代碼也會(huì)越來越難看。

要避免嵌套結(jié)構(gòu)的 if ... else ...,我們可以用 if ... 多個(gè)elif ... else ...的結(jié)構(gòu),一次寫完所有的規(guī)則:

if age >= 18:

print 'kid'else:

print 'baby'

elif 意思就是 else if。這樣一來,我們就寫出了結(jié)構(gòu)非常清晰的一系列條件判斷。

特別注意: 這一系列條件判斷會(huì)從上到下依次判斷,如果某個(gè)判斷為 True,執(zhí)行完對(duì)應(yīng)的代碼塊,后面的條件判斷就直接忽略,不再執(zhí)行了。

for循環(huán)

listtuple可以表示一個(gè)有序集合。如果我們想依次訪問一個(gè)list中的每一個(gè)元素呢?比如 list:

L = ['Adam', 'Lisa', 'Bart']

print L[0]

print L[1]

print L[2]

如果list只包含幾個(gè)元素,這樣寫還行,如果list包含1萬個(gè)元素,我們就不可能寫1萬行print。

這時(shí),循環(huán)就派上用場了。

Python的 for 循環(huán)就可以依次把list或tuple的每個(gè)元素迭代出來:

L = ['Adam', 'Lisa', 'Bart']for namein L:

print name

注意: name 這個(gè)變量是在 for 循環(huán)中定義的,意思是,依次取出list中的每一個(gè)元素,并把元素賦值給 name,然后執(zhí)行for循環(huán)體(就是縮進(jìn)的代碼塊)。

這樣一來,遍歷一個(gè)list或tuple就非常容易了。

while循環(huán)

和 for 循環(huán)不同的另一種循環(huán)是 while 循環(huán),while 循環(huán)不會(huì)迭代 list 或 tuple 的元素,而是根據(jù)表達(dá)式判斷循環(huán)是否結(jié)束。

比如要從 0 開始打印不大于 N 的整數(shù):

N = 10

x = 0while x <>:

print x

x = x + 1

while循環(huán)每次先判斷 x <>,如果為True,則執(zhí)行循環(huán)體的代碼塊,否則,退出循環(huán)。

在循環(huán)體內(nèi),x = x + 1 會(huì)讓 x 不斷增加,最終因?yàn)?/span> x < n="">不成立而退出循環(huán)。

如果沒有這一個(gè)語句,while循環(huán)在判斷 x < n="">,就會(huì)無限循環(huán)下去,變成死循環(huán),所以要特別留意while循環(huán)的退出條件。

break退出循環(huán)

用 for 循環(huán)或者 while 循環(huán)時(shí),如果要在循環(huán)體內(nèi)直接退出循環(huán),可以使用 break 語句。

if x > 100:

break

print sum

咋一看, while True 就是一個(gè)死循環(huán),但是在循環(huán)體內(nèi),我們還判斷了 x > 100 條件成立時(shí),用break語句退出循環(huán),這樣也可以實(shí)現(xiàn)循環(huán)的結(jié)束。

continue繼續(xù)循環(huán)

在循環(huán)過程中,可以用break退出當(dāng)前循環(huán),還可以用continue跳過后續(xù)循環(huán)代碼,繼續(xù)下一次循環(huán)。

假設(shè)我們已經(jīng)寫好了利用for循環(huán)計(jì)算平均分的代碼:

L = [75, 98, 59, 81, 66, 43, 69, 85]

sum = 0.0

n = 0for xin L:

sum = sum + x

n = n + 1

print sum / n

現(xiàn)在老師只想統(tǒng)計(jì)及格分?jǐn)?shù)的平均分,就要把 x < 60="" 的分?jǐn)?shù)剔除掉,這時(shí),利用="" continue,可以做到當(dāng)="" x=""><>

for x in L:

if x <>

continue

sum = sum + x

n = n + 1

多重循環(huán)

for xin ['A', 'B', 'C']:

x 每循環(huán)一次,y 就會(huì)循環(huán) 3 次,這樣,我們可以打印出一個(gè)全排列:

A1

A2A3B1B2B3C1C2C3

什么是dict

或者考試的成績列表:

[95, 85, 59]

但是,要根據(jù)名字找到對(duì)應(yīng)的成績,用兩個(gè) list 表示就不方便。

如果把名字和分?jǐn)?shù)關(guān)聯(lián)起來,組成類似的查找表:

'Adam' ==> 95

'Lisa' ==> 85

'Bart' ==> 59

給定一個(gè)名字,就可以直接查到分?jǐn)?shù)。

Python的 dict 就是專門干這件事的。用 dict 表示“名字”-“成績”的查找表如下:

d = {

'Adam': 95,

'Lisa': 85,

'Bart': 59

}

我們把名字稱為key,對(duì)應(yīng)的成績稱為value,dict就是通過 key 來查找 value

花括號(hào) {} 表示這是一個(gè)dict,然后按照 key: value, 寫出來即可。最后一個(gè) key: value 的逗號(hào)可以省略。

由于dict也是集合,len 函數(shù)可以計(jì)算任意集合的大?。?/span>

>>> len(d)

3

注意: 一個(gè) key-value 算一個(gè),因此,dict大小為3。

訪問dict

我們已經(jīng)能創(chuàng)建一個(gè)dict,用于表示名字和成績的對(duì)應(yīng)關(guān)系:

d = {

'Adam': 95,

'Lisa': 85,

'Bart': 59

}

那么,如何根據(jù)名字來查找對(duì)應(yīng)的成績呢?

可以簡單地使用 d[key] 的形式來查找對(duì)應(yīng)的 value,這和 list 很像,不同之處是,list 必須使用索引返回對(duì)應(yīng)的元素,而dict使用key:

>>> print d['Adam']

95

>>> print d['Paul']

Traceback (most recent call last):

File 'index.py', line 11, in

print d['Paul']

KeyError: 'Paul'

注意: 通過 key 訪問 dict 的value,只要 key 存在,dict就返回對(duì)應(yīng)的value。如果key不存在,會(huì)直接報(bào)錯(cuò):KeyError。

要避免 KeyError 發(fā)生,有兩個(gè)辦法:

一是先判斷一下 key 是否存在,用 in 操作符:

if 'Paul' in d:

print d['Paul']

如果 'Paul' 不存在,if語句判斷為False,自然不會(huì)執(zhí)行 print d['Paul'] ,從而避免了錯(cuò)誤。

二是使用dict本身提供的一個(gè) get 方法,在Key不存在的時(shí)候,返回None:

>>> print d.get('Bart')

59

>>> print d.get('Paul')

None

dict的特點(diǎn)

dict的第一個(gè)特點(diǎn)是查找速度快,無論dict有10個(gè)元素還是10萬個(gè)元素,查找速度都一樣。而list的查找速度隨著元素增加而逐漸下降。

不過dict的查找速度快不是沒有代價(jià)的,dict的缺點(diǎn)是占用內(nèi)存大,還會(huì)浪費(fèi)很多內(nèi)容,list正好相反,占用內(nèi)存小,但是查找速度慢。

由于dict是按 key 查找,所以,在一個(gè)dict中,key不能重復(fù)。

dict的第二個(gè)特點(diǎn)就是存儲(chǔ)的key-value序?qū)κ?/span>沒有順序的!這和list不一樣:

d = {

'Adam': 95,

'Lisa': 85,

'Bart': 59

}

當(dāng)我們?cè)噲D打印這個(gè)dict時(shí):

>>> print d

{'Lisa': 85, 'Adam': 95, 'Bart': 59}

打印的順序不一定是我們創(chuàng)建時(shí)的順序,而且,不同的機(jī)器打印的順序都可能不同,這說明dict內(nèi)部是無序的,不能用dict存儲(chǔ)有序的集合。

dict的第三個(gè)特點(diǎn)是作為 key 的元素必須不可變,Python的基本類型如字符串、整數(shù)、浮點(diǎn)數(shù)都是不可變的,都可以作為 key。但是list是可變的,就不能作為 key。

可以試試用list作為key時(shí)會(huì)報(bào)什么樣的錯(cuò)誤。

不可變這個(gè)限制僅作用于key,value是否可變無所謂:

{

'123': [1, 2, 3], # key 是 str,value是list

123: '123', # key 是 int,value 是 str

('a', 'b'): True # key 是 tuple,并且tuple的每個(gè)元素都是不可變對(duì)象,value是 boolean

}

最常用的key還是字符串,因?yàn)橛闷饋碜罘奖恪?/span>

更新dict

dict是可變的,也就是說,我們可以隨時(shí)往dict中添加新的 key-value。比如已有dict:

d = {

'Adam': 95,

'Lisa': 85,

'Bart': 59

}

要把新同學(xué)'Paul'的成績 72 加進(jìn)去,用賦值語句:

>>> d['Paul'] = 72

再看看dict的內(nèi)容:

>>> print d

{'Lisa': 85, 'Paul': 72, 'Adam': 95, 'Bart': 59}

如果 key 已經(jīng)存在,則賦值會(huì)用新的 value 替換掉原來的 value:

>>> d['Bart'] = 60

>>> print d

{'Lisa': 85, 'Paul': 72, 'Adam': 95, 'Bart': 60}

遍歷dict

由于dict也是一個(gè)集合,所以,遍歷dict和遍歷list類似,都可以通過 for 循環(huán)實(shí)現(xiàn)。

直接使用for循環(huán)可以遍歷 dict 的 key:

>>> d = { 'Adam': 95, 'Lisa': 85, 'Bart': 59 }

>>> for key in d:

... print key

...

Lisa

Adam

Bart

由于通過 key 可以獲取對(duì)應(yīng)的 value,因此,在循環(huán)體內(nèi),可以獲取到value的值。

什么是set

dict的作用是建立一組 key 和一組 value 的映射關(guān)系,dict的key是不能重復(fù)的。

有的時(shí)候,我們只想要 dict 的 key,不關(guān)心 key 對(duì)應(yīng)的 value,目的就是保證這個(gè)集合的元素不會(huì)重復(fù),這時(shí),set就派上用場了。

set 持有一系列元素,這一點(diǎn)和 list 很像,但是set的元素沒有重復(fù),而且是無序的,這點(diǎn)和 dict 的 key很像。

創(chuàng)建 set 的方式是調(diào)用 set 并傳入一個(gè) list,list的元素將作為set的元素:

>>> s = set(['A', 'B', 'C'])

可以查看 set 的內(nèi)容:

>>> print s

set(['A', 'C', 'B'])

請(qǐng)注意,上述打印的形式類似 list, 但它不是 list,仔細(xì)看還可以發(fā)現(xiàn),打印的順序和原始 list 的順序有可能是不同的,因?yàn)閟et內(nèi)部存儲(chǔ)的元素是無序的。

因?yàn)?/span>set不能包含重復(fù)的元素,所以,當(dāng)我們傳入包含重復(fù)元素的 list 會(huì)怎么樣呢?

>>> s = set(['A', 'B', 'C', 'C'])

>>> print s

set(['A', 'C', 'B'])

>>> len(s)

3

結(jié)果顯示,set會(huì)自動(dòng)去掉重復(fù)的元素,原來的list有4個(gè)元素,但set只有3個(gè)元素。

訪問set

由于set存儲(chǔ)的是無序集合,所以我們沒法通過索引來訪問。

訪問 set中的某個(gè)元素實(shí)際上就是判斷一個(gè)元素是否在set中。

例如,存儲(chǔ)了班里同學(xué)名字的set:

>>> s = set(['Adam', 'Lisa', 'Bart', 'Paul'])

我們可以用 in 操作符判斷:

Bart是該班的同學(xué)嗎?

>>> 'Bart' in s

True

Bill是該班的同學(xué)嗎?

>>> 'Bill' in s

False

bart是該班的同學(xué)嗎?

>>> 'bart' in s

False

看來大小寫很重要,'Bart' 和 'bart'被認(rèn)為是兩個(gè)不同的元素。

set的特點(diǎn)

set的內(nèi)部結(jié)構(gòu)和dict很像,唯一區(qū)別是不存儲(chǔ)value,因此,判斷一個(gè)元素是否在set中速度很快。

set存儲(chǔ)的元素和dict的key類似,必須是不變對(duì)象,因此,任何可變對(duì)象是不能放入set中的。

最后,set存儲(chǔ)的元素也是沒有順序的。

set的這些特點(diǎn),可以應(yīng)用在哪些地方呢?

星期一到星期日可以用字符串'MON', 'TUE', ... 'SUN'表示。

假設(shè)我們讓用戶輸入星期一至星期日的某天,如何判斷用戶的輸入是否是一個(gè)有效的星期呢?

可以用 if 語句判斷,但這樣做非常繁瑣:

x = '???'# 用戶輸入的字符串if x!= 'MON' and x!= 'TUE' and x!= 'WED' ... and x!= 'SUN':

print 'input error'else:

注意:if 語句中的...表示沒有列出的其它星期名稱,測試時(shí),請(qǐng)輸入完整。

如果事先創(chuàng)建好一個(gè)set,包含'MON' ~ 'SUN':

weekdays = set(['MON', 'TUE', 'WED', 'THU', 'FRI', 'SAT', 'SUN'])

再判斷輸入是否有效,只需要判斷該字符串是否在set中:

x = '???'# 用戶輸入的字符串if xin weekdays:

print 'input ok'else:

print 'input error'

這樣一來,代碼就簡單多了。

遍歷set

由于 set 也是一個(gè)集合,所以,遍歷 set 和遍歷 list 類似,都可以通過 for 循環(huán)實(shí)現(xiàn)。

直接使用 for 循環(huán)可以遍歷 set 的元素:

>>> s = set(['Adam', 'Lisa', 'Bart'])

>>> for namein s:

... print name

...

Lisa

Adam

Bart

注意: 觀察 for 循環(huán)在遍歷set時(shí),元素的順序和list的順序很可能是不同的,而且不同的機(jī)器上運(yùn)行的結(jié)果也可能不同。

更新set

由于set存儲(chǔ)的是一組不重復(fù)的無序元素,因此,更新set主要做兩件事:

添加元素時(shí),用set的add方法:

>>> s = set([1, 2, 3])

>>> s.add(4)

>>> print s

set([1, 2, 3, 4])

如果添加的元素已經(jīng)存在于set中,add不會(huì)報(bào)錯(cuò),但是不會(huì)加進(jìn)去了:

>>> s.add(3)

>>> print s

set([1, 2, 3])

刪除set中的元素時(shí),用set的remove方法:

>>> s = set([1, 2, 3, 4])

>>> s.remove(4)

>>> print s

set([1, 2, 3])

如果刪除的元素不存在set中,remove會(huì)報(bào)錯(cuò):

>>> s.remove(4)

File '', line 1, in

KeyError: 4

所以用add可以直接添加,而remove前需要判斷。

什么是函數(shù)

我們知道圓的面積計(jì)算公式為:

S = πr2

當(dāng)我們知道半徑r的值時(shí),就可以根據(jù)公式計(jì)算出面積。假設(shè)我們需要計(jì)算3個(gè)不同大小的圓的面積:

r1 = 12.34

r2 = 9.08

r3 = 73.1

s1 = 3.14 * r1 * r1

s2 = 3.14 * r2 * r2

s3 = 3.14 * r3 * r3

當(dāng)代碼出現(xiàn)有規(guī)律的重復(fù)的時(shí)候,你就需要當(dāng)心了,每次寫3.14 * x * x不僅很麻煩,而且,如果要把3.14改成3.14159265359的時(shí)候,得全部替換。

有了函數(shù),我們就不再每次寫s = 3.14 * x * x,而是寫成更有意義的函數(shù)調(diào)用 s = area_of_circle(x),而函數(shù) area_of_circle 本身只需要寫一次,就可以多次調(diào)用。

抽象是數(shù)學(xué)中非常常見的概念。舉個(gè)例子:

計(jì)算數(shù)列的和,比如:1 + 2 + 3 + ... + 100,寫起來十分不方便,于是數(shù)學(xué)家發(fā)明了求和符號(hào)∑,可以把1 + 2 + 3 + ... + 100記作:

100n

n=1

這種抽象記法非常強(qiáng)大,因?yàn)槲覀兛吹健凭涂梢岳斫獬汕蠛停皇沁€原成低級(jí)的加法運(yùn)算。

而且,這種抽象記法是可擴(kuò)展的,比如:

n=1

還原成加法運(yùn)算就變成了:

(1 x 1 + 1) + (2 x 2 + 1) + (3 x 3 + 1) + ... + (100 x 100 + 1)

可見,借助抽象,我們才能不關(guān)心底層的具體計(jì)算過程,而直接在更高的層次上思考問題。

寫計(jì)算機(jī)程序也是一樣,函數(shù)就是最基本的一種代碼抽象的方式。

Python不但能非常靈活地定義函數(shù),而且本身內(nèi)置了很多有用的函數(shù),可以直接調(diào)用。

調(diào)用函數(shù)

Python內(nèi)置了很多有用的函數(shù),我們可以直接調(diào)用。

可以直接從Python的官方網(wǎng)站查看文檔:

http://docs.python.org/2/library/functions.html#abs

調(diào)用 abs 函數(shù):

>>> abs(100)

100

>>> abs(-20)

20

>>> abs(12.34)

12.34

File '', line 1, in

TypeError: abs takes exactly one argument (2 given)

File '', line 1, in

TypeError: bad operand type for abs: 'str'

而比較函數(shù) cmp(x, y) 就需要兩個(gè)參數(shù),如果 x,返回 -1,如果x==y,返回 0,如果 x>y,返回 1

>>> cmp(1, 2)

-1

>>> cmp(2, 1)

1

>>> cmp(3, 3)

0

Python內(nèi)置的常用函數(shù)還包括數(shù)據(jù)類型轉(zhuǎn)換函數(shù),比如 int函數(shù)可以把其他數(shù)據(jù)類型轉(zhuǎn)換為整數(shù):

>>> int('123')

123

>>> int(12.34)

12

str函數(shù)把其他類型轉(zhuǎn)換成 str:

>>> str(123)

'123'

>>> str(1.23)

'1.23'

編寫函數(shù)

在Python中,定義一個(gè)函數(shù)要使用 def 語句,依次寫出函數(shù)名、括號(hào)、括號(hào)中的參數(shù)冒號(hào):,然后,在縮進(jìn)塊中編寫函數(shù)體,函數(shù)的返回值用 return 語句返回。

我們以自定義一個(gè)求絕對(duì)值的 my_abs 函數(shù)為例:

def my_abs(x):

if x >= 0:

return x

else:

return -x

請(qǐng)注意,函數(shù)體內(nèi)部的語句在執(zhí)行時(shí),一旦執(zhí)行到return時(shí),函數(shù)就執(zhí)行完畢,并將結(jié)果返回。因此,函數(shù)內(nèi)部通過條件判斷和循環(huán)可以實(shí)現(xiàn)非常復(fù)雜的邏輯。

如果沒有return語句,函數(shù)執(zhí)行完畢后也會(huì)返回結(jié)果,只是結(jié)果為 None。

return None可以簡寫為return。

返回多值

函數(shù)可以返回多個(gè)值嗎?答案是肯定的。

比如在游戲中經(jīng)常需要從一個(gè)點(diǎn)移動(dòng)到另一個(gè)點(diǎn),給出坐標(biāo)、位移和角度,就可以計(jì)算出新的坐標(biāo):

# math包提供了sincos函數(shù),我們先用import引用它:

import math

def move(x, y, step, angle):

nx = x + step * math.cos(angle)

ny = y - step * math.sin(angle)

return nx, ny

這樣我們就可以同時(shí)獲得返回值:

>>> x, y = move(100, 100, 60, math.pi / 6)

>>> print x, y

151.961524227 70.0

但其實(shí)這只是一種假象,Python函數(shù)返回的仍然是單一值:

>>> r = move(100, 100, 60, math.pi / 6)

>>> print r

(151.96152422706632, 70.0)

用print打印返回結(jié)果,原來返回值是一個(gè)tuple!

但是,在語法上,返回一個(gè)tuple可以省略括號(hào),而多個(gè)變量可以同時(shí)接收一個(gè)tuple,按位置賦給對(duì)應(yīng)的值,所以,Python的函數(shù)返回多值其實(shí)就是返回一個(gè)tuple,但寫起來更方便。

遞歸函數(shù)

在函數(shù)內(nèi)部,可以調(diào)用其他函數(shù)。如果一個(gè)函數(shù)在內(nèi)部調(diào)用自身本身,這個(gè)函數(shù)就是遞歸函數(shù)。

舉個(gè)例子,我們來計(jì)算階乘 n! = 1 * 2 * 3 * ... * n,用函數(shù) fact(n)表示,可以看出:

fact(n) = n! = 1 * 2 * 3 * ... * (n-1) * n = (n-1)! * n = fact(n-1) * n

所以,fact(n)可以表示為 n * fact(n-1),只有n=1時(shí)需要特殊處理。

于是,fact(n)用遞歸的方式寫出來就是:

def fact(n):

if n==1:

return 1

return n * fact(n - 1)

上面就是一個(gè)遞歸函數(shù)??梢栽囋嚕?/span>

>>> fact(1)

1

>>> fact(5)

120

>>> fact(100)

93326215443944152681699238856266700490715968264381621468592963895217599993229915608941463976156518286253697920827223758251185210916864000000000000000000000000L

如果我們計(jì)算fact(5),可以根據(jù)函數(shù)定義看到計(jì)算過程如下:

===> fact(5)

===> 5 * fact(4)

===> 5 * (4 * fact(3))

===> 5 * (4 * (3 * fact(2)))

===> 5 * (4 * (3 * (2 * fact(1))))

===> 5 * (4 * (3 * (2 * 1)))

===> 5 * (4 * (3 * 2))

===> 5 * (4 * 6)

===> 5 * 24

===> 120

遞歸函數(shù)的優(yōu)點(diǎn)是定義簡單,邏輯清晰。理論上,所有的遞歸函數(shù)都可以寫成循環(huán)的方式,但循環(huán)的邏輯不如遞歸清晰。

使用遞歸函數(shù)需要注意防止棧溢出。在計(jì)算機(jī)中,函數(shù)調(diào)用是通過棧(stack)這種數(shù)據(jù)結(jié)構(gòu)實(shí)現(xiàn)的,每當(dāng)進(jìn)入一個(gè)函數(shù)調(diào)用,棧就會(huì)加一層棧幀,每當(dāng)函數(shù)返回,棧就會(huì)減一層棧幀。由于棧的大小不是無限的,所以,遞歸調(diào)用的次數(shù)過多,會(huì)導(dǎo)致棧溢出。可以試試計(jì)算 fact(10000)。

定義默認(rèn)參數(shù)

定義函數(shù)的時(shí)候,還可以有默認(rèn)參數(shù)。

例如Python自帶的 int 函數(shù),其實(shí)就有兩個(gè)參數(shù),我們既可以傳一個(gè)參數(shù),又可以傳兩個(gè)參數(shù):

>>> int('123')

123

>>> int('123', 8)

83

int函數(shù)的第二個(gè)參數(shù)是轉(zhuǎn)換進(jìn)制,如果不傳,默認(rèn)是十進(jìn)制 (base=10),如果傳了,就用傳入的參數(shù)。

可見,函數(shù)的默認(rèn)參數(shù)的作用是簡化調(diào)用,你只需要把必須的參數(shù)傳進(jìn)去。但是在需要的時(shí)候,又可以傳入額外的參數(shù)來覆蓋默認(rèn)參數(shù)值。

我們來定義一個(gè)計(jì)算 x 的N次方的函數(shù):

def power(x, n):

s = 1

while n > 0:

n = n - 1

s = s * x

return s

假設(shè)計(jì)算平方的次數(shù)最多,我們就可以把 n 的默認(rèn)值設(shè)定為 2:

def power(x, n=2):

s = 1

while n > 0:

n = n - 1

s = s * x

return s

這樣一來,計(jì)算平方就不需要傳入兩個(gè)參數(shù)了:

>>> power(5)

25

def fn2(a=1, b):

pass

定義可變參數(shù)

如果想讓一個(gè)函數(shù)能接受任意個(gè)參數(shù),我們就可以定義一個(gè)可變參數(shù):

def fn(*args):

print args

可變參數(shù)的名字前面有個(gè) * 號(hào),我們可以傳入0個(gè)、1個(gè)或多個(gè)參數(shù)給可變參數(shù):

>>> fn

>>> fn('a')

('a',)

>>> fn('a', 'b')

('a', 'b')

>>> fn('a', 'b', 'c')

('a', 'b', 'c')

可變參數(shù)也不是很神秘,Python解釋器會(huì)把傳入的一組參數(shù)組裝成一個(gè)tuple傳遞給可變參數(shù),因此,在函數(shù)內(nèi)部,直接把變量 args看成一個(gè) tuple 就好了。

定義可變參數(shù)的目的也是為了簡化調(diào)用。假設(shè)我們要計(jì)算任意個(gè)數(shù)的平均值,就可以定義一個(gè)可變參數(shù):

def average(*args):

...

這樣,在調(diào)用的時(shí)候,可以這樣寫:

>>> average

0

>>> average(1, 2)

1.5

>>> average(1, 2, 2, 3, 4)

2.4

對(duì)list進(jìn)行切片

取一個(gè)list的部分元素是非常常見的操作。比如,一個(gè)list如下:

>>> L = ['Adam', 'Lisa', 'Bart', 'Paul']

取前3個(gè)元素,應(yīng)該怎么做?

笨辦法:

>>> [L[0], L[1], L[2]]

['Adam', 'Lisa', 'Bart']

之所以是笨辦法是因?yàn)閿U(kuò)展一下,取前N個(gè)元素就沒轍了。

取前N個(gè)元素,也就是索引為0-(N-1)的元素,可以用循環(huán):

>>> r =

>>> n = 3

>>> for i in range(n):

... r.append(L[i])

...

>>> r

['Adam', 'Lisa', 'Bart']

對(duì)這種經(jīng)常取指定索引范圍的操作,用循環(huán)十分繁瑣,因此,Python提供了切片(Slice)操作符,能大大簡化這種操作。

對(duì)應(yīng)上面的問題,取前3個(gè)元素,用一行代碼就可以完成切片:

>>> L[0:3]

['Adam', 'Lisa', 'Bart']

L[0:3]表示,從索引0開始取,直到索引3為止,但不包括索引3。即索引0,1,2,正好是3個(gè)元素。

如果第一個(gè)索引是0,還可以省略:

>>> L[:3]

['Adam', 'Lisa', 'Bart']

也可以從索引1開始,取出2個(gè)元素出來:

>>> L[1:3]

['Lisa', 'Bart']

只用一個(gè) : ,表示從頭到尾:

>>> L[:]

['Adam', 'Lisa', 'Bart', 'Paul']

因此,L[:]實(shí)際上復(fù)制出了一個(gè)新list。

切片操作還可以指定第三個(gè)參數(shù):

>>> L[::2]

['Adam', 'Bart']

第三個(gè)參數(shù)表示每N個(gè)取一個(gè),上面的 L[::2] 會(huì)每兩個(gè)元素取出一個(gè)來,也就是隔一個(gè)取一個(gè)。

把list換成tuple,切片操作完全相同,只是切片的結(jié)果也變成了tuple。

倒序切片

對(duì)于list,既然Python支持L[-1]取倒數(shù)第一個(gè)元素,那么它同樣支持倒數(shù)切片,試試:

>>> L = ['Adam', 'Lisa', 'Bart', 'Paul']

>>> L[-2:]

['Bart', 'Paul']

>>> L[:-2]

['Adam', 'Lisa']

>>> L[-3:-1]

['Lisa', 'Bart']

>>> L[-4:-1:2]

['Adam', 'Bart']

記住倒數(shù)第一個(gè)元素的索引是-1。倒序切片包含起始索引,不包含結(jié)束索引。

對(duì)字符串切片

字符串 'xxx'和 Unicode字符串 u'xxx'也可以看成是一種list,每個(gè)元素就是一個(gè)字符。因此,字符串也可以用切片操作,只是操作結(jié)果仍是字符串:

>>> 'ABCDEFG'[:3]

'ABC'

>>> 'ABCDEFG'[-3:]

'EFG'

>>> 'ABCDEFG'[::2]

'ACEG'

在很多編程語言中,針對(duì)字符串提供了很多各種截取函數(shù),其實(shí)目的就是對(duì)字符串切片。Python沒有針對(duì)字符串的截取函數(shù),只需要切片一個(gè)操作就可以完成,非常簡單。

什么是迭代

在Python中,如果給定一個(gè)listtuple,我們可以通過for循環(huán)來遍歷這個(gè)list或tuple,這種遍歷我們成為迭代(Iteration)。

在Python中,迭代是通過 for ... in 來完成的,而很多語言比如C或者Java,迭代list是通過下標(biāo)完成的,比如Java代碼:

for (i=0; i

n = list[i];

}

可以看出,Python的for循環(huán)抽象程度要高于Java的for循環(huán)。

因?yàn)?Python 的 for循環(huán)不僅可以用在list或tuple上,還可以作用在其他任何可迭代對(duì)象上。

因此,迭代操作就是對(duì)于一個(gè)集合,無論該集合是有序還是無序,我們用 for 循環(huán)總是可以依次取出集合的每一個(gè)元素。

注意: 集合是指包含一組元素的數(shù)據(jù)結(jié)構(gòu),我們已經(jīng)介紹的包括:

1. 有序集合:list,tuple,str和unicode;

2. 無序集合:set

3. 無序集合并且具有 key-value 對(duì):dict

而迭代是一個(gè)動(dòng)詞,它指的是一種操作,在Python中,就是 for 循環(huán)。

迭代與按下標(biāo)訪問數(shù)組最大的不同是,后者是一種具體的迭代實(shí)現(xiàn)方式,而前者只關(guān)心迭代結(jié)果,根本不關(guān)心迭代內(nèi)部是如何實(shí)現(xiàn)的。

索引迭代

Python中,迭代永遠(yuǎn)是取出元素本身,而非元素的索引。

對(duì)于有序集合,元素確實(shí)是有索引的。有的時(shí)候,我們確實(shí)想在 for 循環(huán)中拿到索引,怎么辦?

方法是使用 enumerate 函數(shù)

... print index, '-', name

...

0 - Adam

1 - Lisa

2 - Bart

3 - Paul

使用 enumerate 函數(shù),我們可以在for循環(huán)中同時(shí)綁定索引index和元素name。但是,這不是 enumerate 的特殊語法。實(shí)際上,enumerate 函數(shù)把:

[(0, 'Adam'), (1, 'Lisa'), (2, 'Bart'), (3, 'Paul')]

index = t[0]

name = t[1]

print index, '-', name

如果我們知道每個(gè)tuple元素都包含兩個(gè)元素,for循環(huán)又可以進(jìn)一步簡寫為:

for index, namein enumerate(L):

print index, '-', name

這樣不但代碼更簡單,而且還少了兩條賦值語句。

可見,索引迭代也不是真的按索引訪問,而是由 enumerate 函數(shù)自動(dòng)把每個(gè)元素變成 (index, element) 這樣的tuple,再迭代,就同時(shí)獲得了索引和元素本身。

迭代dict的value

我們已經(jīng)了解了dict對(duì)象本身就是可迭代對(duì)象,用 for 循環(huán)直接迭代 dict,可以每次拿到dict的一個(gè)key。

如果我們希望迭代 dict 對(duì)象的value,應(yīng)該怎么做?

dict 對(duì)象有一個(gè) values 方法,這個(gè)方法把dict轉(zhuǎn)換成一個(gè)包含所有value的list,這樣,我們迭代的就是 dict的每一個(gè) value:

d = { 'Adam': 95, 'Lisa': 85, 'Bart': 59 }

print d.values

# [85, 95, 59]for vin d.values: print v# 85# 95# 59

如果仔細(xì)閱讀Python的文檔,還可以發(fā)現(xiàn),dict除了values方法外,還有一個(gè) itervalues 方法,用 itervalues 方法替代 values 方法,迭代效果完全一樣:

d = { 'Adam': 95, 'Lisa': 85, 'Bart': 59 }

print d.itervalues

print v

# 85

# 95

# 59

1. values 方法實(shí)際上把一個(gè) dict 轉(zhuǎn)換成了包含 value 的list。

2. 但是 itervalues 方法不會(huì)轉(zhuǎn)換,它會(huì)在迭代過程中依次從 dict 中取出 value,所以 itervalues 方法比 values 方法節(jié)省了生成 list 所需的內(nèi)存。

3. 打印 itervalues 發(fā)現(xiàn)它返回一個(gè)對(duì)象,這說明在Python中,for 循環(huán)可作用的迭代對(duì)象遠(yuǎn)不止 list,tuple,str,unicode,dict等,任何可迭代對(duì)象都可以作用于for循環(huán),而內(nèi)部如何迭代我們通常并不用關(guān)心。

如果一個(gè)對(duì)象說自己可迭代,那我們就直接用 for 循環(huán)去迭代它,可見,迭代是一種抽象的數(shù)據(jù)操作,它不對(duì)迭代對(duì)象內(nèi)部的數(shù)據(jù)有任何要求。

迭代dict的key和value

我們了解了如何迭代 dictkeyvalue,那么,在一個(gè) for 循環(huán)中,能否同時(shí)迭代 key和value?答案是肯定的。

首先,我們看看 dict 對(duì)象的 items 方法返回的值:

>>> d = { 'Adam': 95, 'Lisa': 85, 'Bart': 59 }

>>> print d.items

[('Lisa', 85), ('Adam', 95), ('Bart', 59)]

可以看到,items 方法把dict對(duì)象轉(zhuǎn)換成了包含tuple的list,我們對(duì)這個(gè)list進(jìn)行迭代,可以同時(shí)獲得key和value:

>>> for key, value in d.items:

... print key, ':', value

...

Lisa : 85

Adam : 95

Bart : 59

和 values 有一個(gè) itervalues 類似, items 也有一個(gè)對(duì)應(yīng)的 iteritems,iteritems 不把dict轉(zhuǎn)換成list,而是在迭代過程中不斷給出 tuple,所以, iteritems 不占用額外的內(nèi)存。

生成列表

要生成list [1, 2, 3, 4, 5, 6, 7, 8, 9, 10],我們可以用range(1, 11):

>>> range(1, 11)

[1, 2, 3, 4, 5, 6, 7, 8, 9, 10]

但如果要生成[1x1, 2x2, 3x3, ..., 10x10]怎么做?方法一是循環(huán):

>>> L =

>>> for x in range(1, 11):

... L.append(x * x)

...

>>> L

[1, 4, 9, 16, 25, 36, 49, 64, 81, 100]

但是循環(huán)太繁瑣,而列表生成式則可以用一行語句代替循環(huán)生成上面的list:

>>> [x * x for x in range(1, 11)]

[1, 4, 9, 16, 25, 36, 49, 64, 81, 100]

這種寫法就是Python特有的列表生成式。利用列表生成式,可以以非常簡潔的代碼生成 list。

寫列表生成式時(shí),把要生成的元素 x * x 放到前面,后面跟 for 循環(huán),就可以把list創(chuàng)建出來,十分有用,多寫幾次,很快就可以熟悉這種語法。

復(fù)雜表達(dá)式

使用for循環(huán)的迭代不僅可以迭代普通的list,還可以迭代dict。

完全可以通過一個(gè)復(fù)雜的列表生成式把它變成一個(gè) HTML 表格:

tds = ['%s%s'% (name, score)for name, scorein d.iteritems]

print '

print '

print '\n'.join(tds)

print '

'
NameScore
Lisa85
Adam95
Bart59

列表生成式的 for 循環(huán)后面還可以加上 if 判斷。例如:

[1, 4, 9, 16, 25, 36, 49, 64, 81, 100]

如果我們只想要偶數(shù)的平方,不改動(dòng) range的情況下,可以加上 if 來篩選:

>>> [x * xfor xin range(1, 11)if x % 2 == 0]

[4, 16, 36, 64, 100]

有了 if 條件,只有 if 判斷為 True 的時(shí)候,才把循環(huán)的當(dāng)前元素添加到列表中。

多層表達(dá)式

for循環(huán)可以嵌套,因此,在列表生成式中,也可以用多層 for 循環(huán)來生成列表。

對(duì)于字符串 'ABC' 和 '123',可以使用兩層循環(huán),生成全排列:

['A1', 'A2', 'A3', 'B1', 'B2', 'B3', 'C1', 'C2', 'C3']

翻譯成循環(huán)代碼就像下面這樣:

for nin '123':

L.append(m + n)

把函數(shù)作為參數(shù)

一個(gè)簡單的高階函數(shù):

def add(x, y, f):

return f(x) + f(y)

如果傳入abs作為參數(shù)f的值:

add(-5, 9, abs)

根據(jù)函數(shù)的定義,函數(shù)執(zhí)行的代碼實(shí)際上是:

abs(-5) + abs(9)

由于參數(shù) x, y 和 f 都可以任意傳入,如果 f 傳入其他函數(shù),就可以得到不同的返回值。

map函數(shù)

map是 Python 內(nèi)置的高階函數(shù),它接收一個(gè)函數(shù) f 和一個(gè) list,并通過把函數(shù) f 依次作用在 list 的每個(gè)元素上,得到一個(gè)新的 list 并返回。

例如,對(duì)于list [1, 2, 3, 4, 5, 6, 7, 8, 9]

如果希望把list的每個(gè)元素都作平方,就可以用map函數(shù):

因此,我們只需要傳入函數(shù)f(x)=x*x,就可以利用map函數(shù)完成這個(gè)計(jì)算:

def f(x):

return x*x

print map(f, [1, 2, 3, 4, 5, 6, 7, 8, 9])

輸出結(jié)果:

[1, 4, 9, 10, 25, 36, 49, 64, 81]

注意:map函數(shù)不改變?cè)械?list,而是返回一個(gè)新的 list。

利用map函數(shù),可以把一個(gè) list 轉(zhuǎn)換為另一個(gè) list,只需要傳入轉(zhuǎn)換函數(shù)。

由于list包含的元素可以是任何類型,因此,map 不僅僅可以處理只包含數(shù)值的 list,事實(shí)上它可以處理包含任意類型的 list,只要傳入的函數(shù)f可以處理這種數(shù)據(jù)類型。

reduce函數(shù)

reduce函數(shù)也是Python內(nèi)置的一個(gè)高階函數(shù)。reduce函數(shù)接收的參數(shù)和 map類似,一個(gè)函數(shù) f,一個(gè)list,但行為和 map不同,reduce傳入的函數(shù) f 必須接收兩個(gè)參數(shù),reduce對(duì)list的每個(gè)元素反復(fù)調(diào)用函數(shù)f,并返回最終結(jié)果值。

例如,編寫一個(gè)f函數(shù),接收x和y,返回x和y的和:

def f(x, y):

return x + y

調(diào)用 reduce(f, [1, 3, 5, 7, 9])時(shí),reduce函數(shù)將做如下計(jì)算:

先計(jì)算頭兩個(gè)元素:f(1, 3),結(jié)果為4;

再把結(jié)果和第3個(gè)元素計(jì)算:f(4, 5),結(jié)果為9;

再把結(jié)果和第4個(gè)元素計(jì)算:f(9, 7),結(jié)果為16;

再把結(jié)果和第5個(gè)元素計(jì)算:f(16, 9),結(jié)果為25;

由于沒有更多的元素了,計(jì)算結(jié)束,返回結(jié)果25。

上述計(jì)算實(shí)際上是對(duì) list 的所有元素求和。雖然Python內(nèi)置了求和函數(shù)sum,但是,利用reduce求和也很簡單。

reduce還可以接收第3個(gè)可選參數(shù),作為計(jì)算的初始值。如果把初始值設(shè)為100,計(jì)算:

reduce(f, [1, 3, 5, 7, 9], 100)

結(jié)果將變?yōu)?25,因?yàn)榈谝惠営?jì)算是:

計(jì)算初始值和第一個(gè)元素:f(100, 1),結(jié)果為101

filter函數(shù)

filter函數(shù)是 Python 內(nèi)置的另一個(gè)有用的高階函數(shù),filter函數(shù)接收一個(gè)函數(shù) f 和一個(gè)list,這個(gè)函數(shù) f 的作用是對(duì)每個(gè)元素進(jìn)行判斷,返回 True或 False,filter根據(jù)判斷結(jié)果自動(dòng)過濾掉不符合條件的元素,返回由符合條件元素組成的新list。

例如,要從一個(gè)list [1, 4, 6, 7, 9, 12, 17]中刪除偶數(shù),保留奇數(shù),首先,要編寫一個(gè)判斷奇數(shù)的函數(shù):

def is_odd(x):

return x % 2 == 1

然后,利用filter過濾掉偶數(shù):

filter(is_odd, [1, 4, 6, 7, 9, 12, 17])

結(jié)果:[1, 7, 9, 17]

利用filter,可以完成很多有用的功能,例如,刪除 None 或者空字符串:

def is_not_empty(s):

return s and len(s.strip) > 0

filter(is_not_empty, ['test', None, '', 'str', ' ', 'END'])

結(jié)果:['test', 'str', 'END']

注意: s.strip(rm) 刪除 s 字符串中開頭、結(jié)尾處的 rm 序列的字符。

當(dāng)rm為空時(shí),默認(rèn)刪除空白符(包括'\n', '\r', '\t', ' '),如下:

a = ' 123'

a.strip

結(jié)果: '123'

a='\t\t123\r\n'

a.strip

結(jié)果:'123'

自定義排序函數(shù)

Python內(nèi)置的 sorted函數(shù)可對(duì)list進(jìn)行排序:

>>>sorted([36, 5, 12, 9, 21])

[5, 9, 12, 21, 36]

sorted也是一個(gè)高階函數(shù),它可以接收一個(gè)比較函數(shù)來實(shí)現(xiàn)自定義排序,比較函數(shù)的定義是,傳入兩個(gè)待比較的元素 x, y,如果 x 應(yīng)該排在 y 的前面,返回 -1,如果 x 應(yīng)該排在 y 的后面,返回 1。如果 x 和 y 相等,返回 0。

因此,如果我們要實(shí)現(xiàn)倒序排序,只需要編寫一個(gè)reversed_cmp函數(shù):

def reversed_cmp(x, y):

if x > y:

return -1

if x <>

return 1

return 0

這樣,調(diào)用 sorted 并傳入 reversed_cmp 就可以實(shí)現(xiàn)倒序排序:

>>> sorted([36, 5, 12, 9, 21], reversed_cmp)

[36, 21, 12, 9, 5]

sorted也可以對(duì)字符串進(jìn)行排序,字符串默認(rèn)按照ASCII大小來比較:

>>> sorted(['bob', 'about', 'Zoo', 'Credit'])

['Credit', 'Zoo', 'about', 'bob']

'Zoo'排在'about'之前是因?yàn)?Z'的ASCII碼比'a'小。

返回函數(shù)

Python的函數(shù)不但可以返回int、str、list、dict等數(shù)據(jù)類型,還可以返回函數(shù)!

例如,定義一個(gè)函數(shù) f,我們讓它返回一個(gè)函數(shù) g,可以這樣寫:

def f:

print 'call f...'

return g

仔細(xì)觀察上面的函數(shù)定義,我們?cè)诤瘮?shù) f 內(nèi)部又定義了一個(gè)函數(shù) g。由于函數(shù) g 也是一個(gè)對(duì)象,函數(shù)名 g 就是指向函數(shù) g 的變量,所以,最外層函數(shù) f 可以返回變量 g,也就是函數(shù) g 本身。

調(diào)用函數(shù) f,我們會(huì)得到 f 返回的一個(gè)函數(shù):

call f...

>>> x # 變量x是f返回的函數(shù):

>>> x # x指向函數(shù),因此可以調(diào)用

call g... # 調(diào)用x就是執(zhí)行g(shù)函數(shù)定義的代碼

請(qǐng)注意區(qū)分返回函數(shù)和返回值:

def myabs:

return abs # 返回函數(shù)

def myabs2(x):

return abs(x) # 返回函數(shù)調(diào)用的結(jié)果,返回值是一個(gè)數(shù)值

返回函數(shù)可以把一些計(jì)算延遲執(zhí)行。例如,如果定義一個(gè)普通的求和函數(shù):

def calc_sum(lst):

return sum(lst)

調(diào)用calc_sum函數(shù)時(shí),將立刻計(jì)算并得到結(jié)果:

>>> calc_sum([1, 2, 3, 4])

10

但是,如果返回一個(gè)函數(shù),就可以“延遲計(jì)算”:

def calc_sum(lst):

def lazy_sum:

return sum(lst)

return lazy_sum

# 調(diào)用calc_sum并沒有計(jì)算出結(jié)果,而是返回函數(shù):

>>> f = calc_sum([1, 2, 3, 4])

>>> f

# 對(duì)返回的函數(shù)進(jìn)行調(diào)用時(shí),才計(jì)算出結(jié)果:

>>> f

10

由于可以返回函數(shù),我們?cè)诤罄m(xù)代碼里就可以決定到底要不要調(diào)用該函數(shù)。

閉包

在函數(shù)內(nèi)部定義的函數(shù)和外部定義的函數(shù)是一樣的,只是他們無法被外部訪問:

def g:

print 'g...'

def f:

print 'f...'

return g

g 的定義移入函數(shù) f 內(nèi)部,防止其他代碼調(diào)用 g

def f:

print 'f...'

def g:

print 'g...'

return g

def calc_sum(lst):

def lazy_sum:

return sum(lst)

return lazy_sum

注意: 發(fā)現(xiàn)沒法把 lazy_sum 移到 calc_sum 的外部,因?yàn)樗昧?calc_sum 的參數(shù) lst。

像這種內(nèi)層函數(shù)引用了外層函數(shù)的變量(參數(shù)也算變量),然后返回內(nèi)層函數(shù)的情況,稱為閉包(Closure)。

閉包的特點(diǎn)是返回的函數(shù)還引用了外層函數(shù)的局部變量,所以,要正確使用閉包,就要確保引用的局部變量在函數(shù)返回后不能變。舉例如下:

# 希望一次返回3個(gè)函數(shù),分別計(jì)算1x1,2x2,3x3:

def count:

fs =

for i in range(1, 4):

def f:

return i*i

fs.append(f)

return fs

f1, f2, f3 = count

你可能認(rèn)為調(diào)用f1,f2和f3結(jié)果應(yīng)該是1,4,9,但實(shí)際結(jié)果全部都是 9(請(qǐng)自己動(dòng)手驗(yàn)證)。

原因就是當(dāng)count函數(shù)返回了3個(gè)函數(shù)時(shí),這3個(gè)函數(shù)所引用的變量 i 的值已經(jīng)變成了3。由于f1、f2、f3并沒有被調(diào)用,所以,此時(shí)他們并未計(jì)算 i*i,當(dāng) f1 被調(diào)用時(shí):

>>> f1

9 # 因?yàn)閒1現(xiàn)在才計(jì)算i*i,但現(xiàn)在i的值已經(jīng)變?yōu)?

因此,返回函數(shù)不要引用任何循環(huán)變量,或者后續(xù)會(huì)發(fā)生變化的變量。

匿名函數(shù)

高階函數(shù)可以接收函數(shù)做參數(shù),有些時(shí)候,我們不需要顯式地定義函數(shù),直接傳入匿名函數(shù)更方便。

在Python中,對(duì)匿名函數(shù)提供了有限支持。還是以map函數(shù)為例,計(jì)算 f(x)=x2 時(shí),除了定義一個(gè)f(x)的函數(shù)外,還可以直接傳入匿名函數(shù):

>>> map(lambda x: x * x, [1, 2, 3, 4, 5, 6, 7, 8, 9])

[1, 4, 9, 16, 25, 36, 49, 64, 81]

通過對(duì)比可以看出,匿名函數(shù) lambda x: x * x 實(shí)際上就是:

def f(x):

return x * x

關(guān)鍵字lambda 表示匿名函數(shù),冒號(hào)前面的 x 表示函數(shù)參數(shù)。

匿名函數(shù)有個(gè)限制,就是只能有一個(gè)表達(dá)式,不寫return,返回值就是該表達(dá)式的結(jié)果。

使用匿名函數(shù),可以不必定義函數(shù)名,直接創(chuàng)建一個(gè)函數(shù)對(duì)象,很多時(shí)候可以簡化代碼:

>>> sorted([1, 3, 9, 5, 0], lambda x,y: -cmp(x,y))

[9, 5, 3, 1, 0]

返回函數(shù)的時(shí)候,也可以返回匿名函數(shù):

>>> myabs = lambda x: -x if x < 0="" else="">

>>> myabs(-1)

1

>>> myabs(1)

1

編寫無參數(shù)decorator

Python的 decorator 本質(zhì)上就是一個(gè)高階函數(shù),它接收一個(gè)函數(shù)作為參數(shù),然后,返回一個(gè)新函數(shù)。

使用 decorator 用Python提供的 @ 語法,這樣可以避免手動(dòng)編寫 f = decorate(f) 這樣的代碼。

考察一個(gè)@log的定義:

def log(f):

def fn(x):

print 'call ' + f.__name__ + '...'

return f(x)

return fn

對(duì)于階乘函數(shù),@log工作得很好:

@log

def factorial(n):

return reduce(lambda x,y: x*y, range(1, n+1))

print factorial(10)

結(jié)果:

call factorial...

3628800

但是,對(duì)于參數(shù)不是一個(gè)的函數(shù),調(diào)用將報(bào)錯(cuò):

@log

def add(x, y):

return x + y

print add(1, 2)

結(jié)果:

Traceback (most recent call last):

File 'test.py', line 15, in

print add(1,2)

TypeError: fn takes exactly 1 argument (2 given)

因?yàn)?add 函數(shù)需要傳入兩個(gè)參數(shù),但是 @log 寫死了只含一個(gè)參數(shù)的返回函數(shù)。

要讓 @log 自適應(yīng)任何參數(shù)定義的函數(shù),可以利用Python的 *args **kw,保證任意個(gè)數(shù)的參數(shù)總是能正常調(diào)用:

def log(f):

def fn(*args, **kw):

print 'call ' + f.__name__ + '...'

return f(*args, **kw)

return fn

現(xiàn)在,對(duì)于任意函數(shù),@log 都能正常工作。

編寫帶參數(shù)decorator

考察上一節(jié)的 @log 裝飾器:

def log(f):

def fn(x):

print 'call ' + f.__name__ + '...'

return f(x)

return fn

發(fā)現(xiàn)對(duì)于被裝飾的函數(shù),log打印的語句是不能變的(除了函數(shù)名)。

如果有的函數(shù)非常重要,希望打印出'[INFO] call xxx...',有的函數(shù)不太重要,希望打印出'[DEBUG] call xxx...',這時(shí),log函數(shù)本身就需要傳入'INFO'或'DEBUG'這樣的參數(shù),類似這樣:

@log('DEBUG')

def my_func:

pass

把上面的定義翻譯成高階函數(shù)的調(diào)用,就是:

my_func = log('DEBUG')(my_func)

上面的語句看上去還是比較繞,再展開一下:

log_decorator = log('DEBUG')

my_func = log_decorator(my_func)

上面的語句又相當(dāng)于:

@log_decorator

def my_func:

pass

所以,帶參數(shù)的log函數(shù)首先返回一個(gè)decorator函數(shù),再讓這個(gè)decorator函數(shù)接收my_func并返回新函數(shù):

def log(prefix):

def log_decorator(f):

def wrapper(*args, **kw):

print '[%s] %s...' % (prefix, f.__name__)

return f(*args, **kw)

return wrapper

return log_decorator

@log('DEBUG')

def test:

pass

print test

執(zhí)行結(jié)果:

[DEBUG] test...

None

對(duì)于這種3層嵌套的decorator定義,你可以先把它拆開:

# 標(biāo)準(zhǔn)decorator:

def log_decorator(f):

def wrapper(*args, **kw):

print '[%s] %s...' % (prefix, f.__name__)

return f(*args, **kw)

return wrapper

return log_decorator

# 返回decorator:

def log(prefix):

return log_decorator(f)

拆開以后會(huì)發(fā)現(xiàn),調(diào)用會(huì)失敗,因?yàn)樵?層嵌套的decorator定義中,最內(nèi)層的wrapper引用了最外層的參數(shù)prefix,所以,把一個(gè)閉包拆成普通的函數(shù)調(diào)用會(huì)比較困難。不支持閉包的編程語言要實(shí)現(xiàn)同樣的功能就需要更多的代碼。

完善decorator

@decorator可以動(dòng)態(tài)實(shí)現(xiàn)函數(shù)功能的增加,但是,經(jīng)過@decorator“改造”后的函數(shù),和原函數(shù)相比,除了功能多一點(diǎn)外,有沒有其它不同的地方?

在沒有decorator的情況下,打印函數(shù)名:

def f1(x):

pass

print f1.__name__

輸出: f1

有decorator的情況下,再打印函數(shù)名:

def log(f):

def wrapper(*args, **kw):

print 'call...'

return f(*args, **kw)

return wrapper

@log

def f2(x):

pass

print f2.__name__

輸出: wrapper

可見,由于decorator返回的新函數(shù)函數(shù)名已經(jīng)不是'f2',而是@log內(nèi)部定義的'wrapper'。這對(duì)于那些依賴函數(shù)名的代碼就會(huì)失效。decorator還改變了函數(shù)的__doc__等其它屬性。如果要讓調(diào)用者看不出一個(gè)函數(shù)經(jīng)過了@decorator的“改造”,就需要把原函數(shù)的一些屬性復(fù)制到新函數(shù)中:

def log(f):

def wrapper(*args, **kw):

print 'call...'

return f(*args, **kw)

wrapper.__name__ = f.__name__

wrapper.__doc__ = f.__doc__

return wrapper

這樣寫decorator很不方便,因?yàn)槲覀円埠茈y把原函數(shù)的所有必要屬性都一個(gè)一個(gè)復(fù)制到新函數(shù)上,所以Python內(nèi)置的functools可以用來自動(dòng)化完成這個(gè)“復(fù)制”的任務(wù):

import functools

def log(f):

@functools.wraps(f)

def wrapper(*args, **kw):

print 'call...'

return f(*args, **kw)

return wrapper

最后需要指出,由于我們把原函數(shù)簽名改成了(*args, **kw),因此,無法獲得原函數(shù)的原始參數(shù)信息。即便我們采用固定參數(shù)來裝飾只有一個(gè)參數(shù)的函數(shù):

def log(f):

@functools.wraps(f)

def wrapper(x):

print 'call...'

return f(x)

return wrapper

也可能改變?cè)瘮?shù)的參數(shù)名,因?yàn)樾潞瘮?shù)的參數(shù)名始終是 'x',原函數(shù)定義的參數(shù)名不一定叫 'x'。

關(guān)于Python裝飾器的講解有一篇比較通俗易懂的文章向大家推薦一下

偏函數(shù)

當(dāng)一個(gè)函數(shù)有很多參數(shù)時(shí),調(diào)用者就需要提供多個(gè)參數(shù)。如果減少參數(shù)個(gè)數(shù),就可以簡化調(diào)用者的負(fù)擔(dān)。

比如,int函數(shù)可以把字符串轉(zhuǎn)換為整數(shù),當(dāng)僅傳入字符串時(shí),int函數(shù)默認(rèn)按十進(jìn)制轉(zhuǎn)換:

>>> int('12345')

12345

int函數(shù)還提供額外的base參數(shù),默認(rèn)值為10。如果傳入base參數(shù),就可以做 N 進(jìn)制的轉(zhuǎn)換:

>>> int('12345', base=8)

5349

>>> int('12345', 16)

74565

假設(shè)要轉(zhuǎn)換大量的二進(jìn)制字符串,每次都傳入int(x, base=2)非常麻煩,于是,我們想到,可以定義一個(gè)int2的函數(shù),默認(rèn)把base=2傳進(jìn)去:

def int2(x, base=2):

return int(x, base)

這樣,我們轉(zhuǎn)換二進(jìn)制就非常方便了:

>>> int2('1000000')

64

>>> int2('1010101')

85

functools.partial就是幫助我們創(chuàng)建一個(gè)偏函數(shù)的,不需要我們自己定義int2,可以直接使用下面的代碼創(chuàng)建一個(gè)新的函數(shù)int2:

>>> import functools

>>> int2 = functools.partial(int, base=2)

>>> int2('1000000')

64

>>> int2('1010101')

85

所以,functools.partial可以把一個(gè)參數(shù)多的函數(shù)變成一個(gè)參數(shù)少的新函數(shù),少的參數(shù)需要在創(chuàng)建時(shí)指定默認(rèn)值,這樣,新函數(shù)調(diào)用的難度就降低了。

模塊和包

當(dāng)代碼數(shù)量逐漸增多時(shí),不可能全部都放在一個(gè).py文件中,這樣對(duì)以后的修改和查找都會(huì)帶來很大的困難,所以就會(huì)將不同功能的代碼抽離出來做成不同的模塊。然后也解決了在同一個(gè).py文件中出現(xiàn)大量的變量重名問題。不過當(dāng)模塊出現(xiàn)重名時(shí)就可以用到包了。包,也就是一個(gè)存放著.p文件即模塊的文件夾。

模塊的使用:

模塊:main.py

引用其它模塊,

# main.py

import math

Print math.pow(2.12)

引用math模塊時(shí)先寫import math

包是文件夾,模塊時(shí)xxx.py文件,包可以多級(jí)。

那么如何區(qū)分包和普通文件呢,在包內(nèi)一定又有個(gè)_init_.py文件,這個(gè)是必須有的。

導(dǎo)入模塊

要使用一個(gè)模塊,我們必須首先導(dǎo)入該模塊。Python使用import語句導(dǎo)入一個(gè)模塊。例如,導(dǎo)入系統(tǒng)自帶的模塊 math

import math

你可以認(rèn)為math就是一個(gè)指向已導(dǎo)入模塊的變量,通過該變量,我們可以訪問math模塊中所定義的所有公開的函數(shù)、變量和類:

>>> math.pow(2, 0.5) # pow是函數(shù)

1.4142135623730951

>>> math.pi # pi是變量

3.141592653589793

如果我們只希望導(dǎo)入用到的math模塊的某幾個(gè)函數(shù),而不是所有函數(shù),可以用下面的語句:

from math import pow, sin, log

這樣,可以直接引用 pow, sin, log 這3個(gè)函數(shù),但math的其他函數(shù)沒有導(dǎo)入進(jìn)來:

>>> pow(2, 10)

1024.0

>>> sin(3.14)

0.0015926529164868282

如果遇到名字沖突怎么辦?比如math模塊有一個(gè)log函數(shù),logging模塊也有一個(gè)log函數(shù),如果同時(shí)使用,如何解決名字沖突?

如果使用import導(dǎo)入模塊名,由于必須通過模塊名引用函數(shù)名,因此不存在沖突:

import math, logging

print math.log(10) # 調(diào)用的是math的log函數(shù)

logging.log(10, 'something') # 調(diào)用的是logging的log函數(shù)

如果使用 from...import 導(dǎo)入 log 函數(shù),勢必引起沖突。這時(shí),可以給函數(shù)起個(gè)“別名”來避免沖突:

from math import log

from logging import log as logger # logging的log現(xiàn)在變成了logger

print log(10) # 調(diào)用的是math的log

logger(10, 'import from logging') # 調(diào)用的是logging的log

這里可以和C++ 中的繼承中出現(xiàn)變量名字相同時(shí)的處理方法相比較。

File '', line 1, in

ImportError: No module named something

有的時(shí)候,兩個(gè)不同的模塊提供了相同的功能,比如 StringIO cStringIO 都提供了StringIO這個(gè)功能。

這是因?yàn)镻ython是動(dòng)態(tài)語言,解釋執(zhí)行,因此Python代碼運(yùn)行速度慢。

如果要提高Python代碼的運(yùn)行速度,最簡單的方法是把某些關(guān)鍵函數(shù)用 C 語言重寫,這樣就能大大提高執(zhí)行速度。

同樣的功能,StringIO 是純Python代碼編寫的,而 cStringIO 部分函數(shù)是 C 寫的,因此 cStringIO 運(yùn)行速度更快。

利用ImportError錯(cuò)誤,我們經(jīng)常在Python中動(dòng)態(tài)導(dǎo)入模塊:

try:

from cStringIO import StringIO

except ImportError:

from StringIO import StringIO

上述代碼先嘗試從cStringIO導(dǎo)入,如果失敗了(比如cStringIO沒有被安裝),再嘗試從StringIO導(dǎo)入。這樣,如果cStringIO模塊存在,則我們將獲得更快的運(yùn)行速度,如果cStringIO不存在,則頂多代碼運(yùn)行速度會(huì)變慢,但不會(huì)影響代碼的正常執(zhí)行。

try 的作用是捕獲錯(cuò)誤,并在捕獲到指定錯(cuò)誤時(shí)執(zhí)行 except 語句

使用__future__

Python的新版本會(huì)引入新的功能,但是,實(shí)際上這些功能在上一個(gè)老版本中就已經(jīng)存在了。要“試用”某一新的特性,就可以通過導(dǎo)入__future__模塊的某些功能來實(shí)現(xiàn)。

例如,Python 2.7的整數(shù)除法運(yùn)算結(jié)果仍是整數(shù):

>>> 10 / 3

3

但是,Python 3.x已經(jīng)改進(jìn)了整數(shù)的除法運(yùn)算,“/”除將得到浮點(diǎn)數(shù),“//”除才仍是整數(shù):

>>> 10 / 3

3.3333333333333335

>>> 10 // 3

3

要在Python 2.7中引入3.x的除法規(guī)則,導(dǎo)入__future__division

>>> from __future__ import division

>>> print 10 / 3

3.3333333333333335

當(dāng)新版本的一個(gè)特性與舊版本不兼容時(shí),該特性將會(huì)在舊版本中添加到__future__中,以便舊的代碼能在舊版本中測試新特性。

定義類并創(chuàng)建實(shí)例

在Python中,類通過 class 關(guān)鍵字定義。以 Person 為例,定義一個(gè)Person類如下:

class Person(object):

pass

按照 Python 的編程習(xí)慣,類名以大寫字母開頭,緊接著是(object),表示該類是從哪個(gè)類繼承下來的。類的繼承將在后面的章節(jié)講解,現(xiàn)在我們只需要簡單地從object類繼承。

有了Person類的定義,就可以創(chuàng)建出具體的xiaoming、xiaohong等實(shí)例。創(chuàng)建實(shí)例使用 類名+,類似函數(shù)調(diào)用的形式創(chuàng)建:

xiaoming = Person

xiaohong = Person

創(chuàng)建實(shí)例屬性

雖然可以通過Person類創(chuàng)建出xiaoming、xiaohong等實(shí)例,但是這些實(shí)例看上除了地址不同外,沒有什么其他不同。在現(xiàn)實(shí)世界中,區(qū)分xiaoming、xiaohong要依靠他們各自的名字、性別、生日等屬性。

如何讓每個(gè)實(shí)例擁有各自不同的屬性?由于Python是動(dòng)態(tài)語言,對(duì)每一個(gè)實(shí)例,都可以直接給他們的屬性賦值,例如,給xiaoming這個(gè)實(shí)例加上name、genderbirth屬性:

xiaoming = Person

xiaoming.name = 'Xiao Ming'

xiaoming.gender = 'Male'

xiaoming.birth = '1990-1-1'

xiaohong = Person

xiaohong.name = 'Xiao Hong'

xiaohong.school = 'No. 1 High School'

xiaohong.grade = 2

實(shí)例的屬性可以像普通變量一樣進(jìn)行操作:

xiaohong.grade = xiaohong.grade + 1

例子:

class Person(object):

pass

p1 = Person

p1.name = 'Bart'

p2 = Person

p2.name = 'Adam'

p3 = Person

p3.name = 'Lisa'

L1 = [p1, p2, p3]

L2 = sorted(L1,lambda p1,p2:cmp(p1.name,p2.name))

print L2[0].name

print L2[1].name

print L2[2].name

運(yùn)行結(jié)果:

Adam

Bart

Lisa

初始化實(shí)例屬性

雖然我們可以自由地給一個(gè)實(shí)例綁定各種屬性,但是,現(xiàn)實(shí)世界中,一種類型的實(shí)例應(yīng)該擁有相同名字的屬性。例如,Person類應(yīng)該在創(chuàng)建的時(shí)候就擁有 name、gender birth 屬性,怎么辦?

在定義 Person 類時(shí),可以為Person類添加一個(gè)特殊的__init__方法,當(dāng)創(chuàng)建實(shí)例時(shí),__init__方法被自動(dòng)調(diào)用,我們就能在此為每個(gè)實(shí)例都統(tǒng)一加上以下屬性:

class Person(object):

def __init__(self, name, gender, birth):

self.name = name

self.gender = gender

self.birth = birth

__init__ 方法的第一個(gè)參數(shù)必須是 self(也可以用別的名字,但建議使用習(xí)慣用法),后續(xù)參數(shù)則可以自由指定,和定義函數(shù)沒有任何區(qū)別。

相應(yīng)地,創(chuàng)建實(shí)例時(shí),就必須要提供除 self 以外的參數(shù):

xiaoming = Person('Xiao Ming', 'Male', '1991-1-1')

xiaohong = Person('Xiao Hong', 'Female', '1992-2-2')

有了__init__方法,每個(gè)Person實(shí)例在創(chuàng)建時(shí),都會(huì)有 name、gender birth 這3個(gè)屬性,并且,被賦予不同的屬性值,訪問屬性使用.操作符:

print xiaoming.name# 輸出 'Xiao Ming'

print xiaohong.birth# 輸出 '1992-2-2'

>>> class Person(object):

... def __init__(name, gender, birth):

... pass

...

>>> xiaoming = Person('Xiao Ming', 'Male', '1990-1-1')

File '', line 1, in

TypeError: __init__ takes exactly 3 arguments (4 given)

這會(huì)導(dǎo)致創(chuàng)建失敗或運(yùn)行不正常,因?yàn)榈谝粋€(gè)參數(shù)name被Python解釋器傳入了實(shí)例的引用,從而導(dǎo)致整個(gè)方法的調(diào)用參數(shù)位置全部沒有對(duì)上。

注:_init_C++中的構(gòu)造函數(shù)作用類似,初始化

訪問限制

我們可以給一個(gè)實(shí)例綁定很多屬性,如果有些屬性不希望被外部訪問到怎么辦?

Python對(duì)屬性權(quán)限的控制是通過屬性名來實(shí)現(xiàn)的,如果一個(gè)屬性由雙下劃線開頭(__),該屬性就無法被外部訪問??蠢樱?/span>

self.__job = 'Student'

File '', line 1, in

AttributeError: 'Person' object has no attribute '__job'

可見,只有以雙下劃線開頭的'__job'不能直接被外部訪問。

但是,如果一個(gè)屬性以'__xxx__'的形式定義,那它又可以被外部訪問了,以'__xxx__'定義的屬性在Python的類中被稱為特殊屬性,有很多預(yù)定義的特殊屬性可以使用,通常我們不要把普通屬性用'__xxx__'定義。

單下劃線開頭的屬性'_xxx'雖然也可以被外部訪問,但是,按照習(xí)慣,他們不應(yīng)該被外部訪問。

創(chuàng)建類屬性

類是模板,而實(shí)例則是根據(jù)類創(chuàng)建的對(duì)象。

綁定在一個(gè)實(shí)例上的屬性不會(huì)影響其他實(shí)例,但是,類本身也是一個(gè)對(duì)象,如果在類上綁定一個(gè)屬性,則所有實(shí)例都可以訪問類的屬性,并且,所有實(shí)例訪問的類屬性都是同一個(gè)!也就是說,實(shí)例屬性每個(gè)實(shí)例各自擁有,互相獨(dú)立,而類屬性有且只有一份。

def __init__(self, name):

self.name = name

因?yàn)轭悓傩允侵苯咏壎ㄔ陬惿系?,所以,訪問類屬性不需要?jiǎng)?chuàng)建實(shí)例,就可以直接訪問:

print Person.address# => Earth

對(duì)一個(gè)實(shí)例調(diào)用類的屬性也是可以訪問的,所有實(shí)例都可以訪問到它所屬的類的屬性:

p1 = Person('Bob')

p2 = Person('Alice')

print p1.address# => Earth

print p2.address# => Earth

由于Python是動(dòng)態(tài)語言,類屬性也是可以動(dòng)態(tài)添加和修改的:

Person.address = 'China'

print p1.address# => 'China'

print p2.address# => 'China'

因?yàn)轭悓傩灾挥幸环?,所以,?dāng)Person類的address改變時(shí),所有實(shí)例訪問到的類屬性都改變了。

類屬性和實(shí)例屬性名字沖突怎么辦

修改類屬性會(huì)導(dǎo)致所有實(shí)例訪問到的類屬性全部都受影響,但是,如果在實(shí)例變量上修改類屬性會(huì)發(fā)生什么問題呢?

class Person(object):

address = 'Earth'

def __init__(self, name):

self.name = name

p1 = Person('Bob')

p2 = Person('Alice')

print 'Person.address = ' + Person.address

p1.address = 'China'

print 'p1.address = ' + p1.address

print 'p2.address = ' + p2.address

結(jié)果如下:

Person.address = Earth

p1.address = China

p2.address = Earth

我們發(fā)現(xiàn),在設(shè)置了 p1.address = 'China' 后,p1訪問 address 確實(shí)變成了 'China',但是,Person.address和p2.address仍然是'Earch',怎么回事?

原因是 p1.address = 'China'并沒有改變 Person 的 address,而是給 p1這個(gè)實(shí)例綁定了實(shí)例屬性address ,對(duì)p1來說,它有一個(gè)實(shí)例屬性address(值是'China'),而它所屬的類Person也有一個(gè)類屬性address,所以:

訪問 p1.address 時(shí),優(yōu)先查找實(shí)例屬性,返回'China'。

訪問 p2.address 時(shí),p2沒有實(shí)例屬性address,但是有類屬性address,因此返回'Earth'。

可見,當(dāng)實(shí)例屬性和類屬性重名時(shí),實(shí)例屬性優(yōu)先級(jí)高,它將屏蔽掉對(duì)類屬性的訪問。

當(dāng)我們把 p1 的 address 實(shí)例屬性刪除后,訪問 p1.address 就又返回類屬性的值 'Earth'了:

del p1.address

print p1.address# => Earth

可見,千萬不要在實(shí)例上修改類屬性,它實(shí)際上并沒有修改類屬性,而是給實(shí)例綁定了一個(gè)實(shí)例屬性。

定義實(shí)例方法

一個(gè)實(shí)例的私有屬性就是以__開頭的屬性,無法被外部訪問,那這些屬性定義有什么用?

雖然私有屬性無法從外部訪問,但是,從類的內(nèi)部是可以訪問的。除了可以定義實(shí)例的屬性外,還可以定義實(shí)例的方法。

實(shí)例的方法就是在類中定義的函數(shù),它的第一個(gè)參數(shù)永遠(yuǎn)是 self,指向調(diào)用該方法的實(shí)例本身,其他參數(shù)和一個(gè)普通函數(shù)是完全一樣的:

class Person(object):

def __init__(self, name):

self.__name = name

def get_name(self):

return self.__name

get_name(self) 就是一個(gè)實(shí)例方法,它的第一個(gè)參數(shù)是self。__init__(self, name)其實(shí)也可看做是一個(gè)特殊的實(shí)例方法。

調(diào)用實(shí)例方法必須在實(shí)例上調(diào)用:

p1 = Person('Bob')

print p1.get_name # self不需要顯式傳入# => Bob

在實(shí)例方法內(nèi)部,可以訪問所有實(shí)例屬性,這樣,如果外部需要訪問私有屬性,可以通過方法調(diào)用獲得,這種數(shù)據(jù)封裝的形式除了能保護(hù)內(nèi)部數(shù)據(jù)一致性外,還可以簡化外部調(diào)用的難度。

方法也是屬性

我們?cè)?/span> class 中定義的實(shí)例方法其實(shí)也是屬性,它實(shí)際上是一個(gè)函數(shù)對(duì)象:

class Person(object):

def __init__(self, name, score):

self.name = name

self.score = score

def get_grade(self):

return 'A'

p1 = Person('Bob', 90)

print p1.get_grade# => A

也就是說,p1.get_grade 返回的是一個(gè)函數(shù)對(duì)象,但這個(gè)函數(shù)是一個(gè)綁定到實(shí)例的函數(shù),p1.get_grade 才是方法調(diào)用。

因?yàn)榉椒ㄒ彩且粋€(gè)屬性,所以,它也可以動(dòng)態(tài)地添加到實(shí)例上,只是需要用 types.MethodType 把一個(gè)函數(shù)變?yōu)橐粋€(gè)方法:

import types

def fn_get_grade(self):

if self.score >= 80:

return 'A'

if self.score >= 60:

return 'B'

return 'C'

class Person(object):

def __init__(self, name, score):

self.name = name

self.score = score

p1 = Person('Bob', 90)p1.get_grade = types.MethodType(fn_get_grade, p1, Person)

print p1.get_grade# => A

p2 = Person('Alice', 65)

print p2.get_grade# ERROR: AttributeError: 'Person' object has no attribute 'get_grade'

# 因?yàn)閜2實(shí)例并沒有綁定get_grade

給一個(gè)實(shí)例動(dòng)態(tài)添加方法并不常見,直接在class中定義要更直觀。

定義類方法

和屬性類似,方法也分實(shí)例方法類方法。

class中定義的全部是實(shí)例方法,實(shí)例方法第一個(gè)參數(shù) self 是實(shí)例本身。

要在class中定義類方法,需要這么寫:

class Person(object):

count = 0

@classmethod

def how_many(cls):

return cls.count

def __init__(self, name):

self.name = name

Person.count = Person.count + 1

print Person.how_many

p1 = Person('Bob')

通過標(biāo)記一個(gè) @classmethod,該方法將綁定到 Person 類上,而非類的實(shí)例。類方法的第一個(gè)參數(shù)將傳入類本身,通常將參數(shù)名命名為 cls,上面的 cls.count 實(shí)際上相當(dāng)于 Person.count。

因?yàn)槭窃陬惿险{(diào)用,而非實(shí)例上調(diào)用,因此類方法無法獲得任何實(shí)例變量,只能獲得類的引用。

類的繼承

涉及到的概念:父類(基類、超類),子類(派生類、繼承類)。

A繼承類B后,B成為父類,A則是子類。那么B中的一些實(shí)例屬性A也同樣擁有。這樣就會(huì)避免輸入

許多重復(fù)的的代碼。如果沒有類能夠繼承,那么就按標(biāo)準(zhǔn)繼承object。

繼承一個(gè)類

如果已經(jīng)定義了Person類,需要定義新的StudentTeacher類時(shí),可以直接從Person類繼承:

class Person(object):

def __init__(self, name, gender):

self.name = name

self.gender = gender

class Student(Person):

def __init__(self, name, gender, score):

super(Student, self).__init__(name, gender)

self.score = score

一定要用 super(Student, self).__init__(name, gender) 去初始化父類,否則,繼承自 PersonStudent 將沒有 namegender。

函數(shù)super(Student, self)將返回當(dāng)前類繼承的父類,即 Person ,然后調(diào)用__init__方法,注意self參數(shù)已在super中傳入,在__init__中將隱式傳遞,不需要寫出(也不能寫)。

判斷類型

函數(shù)isinstance可以判斷一個(gè)變量的類型,既可以用在Python內(nèi)置的數(shù)據(jù)類型如str、list、dict,也可以用在我們自定義的類,它們本質(zhì)上都是數(shù)據(jù)類型。

假設(shè)有如下的 Person、StudentTeacher 的定義及繼承關(guān)系如下:

class Person(object):

def __init__(self, name, gender):

self.name = name

self.gender = gender

self.score = score

class Teacher(Person):

def __init__(self, name, gender, course):

super(Teacher, self).__init__(name, gender)

self.course = course

p = Person('Tim', 'Male')

s = Student('Bob', 'Male', 88)

t = Teacher('Alice', 'Female', 'English')

當(dāng)我們拿到變量 p、s、t 時(shí),可以使用 isinstance 判斷類型:

>>> isinstance(p, Person)

True # p是Person類型

>>> isinstance(p, Student)

False # p不是Student類型

>>> isinstance(p, Teacher)

False # p不是Teacher類型

這說明在繼承鏈上,一個(gè)父類的實(shí)例不能是子類類型,因?yàn)樽宇惐雀割惗嗔艘恍傩院头椒ā?/span>

我們?cè)倏疾?/span> s

>>> isinstance(s, Person)

True # s是Person類型

>>> isinstance(s, Student)

True # s是Student類型

>>> isinstance(s, Teacher)

False # s不是Teacher類型

s 是Student類型,不是Teacher類型,這很容易理解。但是,s 也是Person類型,因?yàn)镾tudent繼承自Person,雖然它比Person多了一些屬性和方法,但是,把 s 看成Person的實(shí)例也是可以的。

這說明在一條繼承鏈上,一個(gè)實(shí)例可以看成它本身的類型,也可以看成它父類的類型。

多態(tài)

類具有繼承關(guān)系,并且子類類型可以向上轉(zhuǎn)型看做父類類型,如果我們從 Person 派生出 StudentTeacher ,并都寫了一個(gè) whoAmI 方法:

def whoAmI(self):

return 'I am a Person, my name is %s' % self.name

class Student(Person):

def __init__(self, name, gender, score):

super(Student, self).__init__(name, gender)

self.score = score

def whoAmI(self):

return 'I am a Student, my name is %s' % self.name

class Teacher(Person):

def __init__(self, name, gender, course):

super(Teacher, self).__init__(name, gender)

self.course = course

return 'I am a Teacher, my name is %s' % self.name

在一個(gè)函數(shù)中,如果我們接收一個(gè)變量 x,則無論該 x Person、Student還是 Teacher,都可以正確打印出結(jié)果:

def who_am_i(x):

print x.whoAmI

p = Person('Tim', 'Male')

s = Student('Bob', 'Male', 88)

t = Teacher('Alice', 'Female', 'English')

who_am_i(p)

who_am_i(s)

who_am_i(t)

運(yùn)行結(jié)果:

I am a Person, my name is Tim

I am a Student, my name is Bob

I am a Teacher, my name is Alice

這種行為稱為多態(tài)。也就是說,方法調(diào)用將作用在 x 的實(shí)際類型上。sStudent類型,它實(shí)際上擁有自己的 whoAmI方法以及從 Person繼承的 whoAmI方法,但調(diào)用 s.whoAmI總是先查找它自身的定義,如果沒有定義,則順著繼承鏈向上查找,直到在某個(gè)父類中找到為止。

由于Python是動(dòng)態(tài)語言,所以,傳遞給函數(shù) who_am_i(x)的參數(shù) x不一定是 Person 或 Person 的子類型。任何數(shù)據(jù)類型的實(shí)例都可以,只要它有一個(gè)whoAmI的方法即可:

class Book(object):

def whoAmI(self):

return 'I am a book'

這是動(dòng)態(tài)語言和靜態(tài)語言(例如Java)最大的差別之一。動(dòng)態(tài)語言調(diào)用實(shí)例方法,不檢查類型,只要方法存在,參數(shù)正確,就可以調(diào)用。

多重繼承

除了從一個(gè)父類繼承外,Python允許從多個(gè)父類繼承,稱為多重繼承。

多重繼承的繼承鏈就不是一棵樹了,它像這樣:

class A(object):

def __init__(self, a):

print 'init A...'

self.a = a

class B(A):

def __init__(self, a):

super(B, self).__init__(a)

print 'init B...'

class C(A):

def __init__(self, a):

super(C, self).__init__(a)

print 'init C...'

def __init__(self, a):

super(D, self).__init__(a)

print 'init D...'

看下圖:

像這樣,D 同時(shí)繼承自 BC,也就是 D 擁有了 A、B、C 的全部功能。多重繼承通過 super調(diào)用__init__方法時(shí),A 雖然被繼承了兩次,但__init__只調(diào)用一次:

>>> d = D('d')

init A...

init C...

init B...

init D...

多重繼承的目的是從兩種繼承樹中分別選擇并繼承出子類,以便組合功能使用。

舉個(gè)例子,Python的網(wǎng)絡(luò)服務(wù)器有TCPServer、UDPServer、UnixStreamServer、UnixDatagramServer,而服務(wù)器運(yùn)行模式有 多進(jìn)程ForkingMixin 多線程ThreadingMixin兩種。

要?jiǎng)?chuàng)建多進(jìn)程模式的 TCPServer

class MyTCPServer(TCPServer, ForkingMixin)

pass

要?jiǎng)?chuàng)建多線程模式的 UDPServer

class MyUDPServer(UDPServer, ThreadingMixin):

pass

如果沒有多重繼承,要實(shí)現(xiàn)上述所有可能的組合需要 4x2=8 個(gè)子類。

獲取對(duì)象信息

拿到一個(gè)變量,除了用 isinstance 判斷它是否是某種類型的實(shí)例外,還有沒有別的方法獲取到更多的信息呢?

例如,已有定義:

class Person(object):

def __init__(self, name, gender):

self.name = name

self.gender = gender

def __init__(self, name, gender, score):

super(Student, self).__init__(name, gender)

self.score = score

def whoAmI(self):

return 'I am a Student, my name is %s' % self.name

首先可以用 type 函數(shù)獲取變量的類型,它返回一個(gè) Type 對(duì)象:

>>> type(123)

>>> s = Student('Bob', 'Male', 88)

>>> type(s)

其次,可以用 dir 函數(shù)獲取變量的所有屬性:

>>> dir(123) # 整數(shù)也有很多屬性...

['__abs__', '__add__', '__and__', '__class__', '__cmp__', ...]

>>> dir(s)

['__class__', '__delattr__', '__dict__', '__doc__', '__format__', '__getattribute__', '__hash__', '__init__', '__module__', '__new__', '__reduce__', '__reduce_ex__', '__repr__', '__setattr__', '__sizeof__', '__str__', '__subclasshook__', '__weakref__', 'gender', 'name', 'score', 'whoAmI']

對(duì)于實(shí)例變量,dir返回所有實(shí)例屬性,包括`__class__`這類有特殊意義的屬性。注意到方法`whoAmI`也是 s 的一個(gè)屬性。

dir返回的屬性是字符串列表,如果已知一個(gè)屬性名稱,要獲取或者設(shè)置對(duì)象的屬性,就需要用 getattr setattr函數(shù)了:

>>> getattr(s, 'name') # 獲取name屬性

'Bob'

>>> setattr(s, 'name', 'Adam') # 設(shè)置新的name屬性

File '', line 1, in

AttributeError: 'Student' object has no attribute 'age'

>>> getattr(s, 'age', 20) # 獲取age屬性,如果屬性不存在,就返回默認(rèn)值20:

20

特殊方法(魔術(shù)方法)__str__和__repr__

如果要把一個(gè)類的實(shí)例變成 str,就需要實(shí)現(xiàn)特殊方法__str__

def __init__(self, name, gender):

self.name = name

self.gender = gender

def __str__(self):

return '(Person: %s, %s)' % (self.name, self.gender)

現(xiàn)在,在交互式命令行下用 print 試試:

>>> p = Person('Bob', 'male')

>>> print p

(Person: Bob, male)

但是,如果直接敲變量 p

>>> p

似乎__str__ 不會(huì)被調(diào)用。

因?yàn)?Python 定義了__str____repr__兩種方法,__str__用于顯示給用戶,而__repr__用于顯示給開發(fā)人員。

有一個(gè)偷懶的定義__repr__的方法:

class Person(object):

def __init__(self, name, gender):

self.name = name

self.gender = gender

def __str__(self):

return '(Person: %s, %s)' % (self.name, self.gender)

__repr__ = __str__

__cmp__

對(duì) int、str 等內(nèi)置數(shù)據(jù)類型排序時(shí),Python的 sorted 按照默認(rèn)的比較函數(shù) cmp 排序,但是,如果對(duì)一組 Student 類的實(shí)例排序時(shí),就必須提供我們自己的特殊方法 __cmp__

class Student(object):

def __init__(self, name, score):

self.name = name

self.score = score

def __str__(self):

return '(%s: %s)' % (self.name, self.score)

__repr__ = __str__

def __cmp__(self, s):

if self.name <>

return -1

elif self.name > s.name:

return 1

else:

return 0

上述 Student 類實(shí)現(xiàn)了__cmp__方法,__cmp__用實(shí)例自身self和傳入的實(shí)例 s 進(jìn)行比較,如果 self 應(yīng)該排在前面,就返回 -1,如果 s 應(yīng)該排在前面,就返回1,如果兩者相當(dāng),返回 0。

Student類實(shí)現(xiàn)了按name進(jìn)行排序:

>>> L = [Student('Tim', 99), Student('Bob', 88), Student('Alice', 77)]

>>> print sorted(L)

[(Alice: 77), (Bob: 88), (Tim: 99)]

注意: 如果list不僅僅包含 Student 類,則 __cmp__ 可能會(huì)報(bào)錯(cuò):

L = [Student('Tim', 99), Student('Bob', 88), 100, 'Hello']

print sorted(L)

__len__

如果一個(gè)類表現(xiàn)得像一個(gè)list,要獲取有多少個(gè)元素,就得用 len 函數(shù)。

要讓 len 函數(shù)工作正常,類必須提供一個(gè)特殊方法__len__,它返回元素的個(gè)數(shù)。

例如,我們寫一個(gè) Students 類,把名字傳進(jìn)去:

class Students(object):

def __init__(self, *args):

self.names = args

def __len__(self):

return len(self.names)

只要正確實(shí)現(xiàn)了__len__方法,就可以用len函數(shù)返回Students實(shí)例的“長度”:

>>> ss = Students('Bob', 'Alice', 'Tim')

>>> print len(ss)

3

數(shù)學(xué)運(yùn)算

Python 提供的基本數(shù)據(jù)類型 int、float 可以做整數(shù)和浮點(diǎn)的四則運(yùn)算以及乘方等運(yùn)算。

但是,四則運(yùn)算不局限于int和float,還可以是有理數(shù)、矩陣等。

要表示有理數(shù),可以用一個(gè)Rational來表示:

class Rational(object):

def __init__(self, p, q):

self.p = p

self.q = q

p、q 都是整數(shù),表示有理數(shù) p/q。

如果要讓Rational進(jìn)行+運(yùn)算,需要正確實(shí)現(xiàn)__add__:

class Rational(object):

def __init__(self, p, q):

self.p = p

self.q = q

def __add__(self, r):

return Rational(self.p * r.q + self.q * r.p, self.q * r.q)

def __str__(self):

return '%s/%s' % (self.p, self.q)

__repr__ = __str__

現(xiàn)在可以試試有理數(shù)加法:

>>> r1 = Rational(1, 3)

>>> r2 = Rational(1, 2)

>>> print r1 + r2

5/6

類型轉(zhuǎn)換

Rational類實(shí)現(xiàn)了有理數(shù)運(yùn)算,但是,如果要把結(jié)果轉(zhuǎn)為 int float 怎么辦?

考察整數(shù)和浮點(diǎn)數(shù)的轉(zhuǎn)換:

>>> int(12.34)

12

>>> float(12)

12.0

如果要把 Rational 轉(zhuǎn)為 int,應(yīng)該使用:

r = Rational(12, 5)

n = int(r)

要讓int函數(shù)正常工作,只需要實(shí)現(xiàn)特殊方法__int__:

class Rational(object):

def __init__(self, p, q):

self.p = p

self.q = q

def __int__(self):

return self.p // self.q

結(jié)果如下:

>>> print int(Rational(7, 2))

3

>>> print int(Rational(1, 3))

0

同理,要讓float函數(shù)正常工作,只需要實(shí)現(xiàn)特殊方法__float__。

@property

考察 Student 類:

class Student(object):

def __init__(self, name, score):

self.name = name

self.score = score

當(dāng)我們想要修改一個(gè) Studentscroe 屬性時(shí),可以這么寫:

s = Student('Bob', 59)

s.score = 60

但是也可以這么寫:

s.score = 1000

顯然,直接給屬性賦值無法檢查分?jǐn)?shù)的有效性。

如果利用兩個(gè)方法:

class Student(object):

def __init__(self, name, score):

self.name = name

self.__score = score

def get_score(self):

return self.__score

def set_score(self, score):

if score < 0="" or="" score=""> 100:

raise ValueError('invalid score')

這樣一來,s.set_score(1000) 就會(huì)報(bào)錯(cuò)。

這種使用 get/set 方法來封裝對(duì)一個(gè)屬性的訪問在許多面向?qū)ο缶幊痰恼Z言中都很常見。

但是寫 s.get_scores.set_score 沒有直接寫 s.score 來得直接。

有沒有兩全其美的方法?----有。

因?yàn)镻ython支持高階函數(shù),在函數(shù)式編程中我們介紹了裝飾器函數(shù),可以用裝飾器函數(shù)把 get/set 方法“裝飾”成屬性調(diào)用:

class Student(object):

def __init__(self, name, score):

self.name = name

self.__score = score

@property

def score(self):

return self.__score

@score.setter

def score(self, score):

if score < 0="" or="" score=""> 100:

raise ValueError('invalid score')

注意: 第一個(gè)score(self)是get方法,用@property裝飾,第二個(gè)score(self, score)是set方法,用@score.setter裝飾,@score.setter是前一個(gè)@property裝飾后的副產(chǎn)品。

現(xiàn)在,就可以像使用屬性一樣設(shè)置score了:

>>> s = Student('Bob', 59)

>>> s.score = 60

>>> print s.score

60

>>> s.score = 1000

Traceback (most recent call last):

...

ValueError: invalid score

說明對(duì) score 賦值實(shí)際調(diào)用的是 set方法。

__slots__

由于Python是動(dòng)態(tài)語言,任何實(shí)例在運(yùn)行期都可以動(dòng)態(tài)地添加屬性。

如果要限制添加的屬性,例如,Student類只允許添加 name、genderscore 這3個(gè)屬性,就可以利用Python的一個(gè)特殊的__slots__來實(shí)現(xiàn)。

顧名思義,__slots__是指一個(gè)類允許的屬性列表:

class Student(object):

__slots__ = ('name', 'gender', 'score')

def __init__(self, name, gender, score):

self.name = name

self.gender = gender

self.score = score

現(xiàn)在,對(duì)實(shí)例進(jìn)行操作:

>>> s = Student('Bob', 'male', 59)

>>> s.name = 'Tim' # OK

>>> s.score = 99 # OK

>>> s.grade = 'A'

Traceback (most recent call last):

...

AttributeError: 'Student' object has no attribute 'grade'

__slots__的目的是限制當(dāng)前類所能擁有的屬性,如果不需要添加任意動(dòng)態(tài)的屬性,使用__slots__也能節(jié)省內(nèi)存。

__call__

在Python中,函數(shù)其實(shí)是一個(gè)對(duì)象:

>>> f = abs

>>> f.__name__

'abs'

>>> f(-123)

123

由于 f 可以被調(diào)用,所以,f 被稱為可調(diào)用對(duì)象。

所有的函數(shù)都是可調(diào)用對(duì)象。

一個(gè)類實(shí)例也可以變成一個(gè)可調(diào)用對(duì)象,只需要實(shí)現(xiàn)一個(gè)特殊方法__call__

我們把 Person 類變成一個(gè)可調(diào)用對(duì)象:

class Person(object):

def __init__(self, name, gender):

self.name = name

self.gender = gender

def __call__(self, friend):

print 'My name is %s...' % self.name

print 'My friend is %s...' % friend

現(xiàn)在可以對(duì) Person 實(shí)例直接調(diào)用:

>>> p = Person('Bob', 'male')

>>> p('Tim')

My name is Bob...

My friend is Tim...

單看 p('Tim') 你無法確定 p 是一個(gè)函數(shù)還是一個(gè)類實(shí)例,所以,在Python中,函數(shù)也是對(duì)象,對(duì)象和函數(shù)的區(qū)別并不顯著。

本站僅提供存儲(chǔ)服務(wù),所有內(nèi)容均由用戶發(fā)布,如發(fā)現(xiàn)有害或侵權(quán)內(nèi)容,請(qǐng)點(diǎn)擊舉報(bào)。
打開APP,閱讀全文并永久保存 查看更多類似文章
猜你喜歡
類似文章
Python基礎(chǔ)語法大全及知識(shí)點(diǎn)總結(jié)(珍藏版)
Python學(xué)習(xí)
Python 和 JS 有什么相似?
如何制作二維碼,基礎(chǔ)語法300行代碼學(xué)習(xí)??!
入門python,看完這個(gè)300行代碼的例子,你們會(huì)喜歡的~
學(xué)習(xí)筆記20:Python基礎(chǔ)使用(參數(shù),嵌套,列表,元組,字典,字符串等)
更多類似文章 >>
生活服務(wù)
熱點(diǎn)新聞
分享 收藏 導(dǎo)長圖 關(guān)注 下載文章
綁定賬號(hào)成功
后續(xù)可登錄賬號(hào)暢享VIP特權(quán)!
如果VIP功能使用有故障,
可點(diǎn)擊這里聯(lián)系客服!

聯(lián)系客服