我們在編寫代碼時酌住,如果要調(diào)用別人寫的函數(shù)偶房,一般我們不需要關(guān)心這些函數(shù)是如何實現(xiàn)的听绳,只需要關(guān)注如何使用這個函數(shù)。
那么怎么才能知道如何使用呢邀层?也就是我們必須要知道這個函數(shù)需要哪些參數(shù)返敬,參數(shù)都代表什么,哪些參數(shù)是必須寫的寥院,哪些是不用管的劲赠,最后關(guān)注其返回的結(jié)果如何,這樣就能正確的使用該函數(shù)秸谢。
比如我們前面講過的range函數(shù):
range(10)
range(1,10)
range(1, 10, 2)
我們看在傳參數(shù)的時候凛澎,數(shù)量不同會得到不同的結(jié)果。其實這就是 Python 在函數(shù)定義的時候提供的各種參數(shù)形式估蹄。這種形式非常靈活塑煎,且功能強大。
在 Python 中臭蚁,有多種參數(shù)形式最铁,分別是位置參數(shù)(必選參數(shù)),默認(rèn)參數(shù)垮兑,可變參數(shù)和關(guān)鍵字參數(shù)(不同的教程叫法不太一樣)冷尉。
位置參數(shù)
也叫做必選參數(shù),也就是說在函數(shù)定義的時候聲明的參數(shù)系枪,在調(diào)用的時候就必須全部給予實參雀哨,并且必須按照參數(shù)定義的順序賦值。
比如下例嗤无,我們都知道求圓的面積和周長必須要用到 半徑 r 和圓周率 π 震束,現(xiàn)在實現(xiàn)一個函數(shù),提供半徑 r 和 圓周率 π当犯,返回面積(πr
2)和周長(2πr
)
def circle(r, pi):
area = pi * r ** 2
perimeter = 2 * pi * r
return area, perimeter
當(dāng)我們調(diào)用 circle() 函數(shù)的時候垢村,必須同時提供 r 和 π 兩個參數(shù),否則就會報錯嚎卫,當(dāng)然多了也不行嘉栓。
>>> circle(4)
TypeError: circle() missing 1 required positional argument: 'pi'
也就是說參數(shù)不能多也不能少,必須按照函數(shù)定義的參數(shù)個數(shù)依次賦值拓诸,才能正常調(diào)用該函數(shù)侵佃。
>>> circle(4, 3.14)
(50.24, 25.12)
如果順序反了就得不到你想到的結(jié)果了!
>>> circle(3.14, 4)
(39.4384, 25.12)
如果你非要反著用(不按順序),請使用 參數(shù)名=參數(shù)值
的形式奠支。
>>> circle(pi=3.14, r=4)
(50.24, 25.12)
對于沒有任何標(biāo)識的參數(shù)馋辈,我們就稱之為 位置參數(shù)
默認(rèn)參數(shù)
剛才的 circle() 函數(shù),我們在用的時候必須同時傳入 r 和 π 兩個參數(shù)倍谜。但是我們發(fā)現(xiàn) π 也就是圓周率迈螟,一般都是固定的用兩位小數(shù) 3.14。
那么我們就沒必要每次調(diào)用這個函數(shù)都要輸這個 π尔崔,畢竟圓周率不是誰想改就能改的答毫。但是偶爾我們需要使用小數(shù)點后面 4 位或者 6 位以求得更精確的面積和周長。那么如何實現(xiàn)呢季春?
對于函數(shù)的參數(shù)洗搂,絕大部分情況使用固定的值,在少數(shù)情況需要改該參數(shù)的值载弄,那么我們可以設(shè)置為默認(rèn)參數(shù)
簡而言之耘拇,就是給參數(shù)賦一個默認(rèn)值。這樣當(dāng)我們沒有特殊需要的時候侦锯,該參數(shù)直接用默認(rèn)值驼鞭,而當(dāng)你有特殊需要的時候,又可以通過提供新的值來實現(xiàn)你的需求尺碰。而且當(dāng)你沒有特殊需要的時候挣棕,還可以少傳一個參數(shù),使用起來更方便亲桥。
我們來改造一下上面的 circle() 函數(shù):
def circle(r, pi=3.14): # 給參數(shù)賦一個默認(rèn)值即可
area = pi * r ** 2
perimeter = 2 * pi * r
return area, perimeter
那么我們調(diào)用改造后的函數(shù):
# 沒有特殊需要的時候
>>> circle(4)
(50.24, 25.12)
# 精度要求更高的時候
>>> circle(4, 3.14159265358)
(50.26548245728, 25.13274122864)
是不是很方便洛心!
靈活使用默認(rèn)參數(shù),會大大增加函數(shù)調(diào)用者的體驗 :】
可變參數(shù)
參數(shù)收集
在某些情況下题篷,我們需要輸入不確定數(shù)量的參數(shù)(其實一般初級編程用不上)词身,那么根據(jù)上面我們說的參數(shù)必須按照順序和個數(shù)傳入,那么這種情況要怎么處理呢番枚?
在 Python 中提供了參數(shù)收集的機制法严,在函數(shù)定義時损敷,指定可變參數(shù)(參數(shù)收集),在函數(shù)調(diào)用的時候就可以提供任意數(shù)量的參數(shù)深啤。
比如拗馒,我們要寫一個函數(shù),用于保存任意多的用戶姓名:
def collect_names(*name):
print('收集到的姓名:', name)
沒錯溯街,就是在參數(shù)前面加個星號*
诱桂,在函數(shù)內(nèi)部使用這個參數(shù)即可。我們看看結(jié)果是什么呈昔?
>>> collect_names('nemo', 'nano', 'tato', 'jim', 'kid') # 你想傳多少都可以挥等!
收集到的姓名: ('nemo', 'nano', 'tato', 'jim', 'kid')
從結(jié)果我們可以看出,通過參數(shù)收集到的任意參數(shù)堤尾,傳入函數(shù)內(nèi)部就變成了一個元組肝劲,元組嘛,當(dāng)然可以存任意多的數(shù)據(jù)了郭宝。
當(dāng)然收集到元組如何用涡相,就看你的需求了!
除了上面的直接收集參數(shù)的形式剩蟀,還有一種收集 參數(shù)名=參數(shù)值
的傳參形式催蝗,而通過這種方式收集到的參數(shù)在內(nèi)部構(gòu)成一個字典。
如下例育特,寫一個函數(shù)丙号,收集一個人的信息。
def person(**attr):
print('用戶的信息:', attr)
調(diào)用的結(jié)果:
>>> person(name='nemo', age=18, sex='男', height=180)
用戶的信息: {'name': 'nemo', 'age': 18, 'sex': '男', 'height': 180}
收集到內(nèi)部形成一個字典缰冤,然后你要操作就隨你咯犬缨。比如:
def person(**attr):
for k in attr.keys():
print('用戶的%s信息:%s' % (k, attr.get(k)))
>>> person(name='nemo', age=18, sex='男', height=180)
用戶的name信息:nemo
用戶的age信息:18
用戶的sex信息:男
用戶的height信息:180
也就是說你收集到了用戶的信息后,根據(jù)你的需要從字典中提取數(shù)據(jù)并處理棉浸。
參數(shù)收集的逆向過程
其實上面講的參數(shù)收集怀薛,在你日常編碼過程中使用相對較少,反倒其逆向過程用得更多迷郑。
那什么是參數(shù)收集的逆向過程呢枝恋?
參數(shù)收集是在函數(shù)調(diào)用時,傳入任意多的參數(shù)嗡害,在函數(shù)內(nèi)部形式一個元組或字典焚碌。而逆向過程,是先有一個元組(或任意序列)或字典霸妹,在函數(shù)調(diào)用時傳入該序列或字典十电。
那么這有何用處呢?可以簡化函數(shù)調(diào)用,并增加函數(shù)的可讀性鹃骂。
首先我們看第一種參數(shù)收集的逆向過程(元組):
比如用上面的 circle 函數(shù)的例子台盯,我們在一個數(shù)據(jù)文件中存儲了多個需要求面積和周長的數(shù)據(jù)。然后提取出來后放在一個二維的元組中畏线,我們需要調(diào)用該函數(shù)逐一運算爷恳。代碼如下:
data = ((4, 3.14), (6, 3.1415), (11, 3.14))
for i in data:
circle(i[0], i[1])
好在我們這里只有兩個參數(shù),如果參數(shù)更多一些象踊,比如5個6個,那么你要怎么寫 fun(i[0], i[1], i[2]....)
編不下去了棚壁。杯矩。。急需一個更簡單方便的方式袖外,正好史隆。。曼验。
data = ((4, 3.14), (6, 3.1415), (11, 3.14))
for i in data:
circle(*i) # 在變量前加一個星號就搞定啦
我們來看一下泌射,當(dāng) i 的值為(4, 3.14),那么這是一個元組鬓照,用星號能起什么作用呢熔酷?星號相當(dāng)于拆解,把元組拆解成單個的元素豺裆,再分別按順序賦值給 circle() 函數(shù)的各個參數(shù)拒秘。circle(*i) 等同于 circle(4, 3.14)。
同樣字典形式的參數(shù)收集也可以逆向來用臭猜。這個就更有用啦躺酒。比如我們連接 mysql 數(shù)據(jù)的時候(這里我只舉函數(shù)調(diào)用的例子,數(shù)據(jù)庫的使用在 Python 基礎(chǔ)學(xué)完后再深究)蔑歌,使用到的函數(shù)有很多的參數(shù):
db = connect(host=127.0.0.1, port=3306, user='root', password='123456', db='happy') # 需要的話還有參數(shù)需要賦值
那么對于這種情況羹应,我們可以寫一個字典作為數(shù)據(jù)庫連接的配置:
db_config = {
'host': '127.0.0.1',
'port': 3306,
'user': 'root',
'password': '123456',
'db': 'happy'
}
db = connect(**db_config)
這里兩個星號的作用,把字典拆解為單個的 參數(shù)名=參數(shù)值
的形式分別賦值給函數(shù)的各個參數(shù)次屠。
雖然代碼看起來更多一些园匹,但是可讀性更強,維護起來也更容易些劫灶,可以把這個字典放在配置文件中維護偎肃,其他需要用到的地方直接引入這個字典即可。
關(guān)鍵字參數(shù)
最后還剩一個關(guān)鍵字參數(shù)浑此,關(guān)鍵字參數(shù)在函數(shù)定義時是一種參數(shù)的限制條件累颂。
定義為關(guān)鍵字參數(shù)的參數(shù),在調(diào)用時,必須以
參數(shù)名=參數(shù)值
的形式賦值紊馏,不能直接寫參數(shù)值料饥。
def person(name, age, *, sex, height):
print('用戶的姓名為:', name)
print('用戶的年齡為:', age)
print('用戶的性別為:', sex)
print('用戶的身高為:', height)
上面例子中函數(shù)參數(shù)中的星號看到了么?星號后面的參數(shù) sex
和 height
就是關(guān)鍵字參數(shù)了朱监。
注意星號不用賦值也不算一個參數(shù)岸啡,星號屬于一個標(biāo)識用來區(qū)分普通參數(shù)和關(guān)鍵字參數(shù)。
在函數(shù)調(diào)用時赫编,sex 和 height 兩個關(guān)鍵字參數(shù)巡蘸,必須以 sex='男' 和 height=180 這種形式賦值,否則就會報錯擂送。
>>> person('nemo', 18, '男', 180) # 提示只需要兩個位置參數(shù)悦荒,但是給了4個
TypeError: person() takes 2 positional arguments but 4 were given
>>> person('nemo', 18) # 提示缺少兩個關(guān)鍵字參數(shù)
TypeError: person() missing 2 required keyword-only arguments: 'sex' and 'height'
>>> person('nemo', 18, sex='男', height=180) # 只有這樣才能正確調(diào)用
用戶的姓名為: nemo
用戶的年齡為: 18
用戶的性別為: 男
用戶的身高為: 180