把學(xué)過的知識(shí)記錄下來(lái)。
1.安裝Python 3.6
目前,Python有兩個(gè)版本,一個(gè)是2.x版,一個(gè)是3.x版,這兩個(gè)版本是不兼容的。自行百度安裝3.x。
2.Python基礎(chǔ)語(yǔ)法
print()會(huì)依次打印每個(gè)字符串,遇到逗號(hào)“,”會(huì)輸出一個(gè)空格.
Python的語(yǔ)法比較簡(jiǎn)單,采用縮進(jìn)方式,如下
# 這個(gè)是注釋
a = 100
if a >= 0:
print(a)
else:
print(-a)
以#開頭的語(yǔ)句是注釋,注釋是給人看的,可以是任意內(nèi)容,解釋器會(huì)忽略掉注釋。其他每一行都是一個(gè)語(yǔ)句,當(dāng)語(yǔ)句以冒號(hào):結(jié)尾時(shí),縮進(jìn)的語(yǔ)句視為代碼塊。按照約定俗成的管理,應(yīng)該始終堅(jiān)持使用4個(gè)空格的縮進(jìn)。Python程序是大小寫敏感的,Tab和4個(gè)空格不一樣哦。
2.1 數(shù)據(jù)類型
整數(shù) Python可以處理任意大小的整數(shù),當(dāng)然包括負(fù)整數(shù)
浮點(diǎn)數(shù) 科學(xué)計(jì)數(shù)法表示,把10用e替代,1.23x109就是1.23e9,或者12.3e8,0.000012可以寫成1.2e-5,等等。
整數(shù)和浮點(diǎn)數(shù)在計(jì)算機(jī)內(nèi)部存儲(chǔ)的方式是不同的,整數(shù)運(yùn)算永遠(yuǎn)是精確的(除法難道也是精確的?是的?。↑c(diǎn)數(shù)運(yùn)算則可能會(huì)有四舍五入的誤差。
字符串 字符串是以單引號(hào)'或雙引號(hào)"括起來(lái)的任意文本
'I\'m \"OK\"!' #轉(zhuǎn)義字符\來(lái)標(biāo)識(shí) I'm "OK"!
Python還允許用r''表示''內(nèi)部的字符串默認(rèn)不轉(zhuǎn)義
Python允許用'''...'''的格式表示多行內(nèi)容
布爾值 在Python中,可以直接用True、False表示布爾值(請(qǐng)注意大小寫)布爾值可以用and、or和not運(yùn)算。
空值 空值是Python里一個(gè)特殊的值,用None表示。None不能理解為0,因?yàn)?是有意義的,而None是一個(gè)特殊的空值。
變量 理解變量在計(jì)算機(jī)內(nèi)存中的表示也非常重要。當(dāng)我們寫:a = 'ABC' 時(shí),Python解釋器干了兩件事情:
- 在內(nèi)存中創(chuàng)建了一個(gè)'ABC'的字符串;
- 在內(nèi)存中創(chuàng)建了一個(gè)名為a的變量,并把它指向'ABC'。
常量 在Python中,通常用全部大寫的變量名表示常量:
#在Python中,有兩種除法,一種除法是/:
print(10 / 3) #3.3333333333333335
#/除法計(jì)算結(jié)果是浮點(diǎn)數(shù),即使是兩個(gè)整數(shù)恰好整除,結(jié)果也是浮點(diǎn)數(shù)
print(9 / 3)
#還有一種除法是//,稱為地板除,兩個(gè)整數(shù)的除法仍然是整數(shù):
print(10 // 3) #整數(shù)的地板除//永遠(yuǎn)是整數(shù) //除法只取結(jié)果的整數(shù)部分
print(10 % 3) #得到兩個(gè)整數(shù)相除的余數(shù)
#無(wú)論整數(shù)做//除法還是取余數(shù),結(jié)果永遠(yuǎn)是整數(shù),所以,整數(shù)運(yùn)算結(jié)果永遠(yuǎn)是精確的。
2.2字符串和編碼
字符編碼 字符串比較特殊的是還有一個(gè)編碼問題
因?yàn)橛?jì)算機(jī)只能處理數(shù)字,如果要處理文本,就必須先把文本轉(zhuǎn)換為數(shù)字才能處理。
在最新的Python 3版本中,字符串是以Unicode編碼的,也就是說(shuō),Python的字符串支持多語(yǔ)言
#對(duì)于單個(gè)字符的編碼
print(ord('A')) #獲取字符的整數(shù)表示
print(chr(65)) #把編碼轉(zhuǎn)換為對(duì)應(yīng)的字符
x = b'ABC' #Python對(duì)bytes類型的數(shù)據(jù)用帶b前綴的單引號(hào)或雙引號(hào)表示
#要注意區(qū)分'ABC'和b'ABC',前者是str,后者雖然內(nèi)容顯示得和前者一樣,但bytes的每個(gè)字符都只占用一個(gè)字節(jié)
#以Unicode表示的str通過encode()方法可以編碼為指定的bytes
print('ABC'.encode('ascii')) #純英文的str可以用ASCII編碼為bytes
print('中文'.encode('utf-8')) #含有中文的str可以用UTF-8編碼為bytes
#print('中文'.encode('ascii')) #中文的str無(wú)法用ASCII編碼,因?yàn)橹形木幋a的范圍超過了ASCII編碼的范圍
#在bytes中,無(wú)法顯示為ASCII字符的字節(jié),用\x##顯示。
#把bytes變?yōu)閟tr,就需要用decode()方法:
print(b'ABC'.decode('ascii'))
print(b'\xe4\xb8\xad\xe6\x96\x87'.decode('utf-8')) #中文
#如果bytes中包含無(wú)法解碼的字節(jié),decode()方法會(huì)報(bào)錯(cuò)
#如果bytes中只有一小部分無(wú)效的字節(jié),可以傳入errors='ignore'忽略錯(cuò)誤的字節(jié)
print(b'\xe4\xb8\xad\xff'.decode('utf-8', errors='ignore'))#中
print(len(b'ABC')) #len()函數(shù)計(jì)算的是str的字符數(shù),如果換成bytes,len()函數(shù)就計(jì)算字節(jié)數(shù)
print(len('中文'.encode('utf-8')))
由于Python源代碼也是一個(gè)文本文件,所以,當(dāng)你的源代碼中包含中文的時(shí)候,在保存源代碼時(shí),就需要?jiǎng)?wù)必指定保存為UTF-8編碼。
當(dāng)Python解釋器讀取源代碼時(shí),為了讓它按UTF-8編碼讀取,我們通常在文件開頭寫上這兩行:
#!/usr/bin/env python3
# -*- coding: utf-8 -*-
第一行注釋是為了告訴Linux/OS X系統(tǒng),這是一個(gè)Python可執(zhí)行程序,Windows系統(tǒng)會(huì)忽略這個(gè)注釋;
第二行注釋是為了告訴Python解釋器,按照UTF-8編碼讀取源代碼,否則,你在源代碼中寫的中文輸出可能會(huì)有亂碼。
格式化
在Python中,采用的格式化方式和C語(yǔ)言是一致的,用%實(shí)現(xiàn),舉例如下
#%運(yùn)算符就是用來(lái)格式化字符串的
#%s表示用字符串替換,%d表示用整數(shù)替換,%x表示用16進(jìn)制整數(shù)替換,%f表示用浮點(diǎn)數(shù)替換
#有幾個(gè)%?占位符,后面就跟幾個(gè)變量或者值,順序要對(duì)應(yīng)好,如果只有一個(gè)%?,括號(hào)可以省略。
print('Hello, %s' % 'world')
print('Hi, %s, you have $%d.' % ('Michael', 1000000))
#%s永遠(yuǎn)起作用,它會(huì)把任何數(shù)據(jù)類型轉(zhuǎn)換為字符串
#用%%來(lái)表示一個(gè)普通%
format() 它會(huì)用傳入的參數(shù)依次替換字符串內(nèi)的占位符{0}、{1}……,
>>> 'Hello, {0}, 成績(jī)提升了 {1:.1f}%'.format('小明', 17.125)
'Hello, 小明, 成績(jī)提升了 17.1%'
2.3使用list和tuple
list列表
# Python內(nèi)置的一種數(shù)據(jù)類型是列表:list 是一種有序的集合,可以隨時(shí)添加和刪除其中的元素, 類似js數(shù)組
classmates = ['Michael', 123,['asp', 'php'], True] #用len()函數(shù)可以獲得list元素的個(gè)數(shù)
#要確保索引不要越界,記得最后一個(gè)元素的索引是len(classmates) - 1
print(classmates[-2])#還可以用-1做索引,直接獲取最后一個(gè)元素 以此類推
classmates.append('Adam')#往list中追加元素到末尾
classmates.insert(1, 'Jack') #把元素插入到指定的位置
classmates.pop() #刪除list末尾的元素
classmates.pop(1) #刪除指定位置的元素,用pop(i)方法,其中i是索引位置
classmates[1] = 'Sarah' #把某個(gè)元素替換成別的元素,可以直接賦值給對(duì)應(yīng)的索引位置
#如果一個(gè)list中一個(gè)元素也沒有,就是一個(gè)空的list,它的長(zhǎng)度為0
tuple 元組 tuple和list非常類似,但是tuple一旦初始化就不能修改.
classmates = ('Michael', 'Bob', 'Tracy')
#現(xiàn)在,classmates這個(gè)tuple不能變了,它也沒有append(),insert()這樣的方法。
#其他獲取元素的方法和list是一樣的,你可以正常地使用classmates[0],classmates[-1],但不能賦值成另外的元素。
#不可變的tuple有什么意義?因?yàn)閠uple不可變,所以代碼更安全。如果可能,能用tuple代替list就盡量用tuple。
#tuple的陷阱:當(dāng)你定義一個(gè)tuple時(shí),在定義的時(shí)候,tuple的元素就必須被確定下來(lái),比如:
t=(1, 2)
#如果要定義一個(gè)空的tuple,可以寫成()
t = ()
#要定義一個(gè)只有1個(gè)元素的tuple,必須加一個(gè)逗號(hào),來(lái)消除歧義:
t = (1,)
print(t)
#Python在顯示只有1個(gè)元素的tuple時(shí),也會(huì)加一個(gè)逗號(hào),以免你誤解成數(shù)學(xué)計(jì)算意義上的括號(hào)。
2.4條件判斷
#if語(yǔ)句的完整形式就是:
if <條件判斷1>:
<執(zhí)行1>
elif <條件判斷2>:
<執(zhí)行2>
elif <條件判斷3>:
<執(zhí)行3>
else:
<執(zhí)行4>
#if語(yǔ)句執(zhí)行有個(gè)特點(diǎn),它是從上往下判斷,如果在某個(gè)判斷上是True,把該判斷對(duì)應(yīng)的語(yǔ)句執(zhí)行后,就忽略掉剩下的elif和else
age = 3
if age >= 18:#注意不要少寫了冒號(hào):
print('adult')
elif age >= 6:
print('teenager')
else:
print('kid')
2.5循環(huán)
#Python的循環(huán)有兩種,一種是for...in循環(huán),依次把list或tuple中的每個(gè)元素迭代出來(lái)
names = ['Michael', 'Bob', 'Tracy']
for name in names:
print(name)
#執(zhí)行這段代碼,會(huì)依次打印names的每一個(gè)元素:
sum = 0
for x in range(101):#range(101)就可以生成0-100的整數(shù)序列
sum = sum + x
print(sum)
#第二種循環(huán)是while循環(huán),只要條件滿足,就不斷循環(huán),條件不滿足時(shí)退出循環(huán)
sum = 0
n = 99
while n > 0:
sum = sum + n
n = n - 2
if n > 10: # 當(dāng)n = 11時(shí),條件滿足,執(zhí)行break語(yǔ)句
break # break語(yǔ)句會(huì)結(jié)束當(dāng)前循環(huán)
print(n)
#在循環(huán)過程中,也可以通過continue語(yǔ)句,跳過當(dāng)前的這次循環(huán),直接開始下一次循環(huán)。
if n % 2 == 0: # 如果n是偶數(shù),執(zhí)行continue語(yǔ)句
continue # continue語(yǔ)句會(huì)直接繼續(xù)下一輪循環(huán),后續(xù)的print()語(yǔ)句不會(huì)執(zhí)行
print(sum)
2.6使用dict和set
dict字典 Python內(nèi)置了字典:dict的支持,dict全稱dictionary,在其他語(yǔ)言中也稱為map,使用鍵-值(key-value)存儲(chǔ),具有極快的查找速度。
d = {'Michael': 95, 'Bob': 75, 'Tracy': 85}
d['Adam'] = 67 #賦值
d['Adam'] #獲取 如果key不存在,dict就會(huì)報(bào)錯(cuò):
#多次對(duì)一個(gè)key放入value,后面的值會(huì)把前面的值沖掉
#要避免key不存在的錯(cuò)誤,有兩種辦法,一是通過in判斷key是否存在:
print('Adam' in d) #True/False
print(d.get('Thomas')) #如果key不存在,返回None
print(d.get('Thomas', -1)) #不存在 返回傳入的默認(rèn)值
#要?jiǎng)h除一個(gè)key,用pop(key)方法,對(duì)應(yīng)的value也會(huì)從dict中刪除
d.pop('Thomas')
#請(qǐng)務(wù)必注意,dict內(nèi)部存放的順序和key放入的順序是沒有關(guān)系的
和list比較,dict有以下幾個(gè)特點(diǎn):
查找和插入的速度極快,不會(huì)隨著key的增加而變慢;
需要占用大量的內(nèi)存,內(nèi)存浪費(fèi)多。
而list相反:
查找和插入的時(shí)間隨著元素的增加而增加;
占用空間小,浪費(fèi)內(nèi)存很少。
所以,dict是用空間來(lái)?yè)Q取時(shí)間的一種方法。
需要牢記的第一條就是dict的key必須是不可變對(duì)象。
這個(gè)通過key計(jì)算位置的算法稱為哈希算法(Hash)。
要保證hash的正確性,作為key的對(duì)象就不能變。
在Python中,字符串、整數(shù)等都是不可變的,因此,可以放心地作為key。而list是可變的,就不能作為key
set set和dict類似,也是一組key的集合,但不存儲(chǔ)value。由于key不能重復(fù),所以,在set中,沒有重復(fù)的key。
#要?jiǎng)?chuàng)建一個(gè)set,需要提供一個(gè)list作為輸入集合:重復(fù)元素在set中自動(dòng)被過濾:
s = set([1, 2, 3])
#通過add(key)方法可以添加元素到set中,可以重復(fù)添加,但不會(huì)有效果:
s.add(4)
#通過remove(key)方法可以刪除元素:
s.remove(4)
#set可以看成數(shù)學(xué)意義上的無(wú)序和無(wú)重復(fù)元素的集合,因此,兩個(gè)set可以做數(shù)學(xué)意義上的交集、并集等操作:
s1 = set([1, 2, 3])
s2 = set([2, 3, 4])
print(s1 & s2) #{2, 3}
print(s1 | s2) #{1, 2, 3, 4}
#set和dict的唯一區(qū)別僅在于沒有存儲(chǔ)對(duì)應(yīng)的value
#所以,對(duì)于不變對(duì)象來(lái)說(shuō),調(diào)用對(duì)象自身的任意方法,也不會(huì)改變?cè)搶?duì)象自身的內(nèi)容。相反,這些方法會(huì)創(chuàng)建新的對(duì)象并返回
#使用key-value存儲(chǔ)結(jié)構(gòu)的dict在Python中非常有用,選擇不可變對(duì)象作為key很重要,最常用的key是字符串
3.函數(shù)
調(diào)用函數(shù)
# 內(nèi)置函數(shù) http://docs.python.org/3/library/functions.html#abs
print(abs(-2.1)) #求絕對(duì)值的函數(shù)
#調(diào)用函數(shù)的時(shí)候,如果傳入的參數(shù)數(shù)量不對(duì),會(huì)報(bào)TypeError的錯(cuò)誤
#如果傳入的參數(shù)數(shù)量是對(duì)的,但參數(shù)類型不能被函數(shù)所接受,也會(huì)報(bào)TypeError的錯(cuò)誤
print(max(2, 3, 1, -5)) #max函數(shù)可以接收任意多個(gè)參數(shù),并返回最大的那個(gè)
#數(shù)據(jù)類型轉(zhuǎn)換
int('123') #int()函數(shù)可以把其他數(shù)據(jù)類型轉(zhuǎn)換為整數(shù)
float('12.34') #12.34
str(1.23) #'1.23'
bool(1) #True
bool('') # False
print(hex(23)=='0x17') #函數(shù)把一個(gè)整數(shù)轉(zhuǎn)換成十六進(jìn)制表示的字符串
#函數(shù)名其實(shí)就是指向一個(gè)函數(shù)對(duì)象的引用,完全可以把函數(shù)名賦給一個(gè)變量,相當(dāng)于給這個(gè)函數(shù)起了一個(gè)“別名”
a = abs # 變量a指向abs函數(shù)
a(-1) # 所以也可以通過a調(diào)用abs函數(shù)
定義函數(shù)
#定義一個(gè)函數(shù)要使用def語(yǔ)句,依次寫出函數(shù)名、括號(hào)、括號(hào)中的參數(shù)和冒號(hào):,
# 然后,在縮進(jìn)塊中編寫函數(shù)體,函數(shù)的返回值用return語(yǔ)句返回。
def my_abs(x):
if x >= 0:
return x
else:
return -x
print(my_abs(-99))
#函數(shù)體內(nèi)部的語(yǔ)句在執(zhí)行時(shí),一旦執(zhí)行到return時(shí),函數(shù)就執(zhí)行完畢
#如果沒有return語(yǔ)句,函數(shù)執(zhí)行完畢后也會(huì)返回結(jié)果,只是結(jié)果為None。return None可以簡(jiǎn)寫為return。
#函數(shù)執(zhí)行完畢也沒有return語(yǔ)句時(shí),自動(dòng)return None。
# from xxxx import my_abs來(lái)導(dǎo)入my_abs()函數(shù),注意 xxxx 是文件名(不含.py擴(kuò)展名 )
#空函數(shù) 如果想定義一個(gè)什么事也不做的空函數(shù),可以用pass語(yǔ)句:
def nop():
pass
#pass語(yǔ)句什么都不做,那有什么用?實(shí)際上pass可以用來(lái)作為占位符,比如現(xiàn)在還沒想好怎么寫函數(shù)的代碼,就可以先放一個(gè)pass,讓代碼能運(yùn)行起來(lái)。
#pass還可以用在其他語(yǔ)句里,比如:
if age >= 18:
pass
#缺少了pass,代碼運(yùn)行就會(huì)有語(yǔ)法錯(cuò)誤。
#讓我們修改一下my_abs的定義,對(duì)參數(shù)類型做檢查,只允許整數(shù)和浮點(diǎn)數(shù)類型的參數(shù)。
#數(shù)據(jù)類型檢查可以用內(nèi)置函數(shù)isinstance()實(shí)現(xiàn):
def my_abs(x):
if not isinstance(x, (int, float)):
raise TypeError('bad operand type')
if x >= 0:
return x
else:
return -x
#函數(shù)可以返回多個(gè)值嗎?答案是肯定的。
import math
def move(x, y, step, angle=0):
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)
#但其實(shí)這只是一種假象,Python函數(shù)返回的仍然是單一值:
r = move(100, 100, 60, math.pi / 6)
print(r) #(151.96152422706632, 70.0)
#原來(lái)返回值是一個(gè)tuple!但是,在語(yǔ)法上,返回一個(gè)tuple可以省略括號(hào),而多個(gè)變量可以同時(shí)接收一個(gè)tuple,按位置賦給對(duì)應(yīng)的值,
# 所以,Python的函數(shù)返回多值其實(shí)就是返回一個(gè)tuple,但寫起來(lái)更方便。
函數(shù)的參數(shù)
# Python的函數(shù)定義非常簡(jiǎn)單,除了正常定義的必選參數(shù)外,還可以使用默認(rèn)參數(shù)、可變參數(shù)和關(guān)鍵字參數(shù),
# 使得函數(shù)定義出來(lái)的接口,不但能處理復(fù)雜的參數(shù),還可以簡(jiǎn)化調(diào)用者的代碼。
def power(x, n=2):#默認(rèn)參數(shù)
s = 1
while n > 0:
n = n - 1
s = s * x
return s
默認(rèn)參數(shù)可以簡(jiǎn)化函數(shù)的調(diào)用。設(shè)置默認(rèn)參數(shù)時(shí),有幾點(diǎn)要注意:
一是必選參數(shù)在前,默認(rèn)參數(shù)在后,
二是如何設(shè)置默認(rèn)參數(shù)。
當(dāng)函數(shù)有多個(gè)參數(shù)時(shí),把變化大的參數(shù)放前面,變化小的參數(shù)放后面。變化小的參數(shù)就可以作為默認(rèn)參數(shù)。
使用默認(rèn)參數(shù)有什么好處?最大的好處是能降低調(diào)用函數(shù)的難度。
當(dāng)不按順序提供部分默認(rèn)參數(shù)時(shí),需要把參數(shù)名寫上。比如調(diào)用enroll('Adam', 'M', city='Tianjin')
#默認(rèn)參數(shù)有個(gè)最大的坑,演示如下:
def add_end(L=[]):
L.append('END')
return L
print(add_end()) # ['END']
print(add_end()) # ['END', 'END']
#很多初學(xué)者很疑惑,默認(rèn)參數(shù)是[],但是函數(shù)似乎每次都“記住了”上次添加了'END'后的list。
#定義默認(rèn)參數(shù)要牢記一點(diǎn):默認(rèn)參數(shù)必須指向不變對(duì)象!
def add_end(L=None):
if L is None:
L = []
L.append('END')
return L
#現(xiàn)在,無(wú)論調(diào)用多少次,都不會(huì)有問題:
#為什么要設(shè)計(jì)str、None這樣的不變對(duì)象呢?因?yàn)椴蛔儗?duì)象一旦創(chuàng)建,對(duì)象內(nèi)部的數(shù)據(jù)就不能修改。
#此外,由于對(duì)象不變,多任務(wù)環(huán)境下同時(shí)讀取對(duì)象不需要加鎖,同時(shí)讀一點(diǎn)問題都沒有。
可變參數(shù) 允許你傳入0個(gè)或任意個(gè)參數(shù)
def calc(*numbers): #可變參數(shù)
sum = 0
for n in numbers:
sum = sum + n * n
return sum
#如果已經(jīng)有一個(gè)list或者tuple,要調(diào)用一個(gè)可變參數(shù)怎么辦?可以這樣做:
#Python允許你在list或tuple前面加一個(gè)*號(hào),把list或tuple的元素變成可變參數(shù)傳進(jìn)去
nums = [1, 2, 3]
calc(*nums)
# *nums表示把nums這個(gè)list的所有元素作為可變參數(shù)傳進(jìn)去。這種寫法相當(dāng)有用,而且很常見。
關(guān)鍵字參數(shù)
#可變參數(shù)在函數(shù)調(diào)用時(shí)自動(dòng)組裝為一個(gè)tuple。而關(guān)鍵字參數(shù)允許你傳入0個(gè)或任意個(gè)含參數(shù)名的參數(shù),
#這些關(guān)鍵字參數(shù)在函數(shù)內(nèi)部自動(dòng)組裝為一個(gè)dict。請(qǐng)看示例:
def person(name, age, **kw):
print('name:', name, 'age:', age, 'other:', kw)
person('Michael', 30) #name: Michael age: 30 other: {}
person('Bob', 35, city='Beijing') #name: Bob age: 35 other: {'city': 'Beijing'}
person('Adam', 45, gender='M', job='Engineer') #name: Adam age: 45 other: {'gender': 'M', 'job': 'Engineer'}
#和可變參數(shù)類似,也可以先組裝出一個(gè)dict,然后,把該dict轉(zhuǎn)換為關(guān)鍵字參數(shù)傳進(jìn)去
extra = {'city': 'Beijing', 'job': 'Engineer'}
person('Jack', 24, **extra) #name: Jack age: 24 other: {'city': 'Beijing', 'job': 'Engineer'}
# **extra表示把extra這個(gè)dict的所有key-value用關(guān)鍵字參數(shù)傳入到函數(shù)的**kw參數(shù),kw將獲得一個(gè)dict,
# 注意kw獲得的dict是extra的一份拷貝,對(duì)kw的改動(dòng)不會(huì)影響到函數(shù)外的extra。
命名關(guān)鍵字參數(shù)
#仍以person()函數(shù)為例,我們希望檢查是否有city和job參數(shù):
def person(name, age, **kw):
if 'city' in kw:
# 有city參數(shù)
pass
if 'job' in kw:
# 有job參數(shù)
pass
print('name:', name, 'age:', age, 'other:', kw)
person('Jack', 24, city='Beijing', addr='Chaoyang', zipcode=123456)
#如果要限制關(guān)鍵字參數(shù)的名字,就可以用命名關(guān)鍵字參數(shù),例如,只接收city和job作為關(guān)鍵字參數(shù)。
# 這種方式定義的函數(shù)如下:
def person(name, age, *, city, job):
print(name, age, city, job)
#和關(guān)鍵字參數(shù)**kw不同,命名關(guān)鍵字參數(shù)需要一個(gè)特殊分隔符*,*后面的參數(shù)被視為命名關(guān)鍵字參數(shù)。
person('Jack', 24, city='Beijing', job='Engineer') #Jack 24 Beijing Engineer
#如果函數(shù)定義中已經(jīng)有了一個(gè)可變參數(shù),后面跟著的命名關(guān)鍵字參數(shù)就不再需要一個(gè)特殊分隔符*了:
def person(name, age, *args, city, job):
print(name, age, args, city, job)
#命名關(guān)鍵字參數(shù)必須傳入?yún)?shù)名,這和位置參數(shù)不同。如果沒有傳入?yún)?shù)名,調(diào)用將報(bào)錯(cuò):
def person(name, age, *, city='Beijing', job):
print(name, age, city, job)
#由于命名關(guān)鍵字參數(shù)city具有默認(rèn)值,調(diào)用時(shí),可不傳入city參數(shù):
person('Jack', 24, job='Engineer') #Jack 24 Beijing Engineer
#使用命名關(guān)鍵字參數(shù)時(shí),要特別注意,如果沒有可變參數(shù),就必須加一個(gè)*作為特殊分隔符。
#如果缺少*,Python解釋器將無(wú)法識(shí)別位置參數(shù)和命名關(guān)鍵字參數(shù):
參數(shù)組合
在Python中定義函數(shù),可以用必選參數(shù)、默認(rèn)參數(shù)、可變參數(shù)、關(guān)鍵字參數(shù)和命名關(guān)鍵字參數(shù),這5種參數(shù)都可以組合使用。
但是請(qǐng)注意,參數(shù)定義的順序必須是:必選參數(shù)、默認(rèn)參數(shù)、可變參數(shù)、命名關(guān)鍵字參數(shù)和關(guān)鍵字參數(shù)。
def f1(a, b, c=0, *args, **kw):
print('a =', a, 'b =', b, 'c =', c, 'args =', args, 'kw =', kw)
def f2(a, b, c=0, *, d, **kw):
print('a =', a, 'b =', b, 'c =', c, 'd =', d, 'kw =', kw)
f1(1, 2) #a = 1 b = 2 c = 0 args = () kw = {}
f1(1, 2, c=3) #a = 1 b = 2 c = 3 args = () kw = {}
f1(1, 2, 3, 'a', 'b') #a = 1 b = 2 c = 3 args = ('a', 'b') kw = {}
f1(1, 2, 3, 'a', 'b', x=99)#a = 1 b = 2 c = 3 args = ('a', 'b') kw = {'x': 99}
f2(1, 2, d=99, ext=None) #a = 1 b = 2 c = 0 d = 99 kw = {'ext': None}
#最神奇的是通過一個(gè)tuple和dict,你也可以調(diào)用上述函數(shù):
args = (1, 2, 3, 4)
kw = {'d': 99, 'x': '#'}
f1(*args, **kw) #a = 1 b = 2 c = 3 args = (4,) kw = {'d': 99, 'x': '#'}
#對(duì)于任意函數(shù),都可以通過類似func(*args, **kw)的形式調(diào)用它,無(wú)論它的參數(shù)是如何定義的。
默認(rèn)參數(shù)一定要用不可變對(duì)象,如果是可變對(duì)象,程序運(yùn)行時(shí)會(huì)有邏輯錯(cuò)誤!
要注意定義可變參數(shù)和關(guān)鍵字參數(shù)的語(yǔ)法:
*args是可變參數(shù),args接收的是一個(gè)tuple;
**kw是關(guān)鍵字參數(shù),kw接收的是一個(gè)dict。
可變參數(shù)既可以直接傳入:func(1, 2, 3),又可以先組裝list或tuple,再通過args傳入:func((1, 2, 3));
關(guān)鍵字參數(shù)既可以直接傳入:func(a=1, b=2),又可以先組裝dict,再通過kw傳入:func({'a': 1, 'b': 2})。
使用args和kw是Python的習(xí)慣寫法,當(dāng)然也可以用其他參數(shù)名,但最好使用習(xí)慣用法。
命名的關(guān)鍵字參數(shù)是為了限制調(diào)用者可以傳入的參數(shù)名,同時(shí)可以提供默認(rèn)值。
定義命名的關(guān)鍵字參數(shù)在沒有可變參數(shù)的情況下不要忘了寫分隔符,否則定義的將是位置參數(shù)。
遞歸函數(shù)
在函數(shù)內(nèi)部,可以調(diào)用其他函數(shù)。如果一個(gè)函數(shù)在內(nèi)部調(diào)用自身本身,這個(gè)函數(shù)就是遞歸函數(shù)。
def fact(n):
if n==1:
return 1
return n * fact(n - 1)
遞歸函數(shù)的優(yōu)點(diǎn)是定義簡(jiǎn)單,邏輯清晰。理論上,所有的遞歸函數(shù)都可以寫成循環(huán)的方式,但循環(huán)的邏輯不如遞歸清晰。