2.相等運(yùn)算符
3.is:同一性運(yùn)算符
#避免將is運(yùn)算符用于比較類似數(shù)值和字符串這類不可變值归薛,由于Python內(nèi)部操作這些對(duì)象的方式的原因吱晒,使用is運(yùn)算符的結(jié)果是不可預(yù)測(cè)的履磨。
4.in: 成員資格運(yùn)算符
【代碼】
name = input('what
is your name?')
if 's' in name:
print('Your name contains the letter "s".')
else:
print ('Your name does not contain the letter
"s".')
【結(jié)果】
what is your name?nihao
Your name does not contain the letter "s".
5.字符串和序列比較
【代碼】
print("alpha"< "beta")
print(
'FnOrD'.lower())
print(
'FnOrD'.upper())
print([
1,2] < [2,1]) #參與比較的不是字符而是元素的其他類型print([2,[1,4]] <[2,[1,5]]) #如果一個(gè)序列中包含其他序列元素仙辟,比較規(guī)則同樣適用于序列元素
【結(jié)果】
True
fnord
FNORD
True
True
6.布爾運(yùn)算符
【代碼】
#有時(shí)想要檢查一個(gè)以上的條件,可以這樣做:number = int(input('Enter
a number between 1 and 10:'))
if number <= 10:
if number
>=1:
print('Great!')
else:
print('Wrong!')
else:
print('Wrong')
#下面使用布爾思想進(jìn)行優(yōu)化if number <= 10 and number >= 1:
print('Great')
else:
print('Wrong')
#and運(yùn)算符就是所謂的布爾運(yùn)算符允懂。它連接兩個(gè)布爾值厕怜,并且在兩者都為真時(shí)返回真,否則返回假。與它同類的還有兩個(gè)運(yùn)算符粥航,or和not琅捏。
#
布爾運(yùn)算符有個(gè)有趣的特性:只有在需要求值時(shí)才進(jìn)行求值。例如递雀,表達(dá)式x and y需要兩個(gè)變量都為真時(shí)才為真柄延,如果x為假,表達(dá)之會(huì)立刻返回false映之,不管y的值拦焚。這類短路邏輯可以用來實(shí)現(xiàn)C和Java中所謂的三元運(yùn)算符。
【結(jié)果】
Enter a number between 1 and 10:111
Wrong
Wrong
5.4.7 斷言
【代碼】
#if語句有個(gè)非常有用的“近親”杠输,它的工作方式像下面:
#if not condition
#?? crash program
#
就是因?yàn)槠渌绦蛟谕硇r(shí)候崩潰赎败,比如在錯(cuò)誤條件出現(xiàn)時(shí)直接讓它崩潰。語句中使用的關(guān)鍵字是assertage = 10
assert 0 <
age < 100
age = -1
assert 0 <
age < 100
【結(jié)果】
Traceback (most recent call last):
? File"D:/python_file/20180113/test.py", line 8, in
??? assert 0 < age < 100
AssertionError
【代碼】
#如果需要確保程序中的某個(gè)條件一定為真才讓程序正常工作的話蠢甲,assert語句就有用了僵刮,它可以在程序中置入檢查點(diǎn),條件后添加字符串鹦牛,用于解釋斷言age = -1
assert 0 <
age < 100 ,'The
age must be realistic'
【結(jié)果】
Traceback (most recent call last):
? File"D:/python_file/20180113/test.py", line 3, in
??? assert 0 < age < 100,'The age must be realistic'
AssertionError: The age must be realistic
5.5 循環(huán)
但是怎樣才能重復(fù)執(zhí)行多次呢搞糕?比如,寫一個(gè)每月提醒付房租的程序曼追,但是不使用循環(huán)窍仰,需要這樣寫:
發(fā)郵件
等一個(gè)月
發(fā)郵件
等一個(gè)月
(繼續(xù)下去……)
但是如果想讓程序繼續(xù)執(zhí)行,直到認(rèn)為停止它呢礼殊?
5.5.1 while循環(huán)
【代碼】
x = 1
while x <= 100:
print(x)
??? x+=
1
【結(jié)果】
不寫了吧
【代碼】
name = ''
while not name.strip(): #這回驹吮,連空格也逃脫不掉了
??? name = input('Please
enter your name:')
print('Hello,%s!'% name)
【結(jié)果】
不寫了吧
5.5.2 for循環(huán)
【代碼】
#while語句非常靈活,它可以用來在任何條件為真的情況下重復(fù)執(zhí)行一個(gè)代碼塊晶伦。但是碟狞,有時(shí)還得量體裁衣。比如要為一個(gè)集合(序列或其他可迭代對(duì)象)的每個(gè)元素都執(zhí)行一個(gè)代碼塊words = ['this','is','an','ex','parrot']
for word in words:
print(word)
#range函數(shù)的工作方式類似于分片婚陪,它包含下限族沃,但不包含上限。
#range()
函數(shù)返回的是一個(gè)可迭代對(duì)象(類型是對(duì)象)泌参,而不是列表類型脆淹, 所以打印的時(shí)候不會(huì)打印列表。
# #list()
函數(shù)是對(duì)象迭代器沽一,把對(duì)象轉(zhuǎn)為一個(gè)列表盖溺。返回的變量類型為列表。a = range(0,10) #下限為0锯玛,上限為9a = list(a)
print(a)
#xrange函數(shù)的循環(huán)行為類似于range函數(shù),區(qū)別在于range函數(shù)一次創(chuàng)建整個(gè)序列而xrange一次只創(chuàng)建一個(gè)數(shù)。當(dāng)需要迭代一個(gè)巨大的序列時(shí)攘残,xrange會(huì)更高效拙友,一般情況下不需要關(guān)注它。
【結(jié)果】
this
is
an
ex
parrot
[0, 1, 2, 3, 4, 5, 6, 7, 8, 9]
5.5.3 循環(huán)遍歷字典元素
【代碼】
#一個(gè)簡單的for語句就能循環(huán)字典的所有鍵歼郭,就像處理序列一樣d = {'x':1,'y':2,'z':3}
for key in d:
print(key,'correspends to',d[key])
#for循環(huán)的一大好處就是可以使用序列解包
#
字典嚴(yán)肅的順序通常是沒有意義的遗契。迭代的時(shí)候,字典中的鍵和值都能保證被處理病曾,但是處理順序不確定牍蜂。如果處理順序很重要的話,可以把鍵保存在列表中泰涂。
【結(jié)果】
y correspends to 2
z correspends to 3
x correspends to 1
5.5.4 一些迭代工具
在Python中迭代序列時(shí)鲫竞,一些函數(shù)非常有用。有些函數(shù)位于itertools模塊中逼蒙,還有一些內(nèi)建函數(shù)也十分有用从绘。
[if !supportLists]1.? [endif]并行迭代
【代碼】
#程序可以同時(shí)迭代兩個(gè)序列。比如:names = ['anne','beth','george','damon']
ages = [
12,45,32,102]
#如果想要打印名字和對(duì)應(yīng)的年齡是牢,可以像下面這樣做:for i in range(len(names)):
print(names[i],'is',ages[i],'years
old')
print('---------------------------------------------')
z =
zip(names,ages) #python3中僵井,使用zip后,得到的是一個(gè)可迭代對(duì)象print(z)
for i in z:
print(i) #可以看出批什,打印出來的是一個(gè)元組print('---------------------------------------------')
#注:要重新獲得迭代對(duì)象,不能再使用上面一個(gè)z了社搅,以為上面的z已經(jīng)迭代到末尾了z = zip(names,ages) #python3中驻债,使用zip后,得到的是一個(gè)可迭代對(duì)象for i in z: #這里不嫩解包了罚渐?
??? print(i[0],'is',i[1],'years old')
【結(jié)果】
anne is 12 years old
beth is 45 years old
george is 32 years old
damon is 102 years old
---------------------------------------------
('anne', 12)
('beth', 45)
('george', 32)
('damon', 102)
---------------------------------------------
anne is 12 years old
beth is 45 years old
george is 32 years old
damon is 102 years old
【代碼】
#zip函數(shù)也可以用于任意多的序列却汉。關(guān)于它很重要的一點(diǎn)就是zip可以應(yīng)付不等長序列:當(dāng)最短的序列用完時(shí)停止z = zip(range(5),range(100000))
for i in z:
print(i)
#print(i[0],i[1])
【結(jié)果】
(0, 0)
(1, 1)
(2, 2)
(3, 3)
(4, 4)
[if !supportLists]2.? [endif]編號(hào)迭代
【代碼】
#有時(shí)候想要迭代序列中的對(duì)象,還想要獲取當(dāng)前對(duì)象的索引荷并。例如合砂,在一個(gè)字符串列表中替換所有包含'xxx'的子字符串。strings = ['hello,world!,xxx,yyy,zzz','dkdskldsklkl']
#strings 村莊
#string?
人家
#xxx????
要找的人for string in strings:#大框
??? if 'xxx' instring: #小框
??????? index = strings.index(string) #index相當(dāng)于門牌號(hào)
??????? strings[index] = '[censored]'
print(strings)
【結(jié)果】
['[censored]', 'dkdskldsklkl']
【同上源织,代碼】
#沒問題翩伪,但是在替換之前搜索給定的字符串似乎沒必要。如果不替換的話谈息,搜索還會(huì)返回錯(cuò)誤的索引缘屹,較好的版本如下index = 0
for string in strings:
if 'xxx' in string:
??????? strings[index] =
'[censored]'
??? index += 1
print(strings)
【結(jié)果】
['[censored]', 'dkdskldsklkl']
【同上,代碼】
#方法有點(diǎn)笨侠仇,不過可以接受轻姿。另一種方法是使用內(nèi)建的enumerate函數(shù):for index,string in enumerate(strings): #這樣的話犁珠,一戶人家和一戶人家的門牌號(hào)都有了
??? if 'xxx' instring:
??????? strings[index] =
'[censored]'
print(strings)
【結(jié)果】
['[censored]', 'dkdskldsklkl']
[if !supportLists]3.? [endif]翻轉(zhuǎn)和排序迭代
【代碼】
s = sorted([4,3,6,8,3])
print(s)
s2 =
sorted('Hello,
world!')
print(s2)
s3 =
list(reversed('Hello,
world!'))
print(s3)
#reversed函數(shù)和sorted函數(shù)作用相反,但是reversed函數(shù)還需要使用list函數(shù)把得到的對(duì)象解包互亮,sorted函數(shù)自帶解包功能犁享。
【結(jié)果】
[3, 3, 4, 6, 8]
[' ', '!', ',', 'H', 'd', 'e', 'l', 'l', 'l', 'o', 'o', 'r', 'w']
['!', 'd', 'l', 'r', 'o', 'w', ' ', ',', 'o', 'l', 'l', 'e', 'H']
5.5.5 跳出循環(huán)
一般來說,循環(huán)會(huì)一直執(zhí)行直到條件為假豹休,或者到序列元素用完時(shí)炊昆。但是有些時(shí)候可能會(huì)提前中斷一個(gè)循環(huán),進(jìn)行新的迭代威根。
[if !supportLists]1.? [endif]break
【代碼】
#結(jié)束(跳出)循環(huán)可以使用break語句凤巨。假設(shè)需要尋找100以內(nèi)的最大平方數(shù),那么程序可以從100往下迭代到0洛搀,當(dāng)找到一個(gè)平方數(shù)時(shí)就不需要繼續(xù)循環(huán)了敢茁。from math import sqrt
for n in range(99,0,-1): #range增加了第三個(gè)參數(shù):步長
??? root = sqrt(n)
if root
== int(root):
print(n)
break
【結(jié)果】
81
[if !supportLists]2.? [endif]continue
for x in seq:
??? if condition1:continue
??? if condition2:continue
??? if condition3:continue
??? do_something()
??? do_something_else()
??? do_another_thing()
??? etc()
很多時(shí)候,只要使用if語句就可以了姥卢;
for x in seq:
??? if not (condition1 orcondition2 or condition3):
?????? do_something()
?????? do_something_else()
?????? do_another_thing()
?????? etc()
盡管continue語句非常有用卷要,它卻不是最本質(zhì)的。應(yīng)該習(xí)慣使用break語句独榴,因?yàn)樵趙hile True語句中會(huì)經(jīng)常用到它僧叉。
[if !supportLists]3.? [endif]while True/break習(xí)語
【代碼1】
#Python中while和for循環(huán)非常靈活,但一旦使用while語句就會(huì)遇到一個(gè)需要更多功能的問題棺榔。word = 'dummy' #代碼有些丑瓶堕,在進(jìn)入循環(huán)體之前需要給word賦一個(gè)啞值。使用啞值就是工作沒有盡善盡美的標(biāo)志while word:
??? word =
input('please enter a word:')
#處理word
??? print('The word was',word)
【代碼2】
#解決啞值問題while True:
??? word =
input('please enter a word:')
if not word: break
??? #處理word
??? print('The word was',word)
#while True部分實(shí)現(xiàn)了一個(gè)永遠(yuǎn)不會(huì)自己停止的循環(huán)症歇。但在循環(huán)內(nèi)部的if語句中加入條件是可以的郎笆,在條件滿足時(shí)調(diào)用break語句,這樣就可以在循環(huán)體內(nèi)部的任何地方結(jié)束循環(huán)忘晤,而不僅僅在凱循環(huán)的開頭部分宛蚓。if/break語句自然地將循環(huán)分為兩部分:第1部分負(fù)責(zé)初始化,第2部分則在循環(huán)條件為真的情況下使用第1部分內(nèi)部初始化好的數(shù)據(jù)设塔。
5.5.6 循環(huán)中的else子句
broke_out = False
for x in seq:
??? do_something(x)
??? if condition(x):
?????? broke_out = True
?????? break
??? do_something_else(x)
if not broke_out:
??? print(“I didn’t break out!”)
更簡單的方式是在循環(huán)中增加一個(gè)else子句—它僅在沒有調(diào)用break時(shí)執(zhí)行凄吏。
【代碼】
from math import sqrt
for n in range(99,81,-1):
??? root = sqrt(n)
if root
== int(root):
print(n)
break
else: #for循環(huán)中的所有語句都執(zhí)行過的時(shí)候(沒有跳出循環(huán)),從這個(gè)地方開始執(zhí)行
??? print("Didn't find it!")
#跳出for循環(huán)的話闰蛔,從這個(gè)地方開始執(zhí)行
#
注:for和while循環(huán)中都可以使用continue痕钢、break語句和else子句
【結(jié)果】
Didn't find it!
5.6 列表推導(dǎo)式----輕量級(jí)循環(huán)
【代碼】
#列表推導(dǎo)式是利用其他列表創(chuàng)建新列表(類似于數(shù)學(xué)術(shù)語中的集合推導(dǎo)式)的一種方法。它的工作方式類似于for循環(huán)x = [x*x for x in range(10)]
x2 = [x*x
for x in range(10) if x % 3 == 0]
xy = [(x,y)
for x in range(3) for y in range(3)] #兩個(gè)for之間不需要加and
#
作為對(duì)比序六,下面的代碼使用兩個(gè)for語句創(chuàng)建了相同的列表:result = []
for x in range(3):
for y in range(3):
??????? result.append((x,y))
print(x)
print(x2)
print(xy)
print(result)
#也可以和if子句聯(lián)合使用任连,像以前一樣:girls = ['alice','bernice','clarice']
boys = [
'chris','arnold','bob']
print([b + '+' + g for g in girls for b in boys if b[0] == g[0]])
#注:使用普通的圓括號(hào)而不是方括號(hào)不會(huì)得到“元組推導(dǎo)式”。例诀,在最近的版本中随抠,則會(huì)得到一個(gè)生成器裁着。
【結(jié)果】
2
[0, 9, 36, 81]
[(0, 0), (0, 1), (0, 2), (1, 0), (1, 1), (1, 2), (2, 0), (2, 1), (2, 2)]
[(0, 0), (0, 1), (0, 2), (1, 0), (1, 1), (1, 2), (2, 0), (2, 1), (2, 2)]
['arnold+alice', 'bob+bernice', 'chris+clarice']
使用字典進(jìn)行效率更高的優(yōu)化,只使用一層for循環(huán):
【代碼】
girls = ['alice','bernice','clarice']
boys = [
'chris','arnold','bob']
letteGirls = {}
for girl in girls:
??? letteGirls.setdefault(girl[
0],[]).append(girl)
a = [b +
'+' + g for b in boys for g in letteGirls[b[0]]] #列表推導(dǎo)式不能使用元組"()"拱她,而應(yīng)使用列表"[]"print(a)
【結(jié)果】
['chris+clarice', 'arnold+alice', 'bob+bernice']
5.7 三人行
5.7.1 什么都沒發(fā)生
有時(shí)跨算,程序什么事情都不做,這種情況椭懊,應(yīng)讓pass出馬
【代碼】
#Python中空代碼是非法的,解決的方法就是在語句塊中加上一個(gè)pass語句:name = 'Bill Gates'
if name == 'Ralph Auldus Melish':
print('Welcome')
elif name == 'Enid':
#還沒完步势。氧猬。。
??? pass
elifname == 'Bill Gates':
print('Access Denied')
#注釋pass語句聯(lián)合的替代方案時(shí)插入字符串
【結(jié)果】
Access Denied
5.7.2 使用del刪除
【代碼】
#一般來說坏瘩,Python會(huì)刪除那些不再使用的對(duì)象scoundrel = {'age':42,'first
name':'Robin','last
name':'of Locksley'}
robin = scoundrel
print(scoundrel)
print(robin)
scoundrel =
None
print(robin)
#首先盅抚,robin和scoundrel都被綁定到同一個(gè)字典上。所以當(dāng)設(shè)置scoundrel為None的時(shí)候倔矾,字典通過robin還是可用的妄均。但是當(dāng)把robin也設(shè)置為None的時(shí)候,字典就“漂”在內(nèi)存里面了哪自,沒有任何名字綁定到它上面丰包。所以python解釋器直接刪除了那個(gè)字典(這種行為被稱為垃圾收集)。也可以使用None之外的其他值壤巷,字典同樣會(huì)“消失不見”
【結(jié)果】
{'age': 42, 'last name': 'of Locksley', 'first name': 'Robin'}
{'age': 42, 'last name': 'of Locksley', 'first name': 'Robin'}
{'age': 42, 'last name': 'of Locksley', 'first name': 'Robin'}
【代碼】
#使用del語句邑彪,不僅會(huì)移動(dòng)一個(gè)對(duì)象的引用,也會(huì)移除那個(gè)名字本身
# x = 1
# del x
# print(x)
x = ['Hello','world']
y = x
y[
1] = 'Pythoin'
print(x)
del x
print(y)
#原因是刪除的只是名稱胧华,而不是列表本身寄症。事實(shí)上,在Python中是沒有辦法刪除值的(也不需要過多考慮刪除值的問題矩动,因?yàn)樵谀硞€(gè)值不再使用的時(shí)候有巧,Python解釋器會(huì)負(fù)責(zé)內(nèi)存的回收)
【結(jié)果】
['Hello', 'Pythoin']
['Hello', 'Pythoin']
5.7.3 使用exec和eval執(zhí)行和求值字符串
有時(shí)可能會(huì)需要?jiǎng)討B(tài)創(chuàng)造Python代碼,然后將其作為語句執(zhí)行或作為表達(dá)式計(jì)算悲没,---在此之前篮迎,一定要謹(jǐn)慎
[if !supportLists]1.? [endif]exec
【代碼】
#在Python3中,exec是一個(gè)函數(shù)而不是語句exec('print("hello,world")') #exec函數(shù)的形參是一個(gè)字符串檀训,所以兩邊加引號(hào)from math import sqrt
exec("sqrt
= 1")
print(sqrt(4))
#為什么一開始要這樣做柑潦?exec函數(shù)最有用的地方在于可以動(dòng)態(tài)創(chuàng)建代碼字符串。為了安全起見峻凫,可以增加一個(gè)字典渗鬼,起到命名空間的作用。命名空間又稱為作用域荧琼,是個(gè)非常重要的只知識(shí)譬胎。
【結(jié)果】
hello,world
Traceback (most recent call last):
? File"D:/python_file/20180113/test.py", line 6, in
??? print(sqrt(4))
TypeError: 'int' object is not callable
【示例】
#可以通過增加in 來實(shí)現(xiàn)差牛,其中的就是起到放置代碼字符串命名空間作用的字典。from math import sqrt
scope = {}
exec('sqrt =
1',scope) #python3中使用這種形式限定作用域print(sqrt)
print(len(scope))
print(scope['sqrt']) #可以看出堰乔,sqrt=1 中偏化,sqrt是鍵,1是值print(scope.keys()) #因?yàn)閮?nèi)建的__builtins__字典自動(dòng)包含所有的內(nèi)建函數(shù)和值
【結(jié)果】
2
1
dict_keys(['sqrt', '__builtins__'])
[if !supportLists]2.? [endif]eval
【示例】
#eval(用于“求值”)是類似于exec的內(nèi)建函數(shù)镐侯。exec語句會(huì)執(zhí)行一些列Python語句侦讨,而eval會(huì)計(jì)算Python表達(dá)式(以字符串形式書寫),并且返回結(jié)果值苟翻,(exec語句并不返回任何對(duì)象韵卤,因?yàn)樗旧砭褪钦Z句)。e = eval(input("Enter
an arithmetic expression:"))
print(e)
#注:Python2中崇猫,表達(dá)式eval(raw_input(...))事實(shí)上等同于input(...).在Python3.0中沈条,raw_input被重命名為input
#
可以給eval語句提供兩個(gè)命名空間,一個(gè)全局的一個(gè)局部的诅炉。全局的必須是字典蜡歹,局部的可以是任何形式的映射。
#
目前Python內(nèi)沒有任何執(zhí)行不可信任代碼的安全方式涕烧,一個(gè)可選的方案是使用Pythono的實(shí)現(xiàn)月而,比如Jython,以及使用一些本地機(jī)制议纯,比如Java的sandbox
【結(jié)果】
Enter an arithmetic expression:6 + 18 * 2
42
【示例】
#初探作用域
#
給exec或者eval語句提供命名空間時(shí)景鼠,還可以在真正使用命名空間前放置一些值進(jìn)去scope = {}
scope[
'x'] = 2
scope['y'] = 3
print(eval('x*y',scope))
#同理,exec或者eval調(diào)用的作用域也能在另外一個(gè)上面使用:scope = {}
exec('x = 2',scope)
print(eval('x*x',scope))
【結(jié)果】
6
4
第六章 抽 象
這里痹扇,將會(huì)學(xué)習(xí)語句組織成函數(shù)铛漓,這樣,可以告訴計(jì)算機(jī)如何做事鲫构。有了函數(shù)之后浓恶,不必反反復(fù)復(fù)項(xiàng)計(jì)算機(jī)傳遞同樣的具體指令了。本章還會(huì)詳細(xì)介紹參數(shù)和作用域的概念结笨。以及遞歸概念及其在程序中的用途包晰。
6.1 懶惰即美德
【代碼】
fibs = [0,1]
for i in range(8):
??? fibs.append(fibs[-
2] +fibs[-1])
print(fibs)
#如果想要一次計(jì)算前10個(gè)數(shù)的話,沒有問題炕吸。甚至可以將用戶輸入的數(shù)字作為動(dòng)態(tài)范圍的長度使用伐憾,從而改變for語句循環(huán)的次數(shù)fibs = [0,1]
num =
int(input('How
many Fibonacci number do you want?')) #使用for i in range(num-2):
print('i=',i,)
??? fibs.append(fibs[-
2] +fibs[-1]) #主要在于循環(huán)幾次(總共需要5個(gè),原始序列中有兩個(gè))print(fibs)
【結(jié)果】
[0, 1, 1, 2, 3, 5, 8, 13, 21, 34]
How many Fibonacci number do you want?5
i= 0
i= 1
i= 2
[0, 1, 1, 2, 3]
#真正的程序員會(huì)讓自己的程序抽象一些赫模,上面的程序可以改寫為較抽象的版本:
#num = int(input('How many numbers do you want?'))
#print(fibs(num))
如果函數(shù)要用很多次的話树肃,這么做會(huì)節(jié)省很多精力
6.2 抽象和結(jié)構(gòu)
抽象可以節(jié)省很多工作,實(shí)際上它的作用還要更大瀑罗,它是計(jì)算機(jī)程序可以讓人讀懂的關(guān)鍵(這也是最基本的要求)胸嘴。計(jì)算機(jī)非常樂于處理精確和具體的指令雏掠,但是人可就不同了。
計(jì)算機(jī)會(huì):向前走10米劣像,左轉(zhuǎn)90度乡话,再走5步,右轉(zhuǎn)45度耳奕,走123步
人只需要知道:一直沿著街道走绑青,過橋,電影院就在左手邊屋群,這樣就明白了吧时迫!關(guān)鍵大家都知道怎樣走路和過橋,不需要指令來知道這些事谓晌。
組織計(jì)算機(jī)程序也是類似的。程序應(yīng)該是非常抽象的癞揉。事實(shí)上纸肉,我們把這段描述翻譯為Python程序。
page = download_page()
freqs = compute_frequencies(page)
for word,freq in freqs:
??? print word,freq
6.3 創(chuàng)建函數(shù)
函數(shù)可以調(diào)用(可能包含參數(shù))喊熟,它執(zhí)行某種行為并且返回一個(gè)值柏肪。一般來說,內(nèi)建的callable函數(shù)可以用來判斷函數(shù)是否可調(diào)用:
【示例】
import math
x =
1
y = math.sqrt
print(callable(x))
print(callable(y))
【結(jié)果】
False
True
【示例】
#那么芥牌,怎樣定義函數(shù)呢def hello(name):
return 'Hello,'+name+'!'
print(hello('world'))
print(hello('Gumby'))
【結(jié)果】
Hello,world!
Hello,Gumby!
【示例】
def fibs(num):
??? result = [
0,1]
for i in range(num-2):
??????? result.append(result[-
2]+result[-1])
return result
print(fibs(10))
print(fibs(15))
#注:result語句是用來從函數(shù)中返回值的
【結(jié)果】
[0, 1, 1, 2, 3, 5, 8, 13, 21, 34]
[0, 1, 1, 2, 3, 5, 8, 13, 21, 34, 55, 89, 144, 233, 377]
6.3.1 記錄函數(shù)
如果想要給函數(shù)寫文檔烦味,讓后面使用該函數(shù)人能理解的haul,可以加入注釋(以#開頭)壁拉。另外一個(gè)方式就是直接寫上字符串谬俄。這類字符串在其他地方可能會(huì)非常有用。如果在函數(shù)的開頭寫下字符串弃理,它就會(huì)作為函數(shù)的一部分進(jìn)行存儲(chǔ)溃论,這稱為文檔字符串。
【示例】
def square(x):
'Calculates the square of the number x.'
??? return x*x
print(square(2))
【結(jié)果】
4
【示例】
def square(x):
'Calculates the square of the number x.'
??? return x*x
print(square(2))
print(square.__doc__) #這樣對(duì)文檔字符串進(jìn)行訪問痘昌,__doc__是函數(shù)屬性钥勋。第7章會(huì)介紹更多關(guān)于屬性的知識(shí),雙下劃線表示它是個(gè)特殊屬性辆苔。這類特殊和"魔法"屬性在第9章中介紹
#
內(nèi)建的help函數(shù)是非常有用的算灸。在交互式解釋器中使用它,可以得到關(guān)于函數(shù)驻啤,包括它的文檔字符串的信息:print(help(square)) #第10章對(duì)help函數(shù)進(jìn)行討論
【結(jié)果】
4
Calculates the square of the number x.
Help on function square in module __main__:
square(x)
??? Calculates the square of thenumber x.
None
6.3.3 并非真正函數(shù)的函數(shù)
數(shù)學(xué)意義上的函數(shù)菲驴,總在計(jì)算其參數(shù)后返回點(diǎn)什么。Python的有些函數(shù)并不返回任何東西骑冗。但是Python的函數(shù)就是函數(shù)谢翎,即便它從學(xué)術(shù)上講并不是函數(shù)捍靠。沒有return語句,或者return后沒有跟任何值的函數(shù)不返回值
【示例】
def test():
print('This is printed')
return
??? print('This is not')
x =test()
#可以看到森逮,第2個(gè)return語句被跳過了(類似于循環(huán)中的break語句)
#
但是如果test不返回任何值榨婆,那么x又引用什么呢?print(x)
#好熟悉的None褒侧,所以所有的函數(shù)的確都返回了東西:當(dāng)不需要它們返回值的時(shí)候良风,它們就返回None∶乒看來烟央,“有些函數(shù)并不是真正的函數(shù)”的說法有些不公平了。
#
不要被默認(rèn)的行為所迷惑歪脏,當(dāng)調(diào)用者期待一個(gè)序列的時(shí)候疑俭,就不會(huì)意外地返回None。
【結(jié)果】
This is printed
None
6.4 參數(shù)魔法
函數(shù)用起來很簡單婿失,創(chuàng)建起來也不復(fù)雜钞艇。但函數(shù)參數(shù)的用法就有時(shí)有些不可思議了。
6.4.1 值從哪里來
寫在def語句中函數(shù)名后面的變量通常叫做函數(shù)的形式參數(shù)豪硅,而調(diào)用函數(shù)的時(shí)候提供的值是實(shí)際參數(shù)(值)哩照。
6.4.2 能改變參數(shù)么
【示例】
#函數(shù)通過它的參數(shù)獲得一系列值。那參數(shù)只是變量而已懒浮,所以它們的行為和預(yù)想的一樣飘弧。在函數(shù)內(nèi)為參數(shù)賦予新值不會(huì)改變外部任何變量的值:def try_to_change(n):
n = 'Mr. Gumby'
name = 'Mrs. Entity'
try_to_change(name)
print(name) #可以看出,該函數(shù)沒有改變變量name的值
#
在try_to_change函數(shù)的內(nèi)部砚著,參數(shù)n獲得了新值次伶,但是它沒有影響到name變量。n實(shí)際上是個(gè)完全不同的變量稽穆,具體的工作方式類似于下面:name = "Mrs. Entity"
n = name #這等于傳遞參數(shù)n = 'Mr. Gumby'
print(name)
#結(jié)果是顯而易見的学少,當(dāng)變量n改變的時(shí)候,變量name不變秧骑。同樣版确,當(dāng)在函數(shù)內(nèi)部把參數(shù)重綁定(賦值)的時(shí)候,函數(shù)外部的變量不會(huì)受到影響乎折。
#
注:參數(shù)存儲(chǔ)在局部作用域內(nèi)
【結(jié)果】
Mrs. Entity
Mrs. Entity
【示例】
#字符串(以及數(shù)字和元組)是不可變的绒疗,即無法修改(也就是說只能用新的值覆蓋)。但是:如果將可變的數(shù)據(jù)結(jié)構(gòu)如列表用作參數(shù)的時(shí)候會(huì)發(fā)生什么:def change(n): #關(guān)鍵看實(shí)參是什么骂澄,實(shí)參是字符串吓蘑,則不能改變,實(shí)參是列表,則能改變
??? n[0] = 'Mr. Gumby'
names = ['Mrs. Entity','Mrs.
Thing']
change(names)
print(names)
#本例中磨镶,參數(shù)被改變了溃蔫。這就是本例和前面例子中至關(guān)重要的區(qū)別。前面的例子中琳猫,局部變量被賦予了新值伟叛,但是這個(gè)例子中變量names所綁定的列表的確改變了。有些奇怪了吧脐嫂?
#
下面進(jìn)行模仿names = ['Mrs. Entity','Mrs.
Thing']
n = names
#再來一次统刮,模擬傳參行為n[0] = 'Mr. Gumby' #改變列表names
【結(jié)果】
['Mr. Gumby', 'Mrs. Thing']
【示例】
#這種情況在前面已經(jīng)出現(xiàn)很多次了。當(dāng)兩個(gè)變量同時(shí)引用一個(gè)列表的時(shí)候账千,它們的確是同時(shí)引用一個(gè)列表侥蒙。如果想避免出現(xiàn)這種情況,可以復(fù)制一個(gè)列表的副本匀奏。在序列中做切片的時(shí)候鞭衩,返回的切片總是一個(gè)副本。names =['Mrs. Entity','Mrs.
Thing']
n = names[:]
#使用切片進(jìn)行復(fù)制(這個(gè)相當(dāng)于字典中的深復(fù)制)
#
如果現(xiàn)在改變n娃善,則不會(huì)影響到namesn[0] = 'Mr. Gumby'
print(n)
print(names)
【結(jié)果】
['Mr. Gumby', 'Mrs. Thing']
['Mrs. Entity', 'Mrs. Thing']
【示例】
#這種情況在前面已經(jīng)出現(xiàn)很多次了论衍。當(dāng)兩個(gè)變量同時(shí)引用一個(gè)列表的時(shí)候,它們的確是同時(shí)引用一個(gè)列表会放。如果想避免出現(xiàn)這種情況,可以復(fù)制一個(gè)列表的副本钉凌。在序列中做切片的時(shí)候,返回的切片總是一個(gè)副本。names =['Mrs. Entity','Mrs.
Thing']
n = names[:]
#使用切片進(jìn)行復(fù)制(這個(gè)相當(dāng)于字典中的深復(fù)制)
#
如果現(xiàn)在改變n蓝谨,則不會(huì)影響到namesn[0] = 'Mr. Gumby'
print(n) #n變了print(names) #names沒變
#
再用change試一下def change(n): #關(guān)鍵看實(shí)參是什么削樊,實(shí)參是字符串,則不能改變酸纲,實(shí)參是列表捣鲸,則能改變
??? n[0] = 'Mr. Gumby'
change(names[:]) #使用這種方式,傳遞進(jìn)去的是一個(gè)串闽坡,如果形參是names,則傳遞進(jìn)去的是一個(gè)列表print(names)
【結(jié)果】
['Mr. Gumby', 'Mrs. Thing']
['Mrs. Entity', 'Mrs. Thing']
['Mrs. Entity', 'Mrs. Thing']
[if !supportLists]1.? [endif]為什么我們要修改參數(shù)
使用函數(shù)改變數(shù)據(jù)結(jié)構(gòu)(比如列表或字典)是將程序抽象化的好方法栽惶。假設(shè)需要編寫一個(gè)存儲(chǔ)名字并且能用名字、中間名或姓查找聯(lián)系人的程序疾嗅,可以使用下面的數(shù)據(jù)結(jié)構(gòu):
【示例】
storage= {}
storage[
'first'] = {}
storage[
'middle'] = {}
storage[
'last'] = {}
#storage這個(gè)數(shù)據(jù)結(jié)構(gòu)的存儲(chǔ)方式是帶有3個(gè)鍵“first”外厂、“middle”和“l(fā)ast”的字典。每個(gè)鍵的下面都又存儲(chǔ)一個(gè)字典代承。子字典中汁蝶,可以使用名字(名字、中間名或姓)作為鍵,插入聯(lián)系人列表作為值掖棉。me = 'Magnus Lie Hetland'
storage['first']['Magnus'] = [me] #Magnus是key墓律,me是value(說明value是一個(gè)列表,可以使用append方法進(jìn)行追加元素)storage['middle']['Lie'] =[me]
storage[
'last']['Hetland'] = [me]
#每個(gè)鍵下面都存儲(chǔ)了一個(gè)以人名組成的列表幔亥。本例表中耻讽,人名只有“我”。如果想得到所有注冊(cè)的中間名為Lie的人紫谷,可以像下面這樣做:print(storage['middle']['Lie'])
#將人名加入到列表中的步驟有些枯燥乏味齐饮,尤其是要加入很多姓名相同的人時(shí),因?yàn)橐獢U(kuò)展已經(jīng)存儲(chǔ)了哪些名字的列表笤昨。例如祖驱,下面加入我姐姐的名字,而且假設(shè)不知道數(shù)據(jù)庫中已經(jīng)存儲(chǔ)了什么:my_sister = 'Anne Lie Hetland'
storage['first'].setdefault('Anne',[]).append(my_sister)#Anne是key瞒窒,my_sister是valuestorage['middle'].setdefault('Lie',[]).append(my_sister)
storage[
'last'].setdefault('Hetland',[]).append(my_sister)
print(storage['middle']['Lie'])
【結(jié)果】
['Magnus Lie Hetland']
['Magnus Lie Hetland', 'Anne Lie Hetland']
【代碼】
#如果要寫個(gè)大程序來這樣更新列表捺僻,那么很顯然程序很快就會(huì)變得臃腫且笨拙不堪了。抽象的要點(diǎn)就是隱藏更新時(shí)的繁瑣的細(xì)節(jié)崇裁,這個(gè)過程可以使用函數(shù)來實(shí)現(xiàn)匕坯。def init(data): #data本身是個(gè)字典
??? data['first'] = {}
??? data[
'middle'] = {}
??? data[
'last'] = {}
#使用的方法如下:storage = {}
init(storage)
#print(storage)
#可以看到,函數(shù)包辦了初始化工作拔稳,讓代碼更易讀
#
在編寫存儲(chǔ)名字的函數(shù)前葛峻,先寫個(gè)獲得名字的函數(shù)def lookup(data,label,name):
return data[label].get(name)
#標(biāo)簽(比如“middle")以及名字(比如“Lie”)可以作為參數(shù)提供給lookup函數(shù)使用,這樣會(huì)獲得包含全名的列表
#
注:返回的列表和存儲(chǔ)在數(shù)據(jù)結(jié)構(gòu)中的列表是相同的巴比,所以如果列表修改了术奖,那么也會(huì)影響數(shù)據(jù)結(jié)構(gòu)def store(data,full_name):
??? names = full_name.split()
#得到的是一個(gè)列表['','','',...]
??? if len(names)
== 2:
??????? names.insert(
1,'') #在names列表的下標(biāo)為1的位置處插入
??? labels = 'first','middle','last'#是一個(gè)元組
??? for label,name inzip(labels,names):
??????? people = lookup(data,label,name)
if people:
??????????? people.append(full_name)
else:
??????????? data[label][name] =[full_name]
#如果原先沒有值,則賦值
#
(1)通過參數(shù)data和full_name進(jìn)入函數(shù)轻绞,這兩個(gè)參數(shù)被設(shè)置為函數(shù)在外部獲得的一些值采记。
#
(2)通過拆分full_name,得到一個(gè)叫做names的列表
#
(3)如果names的長度為2(只有首名和末名)政勃,那么插入一個(gè)空字符串作為中間名
#
(4)將字符串“first”唧龄、“middle”和“l(fā)ast”作為元組存儲(chǔ)在labels中(也可以使用列表,這里只是為了方便而去掉括號(hào))
#
(5)使用zip函數(shù)聯(lián)合標(biāo)簽和名字奸远,對(duì)于每一個(gè)(label既棺,name)對(duì),進(jìn)行以下處理:
??? #
①獲得屬于給定標(biāo)簽和名字的列表
??? #
②將full_name添加到列表中懒叛,或者插入一個(gè)需要更新的新列表MyNames = {}
init(MyNames)
store(MyNames,
'Magnus Lie
Hetland')
print(lookup(MyNames,'middle','Lie'))
#好像可以工作援制,再試試store(MyNames,'Robin Hood')
store(MyNames,
'Robin Locksley')
print(lookup(MyNames,'first','Robin'))
store(MyNames,
'Mr. Gumby')
print(lookup(MyNames,'middle',''))
#可看到,如果某些人的名字芍瑞、中間名或姓相同晨仑,那么結(jié)果中會(huì)包含所有這些人的信息。
#
這類程序很適合進(jìn)行面向?qū)ο蟪绦蛟O(shè)計(jì)
【結(jié)果】
['Magnus Lie Hetland']
['Robin Hood', 'Robin Locksley']
['Robin Hood', 'Robin Locksley', 'Mr. Gumby']
[if !supportLists]2.? [endif]如果我的參數(shù)不可變呢
在某些語言(如C++,Pascal和Ada)中洪己,重綁定參數(shù)并且使這些改變到函數(shù)外的變量是很平常的事情妥凳。但在Python中,這是不可能的:函數(shù)只能修改參數(shù)對(duì)象本身答捕。但是如果你的參數(shù)不可變—比如是數(shù)字—又該怎么辦呢逝钥?
不好意思,沒有辦法拱镐。這個(gè)時(shí)候應(yīng)該從函數(shù)中返回所有需要的值(如果值多余一個(gè)的話就以元組的形式返回)艘款。
??1???N??