《利用Python進(jìn)行數(shù)據(jù)分析·第2版》【Chapter 3.2】Python 的函數(shù)

【Chapter 3.2】Python 的函數(shù)

3.2 函數(shù)

Functions是python中很重要的概念≌饕铮可以用def來定義立美;

def my_function(x, y, z=1.5):
    if z > 1:
        return z * (x + y)
    else:
        return z / (x + y)

1 Namespaces, Scope, and Local Functions(命名空間翻擒,作用范圍,局部函數(shù))

scope分兩種渊抽,global and local (全局和局部)。namespace用來描述變量的作用范圍议忽。當(dāng)調(diào)用一個(gè)函數(shù)的時(shí)候懒闷,會(huì)自動(dòng)創(chuàng)建一個(gè)局部命名空間,用來存放函數(shù)的參數(shù)栈幸,一旦函數(shù)結(jié)束愤估,局部命名空間會(huì)被自動(dòng)廢棄掉∷僦罚考慮下面的例子:

def func():
    a = []
    for i in range(5):
        a.append(i)

當(dāng)func()被調(diào)用玩焰,會(huì)創(chuàng)建一個(gè)空list a,然后5個(gè)元素賦給a芍锚。函數(shù)結(jié)束后昔园,a也會(huì)被廢棄。假設(shè)有下面的定義:

a = []
def func():
    for i in range(5):
        a.append(i)

給函數(shù)范圍外的變量賦值是可行的并炮,但是這些變量必須通過global關(guān)鍵字來聲明:

a = None

def bind_a_variable():
    global a
    a = []

bind_a_variable()
In [170]: print(a)
[]

注意:

這里我們不推薦使用global關(guān)鍵字默刚。因?yàn)檫@個(gè)全局變量通常用于存儲系統(tǒng)狀態(tài)(state in a system),如果你用了很多全局變量逃魄,或許你該考慮使用class荤西。

2 Returning Multiple Values(返回多個(gè)值)

Python 中函數(shù)可以返回多個(gè)值。下面是一個(gè)簡單的例子:

def f():
    a = 5
    b = 6
    c = 7
    return a, b, c

a, b, c = f()

在數(shù)據(jù)分析和其他科學(xué)計(jì)算應(yīng)用中嗅钻,你會(huì)發(fā)現(xiàn)自己常常這么干皂冰。

原理:其實(shí)函數(shù)還是返回了一個(gè)object,即tuple养篓,然后這個(gè)tuple被解壓給了result variables. 比如:

return_value = f()

這樣的話秃流,return_value就是一個(gè)3個(gè)返回值的三元元組 。

此外柳弄,還有一種非常具有吸引力的多值返回方式——返回字典:

def f():
    a = 5
    b = 6
    c = 7
    return {'a' : a, 'b' : b, 'c' : c}

3. 函數(shù)也是對象

因?yàn)楹瘮?shù)是對象舶胀,所以很多構(gòu)造能輕易表達(dá)出來概说。比如我們要對下面的string做一些數(shù)據(jù)清洗:

In [176]: states = [' Alabama ', 'Georgia!', 'Georgia', 'georgia', 
     ...:           'FlOrIda', 'south carolina##', 'West virginia?']
     ...: 

要想讓這些string統(tǒng)一,我們要做幾件事:去除空格嚣伐,刪去標(biāo)點(diǎn)符號糖赔,標(biāo)準(zhǔn)化大小寫。

做法一 :是利用內(nèi)建函數(shù)和re模塊(正則表達(dá)式):

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
clean_string(states)

['Alabama',
 'Georgia',
 'Georgia',
 'Georgia',
 'Florida',
 'South Carolina',
 'West Virginia']

還有一種做法轩端,把一系列操作放在一個(gè)list里:

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
clean_strings(states, clean_ops)

一個(gè)更函數(shù)化的方式能讓你方便得在一個(gè)高等級上轉(zhuǎn)變string放典。可以把函數(shù)當(dāng)做其他函數(shù)的參數(shù)基茵,比如用內(nèi)建的map函數(shù)奋构,這個(gè)map函數(shù)能把一個(gè)函數(shù)用于一個(gè)序列上:

for x in map(remove_punctuation, states):
    print(x)
    
Alabama 
Georgia
Georgia
georgia
FlOrIda
south carolina
West virginia

4.匿名(lambda)函數(shù)

這種函數(shù)只有一行,結(jié)果能返回值拱层。下面兩個(gè)函數(shù)是一樣的效果:

def short_function(x):
    return x * 2

equiv_anon = lambda x: x * 2

之后我們只稱其為lambda函數(shù)弥臼。這種函數(shù)在數(shù)據(jù)分析方面非常有用,就因?yàn)榉奖愀啤1热缦旅娴睦樱?/p>

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)

假設(shè)你想按不同字母的數(shù)量給一組string排序:

In [178]: strings.sort(key=lambda x: len(set(list(x))))

In [179]: strings
Out[179]: ['aaaa', 'foo', 'abab', 'bar', 'card']

5. 柯里化:局部參數(shù)應(yīng)用

在計(jì)算機(jī)科學(xué)中径缅,柯里化(Currying)是把接受多個(gè)參數(shù)的函數(shù)變換成接受一個(gè)單一參數(shù)(最初函數(shù)的第一個(gè)參數(shù))的函數(shù),并且返回接受余下的參數(shù)且返回結(jié)果的新函數(shù)的技術(shù)烙肺。

簡單的說纳猪,通過局部參數(shù)應(yīng)用,在一個(gè)原有函數(shù)的基礎(chǔ)上茬高,構(gòu)造一個(gè)新的函數(shù)兆旬。比如,我們想要一個(gè)加法的函數(shù):

def add_number(x, y):
    return x + y

通過上面這個(gè)函數(shù)怎栽,我們可以衍生出一個(gè)只需要一個(gè)參數(shù)的新方程丽猬,add_five,即把5加到參數(shù)上:

add_five = lambda y: add_number(5, y)

其中第二個(gè)參數(shù)y叫做被柯里化了熏瞄。這其實(shí)沒什么特別的脚祟,我們做的其實(shí)就是用一個(gè)已有的函數(shù)定義了一個(gè)新函數(shù)。而內(nèi)建的functools模塊里的 partial函數(shù)能簡化這個(gè)操作:

from functools import partial
add_five = partial(add_number, 5)

6. Generators(生成器)

這個(gè)東西在python里很重要强饮,能用來迭代序列由桌。比如,迭代一個(gè)字典:

some_dict = {'a': 1, 'b': 2, 'c': 3}

for key in some_dict:
    print(key)

當(dāng)我們寫for key in some_dict的時(shí)候邮丰,python解釋器就新建了一個(gè)iterator:

dict_iterator = iter(some_dict)
dict_iterator

<dict_keyiterator at 0x104c92ef8>

一個(gè)生成器能產(chǎn)生object給python 解釋器行您,當(dāng)遇到for loop這樣的情景時(shí)。大部分方法剪廉,除了list之類的object娃循,都能接受迭代器。比如內(nèi)建的函數(shù)min, max, sum,或是類型構(gòu)造器 list, tuple:

list(dict_iterator)

['a', 'b', 'c']

生成器是用于構(gòu)造迭代對象的簡潔方式斗蒋。不像其他函數(shù)一口氣執(zhí)行完捌斧,返回一個(gè)結(jié)果笛质,生成器是多次返回一個(gè)序列,每請求一次捞蚂,才會(huì)返回一個(gè)妇押。用 yield 可以構(gòu)建一個(gè)生成器:

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)你實(shí)際調(diào)用一個(gè)生成器的時(shí)候,不會(huì)有代碼立刻執(zhí)行:

gen = squares()

gen
Out[198]: <generator object squares at 0x0000018B5DD88BA0>

知道我們發(fā)出請求姓迅,生成器才會(huì)執(zhí)行代碼:

for x in gen:
    print(x, end=' ')
    
Generating squares from 1 to 100
1 4 9 16 25 36 49 64 81 100 

Generator expresssions (生成器表達(dá)式)

另一個(gè)構(gòu)造生成器的方式是利用生成器表達(dá)式敲霍。寫法就像列表表達(dá)式一樣,只不過使用括號:

gen = (x **2 for x in range(100))

gen
Out[201]: <generator object <genexpr> at 0x0000018B5DDD37D8>

上面的代碼和下面冗長的代碼是等效的:

def _make_gen():
    for x in range(100):
        yield x ** 2
gen = _make_gen()

生成器表達(dá)式也可以取代列表推導(dǎo)式丁存,作為函數(shù)參數(shù):

dict((i, i**2) for i in range(5))

itertools模塊

itertools模塊有很多常用算法的生成器色冀。比如groupby能取任意序列當(dāng)做函數(shù)。

import itertools 

first_letter = lambda x: x[0]
names = ['Alan', 'Adam', 'Wes', 'Will', 'Albert', 'Steven']
for letter,group in itertools.groupby(names,first_letter):
    print(letter,list(group))
    
A ['Alan', 'Adam']
W ['Wes', 'Will']
A ['Albert']
S ['Steven']

itertools.groupby(iterable[, key]) 返回一個(gè)產(chǎn)生按照key進(jìn)行分組后的值集合的迭代器.

如果iterable在多次連續(xù)迭代中生成了同一項(xiàng)柱嫌,則會(huì)定義一個(gè)組,如果將此函數(shù)應(yīng)用一個(gè)分類列表屯换,那么分組將定義該列表中的所有唯一項(xiàng)编丘,key(如果已提供)是一個(gè)函數(shù),應(yīng)用于每一項(xiàng)彤悔,如果此函數(shù)存在返回值嘉抓,該值將用于后續(xù)項(xiàng)而不是該項(xiàng)本身進(jìn)行比較,此函數(shù)返回的迭代器生成元素(key, group)晕窑,其中key是分組的鍵值抑片,group是迭代器,生成組成該組的所有項(xiàng)杨赤。

即:按照keyfunc函數(shù)對序列每個(gè)元素執(zhí)行后的結(jié)果分組(每個(gè)分組是一個(gè)迭代器), 返回這些分組的迭代器

itertools.groupby(names, first_letter)敞斋,把names中的每個(gè)字符串放入到first_letter這個(gè)函數(shù)中,得到了首字母作為key疾牲,賦給了letter植捎。但后面那個(gè)group的結(jié)果我沒看懂,Albert也是A開頭的阳柔,為什么沒有歸到第一組里呢焰枢? 答案由github用戶RookieDay提供,具體內(nèi)容如下:

itertools.groupby(iterable[, key])

根據(jù)文檔里面的這段解釋

The operation of groupby() is similar to the uniq filter in Unix. It generates a break or new group every time the value of the key function changes (which is why it is usually necessary to have sorted the data using the same key function).

可以發(fā)現(xiàn)舌剂,這里groupby的使用類似于Unix里面的uniq(uniq可檢查文本文件中重復(fù)出現(xiàn)的行 --> 重復(fù)行是指連續(xù)出現(xiàn)的重復(fù)行)命令济锄,key函數(shù)值的改變會(huì)中斷或者生成新的組(這就是為什么使用相同key函數(shù)對數(shù)據(jù)排序的原因),所以這里groupby 可以理解為把迭代器中相鄰的重復(fù)元素挑出來放在一起(參考廖雪峰groupby)

groups = []
uniquekeys = []
data = sorted(data, key=keyfunc)
for k, g in groupby(data, keyfunc):
    groups.append(list(g))      # Store group iterator as a list
    uniquekeys.append(k)

所以如果想要讓Albert與其他兩個(gè)A開頭的名字分到一組的話霍转,可以先對names排序荐绝,讓三個(gè)A開頭的名字相鄰,然后再用groupby:

表3-2中列出了一些我經(jīng)常用到的itertools函數(shù)谴忧。建議參閱Python官方文檔很泊,進(jìn)一步學(xué)習(xí)角虫。

7. 錯(cuò)誤和異常處理

在數(shù)據(jù)分析應(yīng)用中,許多函數(shù)只能用于特定的輸入委造。比如float能把string變?yōu)楦↑c(diǎn)數(shù)戳鹅,但如果有不正確的輸入的話會(huì)報(bào)錯(cuò):

捕捉多個(gè)不同的異常:

def attempt_float(x):
    try:
        return float(x)
    except (TypeError, ValueError):
        return x

在某些情況下,你不想抑制任何異常昏兆,但你想希望執(zhí)行某些代碼枫虏,不論try里的代碼成功執(zhí)行與否。這樣的話爬虱,需要用的finally:

f = open(path, 'w')

try:
    write_to_file(y)
finally:
    f.close()

這樣的處理會(huì)始終會(huì)讓f關(guān)閉隶债。同樣的,你可以在try里的代碼成功執(zhí)行后跑筝,讓某些代碼執(zhí)行:

f = open(path, 'w')

try:
    write_to_file(f)
except:
    print('Failed')
else:
    print('Succeeded')
finally:
    f.close()

Exceptions in IPython (IPython中的異常)

當(dāng)使用%run執(zhí)行代碼片段死讹,引發(fā)異常后,IPython中默認(rèn)打印出所有的調(diào)用信息(traceback)

%run example/ipython_bug.py
ERROR:root:File `'example/ipython_bug.py'` not found.
?著作權(quán)歸作者所有,轉(zhuǎn)載或內(nèi)容合作請聯(lián)系作者
  • 序言:七十年代末曲梗,一起剝皮案震驚了整個(gè)濱河市赞警,隨后出現(xiàn)的幾起案子,更是在濱河造成了極大的恐慌虏两,老刑警劉巖愧旦,帶你破解...
    沈念sama閱讀 206,968評論 6 482
  • 序言:濱河連續(xù)發(fā)生了三起死亡事件,死亡現(xiàn)場離奇詭異定罢,居然都是意外死亡笤虫,警方通過查閱死者的電腦和手機(jī),發(fā)現(xiàn)死者居然都...
    沈念sama閱讀 88,601評論 2 382
  • 文/潘曉璐 我一進(jìn)店門祖凫,熙熙樓的掌柜王于貴愁眉苦臉地迎上來琼蚯,“玉大人,你說我怎么就攤上這事惠况×柰#” “怎么了?”我有些...
    開封第一講書人閱讀 153,220評論 0 344
  • 文/不壞的土叔 我叫張陵售滤,是天一觀的道長罚拟。 經(jīng)常有香客問我,道長完箩,這世上最難降的妖魔是什么赐俗? 我笑而不...
    開封第一講書人閱讀 55,416評論 1 279
  • 正文 為了忘掉前任,我火速辦了婚禮弊知,結(jié)果婚禮上阻逮,老公的妹妹穿的比我還像新娘。我一直安慰自己秩彤,他們只是感情好叔扼,可當(dāng)我...
    茶點(diǎn)故事閱讀 64,425評論 5 374
  • 文/花漫 我一把揭開白布事哭。 她就那樣靜靜地躺著,像睡著了一般瓜富。 火紅的嫁衣襯著肌膚如雪鳍咱。 梳的紋絲不亂的頭發(fā)上,一...
    開封第一講書人閱讀 49,144評論 1 285
  • 那天与柑,我揣著相機(jī)與錄音谤辜,去河邊找鬼。 笑死价捧,一個(gè)胖子當(dāng)著我的面吹牛丑念,可吹牛的內(nèi)容都是我干的。 我是一名探鬼主播结蟋,決...
    沈念sama閱讀 38,432評論 3 401
  • 文/蒼蘭香墨 我猛地睜開眼脯倚,長吁一口氣:“原來是場噩夢啊……” “哼!你這毒婦竟也來了嵌屎?” 一聲冷哼從身側(cè)響起挠将,我...
    開封第一講書人閱讀 37,088評論 0 261
  • 序言:老撾萬榮一對情侶失蹤,失蹤者是張志新(化名)和其女友劉穎编整,沒想到半個(gè)月后,有當(dāng)?shù)厝嗽跇淞掷锇l(fā)現(xiàn)了一具尸體乳丰,經(jīng)...
    沈念sama閱讀 43,586評論 1 300
  • 正文 獨(dú)居荒郊野嶺守林人離奇死亡掌测,尸身上長有42處帶血的膿包…… 初始之章·張勛 以下內(nèi)容為張勛視角 年9月15日...
    茶點(diǎn)故事閱讀 36,028評論 2 325
  • 正文 我和宋清朗相戀三年,在試婚紗的時(shí)候發(fā)現(xiàn)自己被綠了产园。 大學(xué)時(shí)的朋友給我發(fā)了我未婚夫和他白月光在一起吃飯的照片汞斧。...
    茶點(diǎn)故事閱讀 38,137評論 1 334
  • 序言:一個(gè)原本活蹦亂跳的男人離奇死亡,死狀恐怖什燕,靈堂內(nèi)的尸體忽然破棺而出粘勒,到底是詐尸還是另有隱情,我是刑警寧澤屎即,帶...
    沈念sama閱讀 33,783評論 4 324
  • 正文 年R本政府宣布庙睡,位于F島的核電站,受9級特大地震影響技俐,放射性物質(zhì)發(fā)生泄漏乘陪。R本人自食惡果不足惜,卻給世界環(huán)境...
    茶點(diǎn)故事閱讀 39,343評論 3 307
  • 文/蒙蒙 一雕擂、第九天 我趴在偏房一處隱蔽的房頂上張望啡邑。 院中可真熱鬧,春花似錦井赌、人聲如沸谤逼。這莊子的主人今日做“春日...
    開封第一講書人閱讀 30,333評論 0 19
  • 文/蒼蘭香墨 我抬頭看了看天上的太陽流部。三九已至戚绕,卻和暖如春,著一層夾襖步出監(jiān)牢的瞬間贵涵,已是汗流浹背列肢。 一陣腳步聲響...
    開封第一講書人閱讀 31,559評論 1 262
  • 我被黑心中介騙來泰國打工, 沒想到剛下飛機(jī)就差點(diǎn)兒被人妖公主榨干…… 1. 我叫王不留宾茂,地道東北人瓷马。 一個(gè)月前我還...
    沈念sama閱讀 45,595評論 2 355
  • 正文 我出身青樓,卻偏偏與公主長得像跨晴,于是被迫代替她去往敵國和親欧聘。 傳聞我的和親對象是個(gè)殘疾皇子,可洞房花燭夜當(dāng)晚...
    茶點(diǎn)故事閱讀 42,901評論 2 345

推薦閱讀更多精彩內(nèi)容