新手上路必学的Python函数基础知识,全在这里了(多段代码举例)
導(dǎo)讀:函數(shù)是Python中最重要、最基礎(chǔ)的代碼組織和代碼復(fù)用方式。根據(jù)經(jīng)驗,如果你需要多次重復(fù)相同或類似的代碼,就非常值得寫一個可復(fù)用的函數(shù)。通過給一組Python語句一個函數(shù)名,形成的函數(shù)可以幫助你的代碼更加可讀。
函數(shù)聲明時使用def關(guān)鍵字,返回時使用return關(guān)鍵字:
def?my_function(x,?y,?z=1.5):??
if?z?>?1:??
????return?z?*?(x?+?y)??
else:??
????return?z?/?(x?+?y)??
有多條返回語句是沒有問題的。如果Python達(dá)到函數(shù)的尾部時仍然沒有遇到return語句,就會自動返回None。
每個函數(shù)都可以有位置參數(shù)和關(guān)鍵字參數(shù)。關(guān)鍵字參數(shù)最常用于指定默認(rèn)值或可選參數(shù)。在前面的函數(shù)中,x和y是位置參數(shù),z是關(guān)鍵字參數(shù)。這意味著函數(shù)可以通過以下任意一種方式進行調(diào)用:
my_function(5,?6,?z=0.7)??
my_function(3.14,?7,?3.5)??
my_function(10,?20)??
函數(shù)參數(shù)的主要限制是關(guān)鍵字參數(shù)必須跟在位置參數(shù)后(如果有的話)。你可以按照任意順序指定關(guān)鍵字參數(shù);這可以讓你不必強行記住函數(shù)參數(shù)的順序,而只需用參數(shù)名指定。
也可以使用關(guān)鍵字參數(shù)向位置參數(shù)傳參。在前面的例子中,我們也可以這樣寫:
my_function(x=5,?y=6,?z=7)??
my_function(y=6,?x=5,?z=7)??
在部分場景中,這樣做有助于代碼可讀性
01 命名空間、作用域和本地函數(shù)
函數(shù)有兩種連接變量的方式:全局、本地。在Python中另一種更貼切地描述變量作用域的名稱是命名空間。在函數(shù)內(nèi)部,任意變量都是默認(rèn)分配到本地命名空間的。本地命名空間是在函數(shù)被調(diào)用時生成的,并立即由函數(shù)的參數(shù)填充。當(dāng)函數(shù)執(zhí)行結(jié)束后,本地命名空間就會被銷毀(除了一些特殊情況)。考慮以下函數(shù):
def?func():??
????a?=?[]??
????for?i?in?range(5):??
????????a.append(i)??
當(dāng)func()調(diào)用時,空的列表會被創(chuàng)建,五個元素被添加到列表,之后a會在函數(shù)退出時被銷毀。假設(shè)我們像下面這樣聲明a:
a?=?[]??
def?func():??
????for?i?in?range(5):??
????????a.append(i)??
在函數(shù)外部給變量賦值是可以的,但是那變量必須使用global關(guān)鍵字聲明為全局變量:
In?[168]:?a?=?None??
In?[169]:?def?bind_a_variable():??
.....:????????global?a??
.....:????????a?=?[]??
.....:????????bind_a_variable()??
.....:??
In?[170]:?print(a)??
[]??
我簡單的講下global關(guān)鍵字的用法。通常全局變量用來存儲系統(tǒng)中的某些狀態(tài)。如果你發(fā)現(xiàn)你大量使用了全局變量,可能表明你需要面向?qū)ο缶幊?使用類)
02 返回多個值
當(dāng)我在使用Java和C++編程后第一次使用Python編程時,我最喜歡的特性就是使用簡單語法就可以從函數(shù)中返回多個值。以下是代碼:
def?f():??
a?=?5??
b?=?6??
c?=?7??
return?a,?b,?c??
a,?b,?c?=?f()??
在數(shù)據(jù)分析和其他科研應(yīng)用,你可能會發(fā)現(xiàn)經(jīng)常需要返回多個值。這里實質(zhì)上是返回了一個對象,也就是元組,而元組之后又被拆包為多個結(jié)果變量。在前面的例子中,我們可以用下面的代碼代替:
return_value?=?f()??
在這個例子中,return_value是一個3個元素的元組。像之前那樣一次返回多個值還有一種潛在的、更有吸引力的實現(xiàn):
def?f():??
a?=?5??
b?=?6??
c?=?7??
return?{'a'?:?a,?'b'?:?b,?'c'?:?c}??
具體用哪種技術(shù)取決于你需要做什么的事。
03 函數(shù)是對象
由于Python的函數(shù)是對象,很多在其他語言中比較難的構(gòu)造在Python中非常容易實現(xiàn)。假設(shè)我們正在做數(shù)據(jù)清洗,需要將一些變形應(yīng)用到下列字符串列表中:
In?[171]:?states?=?['?Alabama?',?'Georgia!',?'Georgia',?'georgia',?'FlOrIda',??
.....:?'south?carolina##',?'West?virginia?']??
任何處理過用戶提交數(shù)據(jù)的人都對這樣的數(shù)據(jù)感到凌亂。為了使這些數(shù)據(jù)整齊、可用于分析,有很多是事情需要去做:去除空格、移除標(biāo)點符號、調(diào)整適當(dāng)?shù)拇笮憽R环N方式是使用內(nèi)建的字符串方法,結(jié)合標(biāo)準(zhǔn)庫中的正則表達(dá)式模塊re:
import?re??
def?clean_strings(strings):??
????result?=?[]??
????for?value?in?strings:??
????????value?=?value.strip()??
????????value?=?re.sub('[!#?]',?'',?value)??
????????value?=?value.title()??
????????result.append(value)??
????return?result??
結(jié)果如下:
In?[173]:?clean_strings(states)??
Out[173]:??
['Alabama',??
'Georgia',??
'Georgia',??
'Georgia',??
'Florida',??
'South?Carolina',??
'West?Virginia']??
另一種會讓你覺得有用的實現(xiàn)就是將特定的列表操作應(yīng)用到某個字符串的集合上:
def?remove_punctuation(value):??
return?re.sub('[!#?]',?'',?value)??
clean_ops?=?[str.strip,?remove_punctuation,?str.title]??
def?clean_strings(strings,?ops):??
????result?=?[]??
????for?value?in?strings:??
????????for?function?in?ops:??
????????????value?=?function(value)??
????????result.append(value)??
????return?result?
結(jié)果如下:
In?[175]:?clean_strings(states,?clean_ops)??
Out[175]:??
['Alabama',??
'Georgia',??
'Georgia',??
'Georgia',??
'Florida',??
'South?Carolina',??
'West?Virginia']??
像這種更為函數(shù)化的模式可以使你在更高層次上方便地修改字符串變換方法。clean_strings函數(shù)現(xiàn)在也具有更強的復(fù)用性、通用性。
你可以將函數(shù)作為一個參數(shù)傳給其他的函數(shù),比如內(nèi)建的map函數(shù),可以將一個函數(shù)應(yīng)用到一個序列上:
In?[176]:?for?x?in?map(remove_punctuation,?states):??
.....:????????print(x)??
Alabama??
Georgia??
Georgia??
georgia??
FlOrIda??
south?carolina??
West?virginia??
04 匿名(Lambda)函數(shù)
Python支持所謂的匿名或lambda函數(shù)。匿名函數(shù)是一種通過單個語句生成函數(shù)的方式,其結(jié)果是返回值。匿名函數(shù)使用lambda關(guān)鍵字定義,該關(guān)鍵字僅表達(dá)“我們聲明一個匿名函數(shù)”的意思:
def?short_function(x):??
????return?x?*?2??
equiv_anon?=?lambda?x:?x?*?2??
匿名函數(shù)在數(shù)據(jù)分析中非常方便,因為在很多案例中數(shù)據(jù)變形函數(shù)都可以作為函數(shù)的參數(shù)。匿名函數(shù)代碼量小(也更為清晰),將它作為參數(shù)進行傳值,比寫一個完整的函數(shù)或者將匿名函數(shù)賦值給局部變量更好。舉個例子,考慮下面的不佳示例:
def?apply_to_list(some_list,?f):??
????return?[f(x)?for?x?in?some_list]??
ints?=?[4,?0,?1,?5,?6]??
apply_to_list(ints,?lambda?x:?x?*?2)??
你也可以寫成[x * 2 for x in ints] ,但是在這里我們能夠簡單地將一個自定義操作符傳遞給apply_to_list函數(shù)。
另一個例子,假設(shè)你想要根據(jù)字符串中不同字母的數(shù)量對一個字符串集合進行排序:
In?[177]:?strings?=?['foo',?'card',?'bar',?'aaaa',?'abab']??
這里我們可以將一個匿名函數(shù)傳給列表的sort方法:
In?[178]:?strings.sort(key=lambda?x:?len(set(list(x))))??
In?[179]:?strings??
Out[179]:?['aaaa',?'foo',?'abab',?'bar',?'card']??
和def關(guān)鍵字聲明的函數(shù)不同,匿名函數(shù)對象自身并沒有一個顯式的__name__ 屬性,這是lambda函數(shù)被稱為匿名函數(shù)的一個原因。
05 柯里化:部分函數(shù)應(yīng)用
柯里化是計算機科學(xué)術(shù)語(以數(shù)學(xué)家Haskell Curry命名),它表示通過部分參數(shù)應(yīng)用的方式從已有的函數(shù)中衍生出新的函數(shù)。例如,假設(shè)我們有一個不重要的函數(shù),其功能是將兩個數(shù)加一起:
def?add_numbers(x,?y):??
return?x?+?y??
使用這個函數(shù),我們可以衍生出一個只有一個變量的新函數(shù),add_five,可以給參數(shù)加上5:
add_five?=?lambda?y:?add_numbers(5,?y)??
第二個參數(shù)對于函數(shù)add_numers就是柯里化了。這里并沒有什么神奇的地方,我們真正做的事只是定義了一個新函數(shù),這個新函數(shù)調(diào)用了已經(jīng)存在的函數(shù)。內(nèi)建的functools模塊可以使用pratial函數(shù)簡化這種處理:
from?functools?import?partial??
add_five?=?partial(add_numbers,?5)??
06 生成器
通過一致的方式遍歷序列,例如列表中的對象或者文件中的一行行內(nèi)容,這是Python的一個重要特性。這個特性是通過迭代器協(xié)議來實現(xiàn)的,迭代器協(xié)議是一種令對象可遍歷的通用方式。例如,遍歷一個字典,獲得字典的鍵:
In?[180]:?some_dict?=?{'a':?1,?'b':?2,?'c':?3}??
In?[181]:?for?key?in?some_dict:??
.....:????????print(key)??
a???
b??
c??
當(dāng)你寫下for key in some_dict 的語句時,Python解釋器首先嘗試根據(jù)some_dict生成一個迭代器:
In?[182]:?dict_iterator?=?iter(some_dict)??
In?[183]:?dict_iterator??
Out[183]:?<dict_keyiterator?at?0x7fbbd5a9f908>??
迭代器就是一種用于在上下文中(比如for循環(huán))向Python解釋器生成對象的對象。大部分以列表或列表型對象為參數(shù)的方法都可以接收任意的迭代器對象。包括內(nèi)建方法比如min、max和sum,以及類型構(gòu)造函數(shù)比如list和tuple:
In?[184]:?list(dict_iterator)??
Out[184]:?['a',?'b',?'c']??
用迭代器構(gòu)造新的可遍歷對象是一種非常簡潔的方式。普通函數(shù)執(zhí)行并一次返回單個結(jié)果,而生成器則“惰性”地返回一個多結(jié)果序列,在每一個元素產(chǎn)生之后暫停,直到下一個請求。如需創(chuàng)建一個生成器,只需要在函數(shù)中將返回關(guān)鍵字return替換為yield關(guān)鍵字:
def?squares(n=10):??
print('Generating?squares?from?1?to?{0}'.format(n?**?2))??
for?i?in?range(1,?n?+?1):??
????yield?i?**?2??
當(dāng)你實際調(diào)用生成器時,代碼并不會立即執(zhí)行:
In?[186]:?gen?=?squares()??
In?[187]:?gen??
Out[187]:?<generator?object?squares?at?0x7fbbd5ab4570>??
直到你請求生成器中的元素時,它才會執(zhí)行它的代碼:
In?[188]:?for?x?in?gen:??
.....:?print(x,?end='?')??
Generating?squares?from?1?to?100??
1?4?9?16?25?36?49?64?81?100??
1. 生成器表達(dá)式
生成器表達(dá)式來創(chuàng)建生成器更為簡單。生成器表達(dá)式與列表、字典、集合的推導(dǎo)式很類似,創(chuàng)建一個生成器表達(dá)式,只需要將列表推導(dǎo)式的中括號替換為小括號即可:
In?[189]:?gen?=?(x?**?2?for?x?in?range(100))??
In?[190]:?gen??
Out[190]:?<generator?object?<genexpr>?at?0x7fbbd5ab29e8>??
上面的代碼與下面更為復(fù)雜的生成器是等價的:
def?_make_gen():??
for?x?in?range(100):??
????yield?x?**?2??
gen?=?_make_gen()??
在很多情況下,生成器表達(dá)式可以作為函數(shù)參數(shù)用于替代列表推導(dǎo)式:
In?[191]:?sum(x?**?2?for?x?in?range(100))??
Out[191]:?328350??
In?[192]:?dict((i,?i?**2)?for?i?in?range(5))??
Out[192]:?{0:?0,?1:?1,?2:?4,?3:?9,?4:?16}??
2. itertools模塊
標(biāo)準(zhǔn)庫中的itertools模塊是適用于大多數(shù)數(shù)據(jù)算法的生成器集合。例如,groupby可以根據(jù)任意的序列和一個函數(shù),通過函數(shù)的返回值對序列中連續(xù)的元素進行分組,參見下面的例子:
In?[193]:?import?itertools??
In?[194]:?first_letter?=?lambda?x:?x[0]??
In?[195]:?names?=?['Alan',?'Adam',?'Wes',?'Will',?'Albert',?'Steven']??
In?[196]:?for?letter,?names?in?itertools.groupby(names,?first_letter):??
.....:????????print(letter,?list(names))?#?names是一個生成器??
A?['Alan',?'Adam']??
W?['Wes',?'Will']??
A?['Albert']??
S?['Steven']??
下表是一些我認(rèn)為經(jīng)常用到的itertools函數(shù)的列表。你可以通過查詢Python官方文檔來獲得更多關(guān)于內(nèi)建工具庫的信息。
函數(shù) | 描述 |
combinations(iterable, ?k) | 根據(jù)iterable參數(shù)中的所有元素生成一個包含所有可能K元組的序列,忽略元素的順序,也不進行替代(需要替代請參考函數(shù) combinations_with_replacement ) |
permutations(iterable, ?k) | 根據(jù)itrable參數(shù)中的按順序生成包含所有可能K元組的序列 |
groupby(iterable[, ?keyfunc]) | 根據(jù)每一個獨一的Key生成 (key, sub-iterator) 元組 |
product(*iterables, ?repeat=1) | 以元組的形式,根據(jù)輸入的可遍歷對象們生成笛卡爾積,與嵌套的for循環(huán)類似 |
07 錯誤和異常處理
優(yōu)雅地處理Python的錯誤或異常是構(gòu)建穩(wěn)定程序的重要組成部分。在數(shù)據(jù)分析應(yīng)用中,很多函數(shù)只能處理特定的輸入。例如,Python的float函數(shù)可以將字符串轉(zhuǎn)換為浮點數(shù)字,但是對不正確的輸入會產(chǎn)生ValueError:
In?[197]:?float('1.2345')??
Out[197]:?1.2345??
In?[198]:?float('something')??
---------------------------------------------------------------------------??
ValueError?Traceback?(most?recent?call?last)??
<ipython-input-198-439904410854>?in?<module>()??
---->?1?float('something')??
ValueError:?could?not?convert?string?to?float:?'something'??
假設(shè)我們想要在float函數(shù)運行失敗時可以優(yōu)雅地返回輸入?yún)?shù)。我們可以通過將float函數(shù)寫入一個try/except代碼段來實現(xiàn):
def?attempt_float(x):??
try:??
????return?float(x)??
except:??
????return?x??
如果float(x)執(zhí)行時拋出了異常,則代碼段中的except部分代碼將會被執(zhí)行:
In?[200]:?attempt_float('1.2345')??
Out[200]:?1.2345??
In?[201]:?attempt_float('something')??
Out[201]:?'something??
你可能會注意到,除了ValueError,float函數(shù)還會拋出其他的異常:
In?[202]:?float((1,?2))??
---------------------------------------------------------------------------??
TypeError?Traceback?(most?recent?call?last)??
<ipython-input-202-842079ebb635>?in?<module>()??
---->?1?float((1,?2))??
TypeError:?float()?argument?must?be?a?string?or?a?number,?not?'tuple'??
你可能只想處理ValueError,因為TypeError(輸入的不是字符串或數(shù)值)可能表明你的程序中有個合乎語法的錯誤。為了實現(xiàn)這個目的,在except后面寫下異常類型:
def?attempt_float(x):??
try:??
????return?float(x)??
except?ValueError:??
????return?x??
然后我們可以得到:
In?[204]:?attempt_float((1,?2))??
---------------------------------------------------------------------------??
TypeError?Traceback?(most?recent?call?last)??
<ipython-input-204-9bdfd730cead>?in?<module>()??
---->?1?attempt_float((1,?2))??
<ipython-input-203-3e06b8379b6b>?in?attempt_float(x)??
1?def?attempt_float(x):??
2?try:??
---->?3?return?float(x)??
4?except?ValueError:??
5?return?x??
TypeError:?float()?argument?must?be?a?string?or?a?number,?not?'tuple'??
你可以通過將多個異常類型寫成元組的方式同事捕獲多個異常(小括號是必不可少的):
def?attempt_float(x):??
try:??
????return?float(x)??
except?(TypeError,?ValueError):??
????return?x??
某些情況下,你可能想要處理一個異常,但是你希望一部分代碼無論try代碼塊是否報錯都要執(zhí)行。為了實現(xiàn)這個目的,使用finally關(guān)鍵字:
f?=?open(path,?'w')??
try:??
????write_to_file(f)??
finally:??
????f.close()??
這樣,我們可以讓f在程序結(jié)束后總是關(guān)閉。類似的,你可以使用else來執(zhí)行當(dāng)try代碼塊成功執(zhí)行時才會執(zhí)行的代碼:
f?=?open(path,?'w')??
try:??
????write_to_file(f)??
except:??
????print('Failed')??
else:??
????print('Succeeded')??
finally:??
????f.close()??
IPython中的異常
如果當(dāng)你正在%run一個腳本或執(zhí)行任何語句報錯時,IPython將會默認(rèn)打印出完整的調(diào)用堆棧跟蹤(報錯追溯),會將堆棧中每個錯誤點附近的幾行上下文代碼打印出:
In?[10]:?%run?examples/ipython_bug.py??
---------------------------------------------------------------------------??
AssertionError?Traceback?(most?recent?call?last)??
/home/wesm/code/pydata-book/examples/ipython_bug.py?in?<module>()??
13?throws_an_exception()??
14??
--->?15?calling_things()??
/home/wesm/code/pydata-book/examples/ipython_bug.py?in?calling_things()??
11?def?calling_things():??
12?works_fine()??
--->?13?throws_an_exception()??
14??
15?calling_things()??
/home/wesm/code/pydata-book/examples/ipython_bug.py?in?throws_an_exception()??
7?a?=?5??
8?b?=?6??
---->?9?assert(a?+?b?==?10)??
10??
11?def?calling_things():??
AssertionError:??
比標(biāo)準(zhǔn)Python解釋器提供更多額外的上下文是IPython的一大進步(標(biāo)準(zhǔn)Python解釋器不提供任何額外的上下文)。你可以使用%xmode命令來控制上下文的數(shù)量,可以從Plain(普通)模式(與標(biāo)準(zhǔn)Python解釋器一致)切換到Verbose(復(fù)雜)模式(可以顯示函數(shù)的參數(shù)值以及更多有用信息)。
關(guān)于作者:韋斯·麥金尼(Wes McKinney)是流行的Python開源數(shù)據(jù)分析庫pandas的創(chuàng)始人。他是一名活躍的演講者,也是Python數(shù)據(jù)社區(qū)和Apache軟件基金會的Python/C++開源開發(fā)者。目前他在紐約從事軟件架構(gòu)師工作。
本文摘編自《利用Python進行數(shù)據(jù)分析》(原書第2版),經(jīng)出版方授權(quán)發(fā)布。
延伸閱讀《利用Python進行數(shù)據(jù)分析》
轉(zhuǎn)載請聯(lián)系微信:togo-maruko
點擊文末右下角“寫留言”發(fā)表你的觀點
推薦語:適合剛學(xué)Python的數(shù)據(jù)分析師或剛學(xué)數(shù)據(jù)科學(xué)以及科學(xué)計算的Python編程者。閱讀本書可以獲得一份關(guān)于在Python下操作、處理、清洗、規(guī)整數(shù)據(jù)集的完整說明。
更多精彩
在公眾號后臺對話框輸入以下關(guān)鍵詞
查看更多優(yōu)質(zhì)內(nèi)容!
PPT?|?報告?|?讀書?|?書單?|?干貨
Python?|?機器學(xué)習(xí)?|?深度學(xué)習(xí)?|?神經(jīng)網(wǎng)絡(luò)
區(qū)塊鏈?|?揭秘?|?高考?|?福利
猜你想看
pandas創(chuàng)始人手把手教你利用Python進行數(shù)據(jù)分析(思維導(dǎo)圖)
百度吹過的牛實現(xiàn)了,你的呢?5本書帶你搞定AI前沿技術(shù)
程序猿們,如果你不想跑偏,就千萬別讀這4本書!
20位大佬組團“踢館”,誰超越了圖靈?
Q:?你都會用到哪些Python的函數(shù)?
歡迎留言與大家分享
覺得不錯,請把這篇文章分享給你的朋友
轉(zhuǎn)載 / 投稿請聯(lián)系:baiyu@hzbook.com
更多精彩,請在后臺點擊“歷史文章”查看
點擊閱讀原文,了解更多
新人創(chuàng)作打卡挑戰(zhàn)賽發(fā)博客就能抽獎!定制產(chǎn)品紅包拿不停!總結(jié)
以上是生活随笔為你收集整理的新手上路必学的Python函数基础知识,全在这里了(多段代码举例)的全部內(nèi)容,希望文章能夠幫你解決所遇到的問題。
- 上一篇: 从0到1:Python爬虫知识点梳理
- 下一篇: Makefile之变量(10)