15 Python函數(shù)

函數(shù)是組織好的对雪、可重復(fù)使用的,用來實(shí)現(xiàn)單一或相關(guān)聯(lián)功能的代碼段米绕。
函數(shù)能夠提高應(yīng)用的模塊性和代碼的重復(fù)利用率瑟捣。Python提供了許多內(nèi)建函數(shù),如print()义郑、int()蝶柿。讀者也可以自己創(chuàng)建函數(shù),這樣的函數(shù)稱為用戶自定義函數(shù)非驮。

調(diào)用函數(shù)

在程序設(shè)計(jì)中,函數(shù)是指用于進(jìn)行某種計(jì)算的一系列語句的有名稱的組合雏赦。定義函數(shù)時(shí)劫笙,需要指定函數(shù)的名稱并編寫一系列程序語句芙扎,之后可以使用名稱“調(diào)用”這個函數(shù)。
前面我們已經(jīng)介紹過函數(shù)調(diào)用填大,例如:

>>> print('hello world')
hello world
>>> type('hello')
<class 'str'>
>>> int(12.1)
12

以上代碼就是函數(shù)的調(diào)用戒洼。函數(shù)括號中的表達(dá)式稱之為函數(shù)的參數(shù)。函數(shù)“接收”參數(shù)允华,并“返回”結(jié)果圈浇,這個結(jié)果稱為返回值(return value)。比如上面示例中的int(12.1)靴寂,12.1就是“接收”的參數(shù)磷蜀,得到的結(jié)果是12,12就是返回值百炬。
Python 3內(nèi)置了很多有用的函數(shù)褐隆,可以直接調(diào)用。要調(diào)用一個函數(shù)剖踊,就需要知道函數(shù)的名稱和參數(shù)庶弃,比如求絕對值的函數(shù)abs只有一個參數(shù)〉鲁海可以直接從Python的官方網(wǎng)站查看文檔:

https://docs.python.org/3/library/functions.html

進(jìn)入官方網(wǎng)站可以看到如圖所示的頁面歇攻,這里顯示了Python 3內(nèi)置的所有函數(shù),abs()函數(shù)在第一個位置梆造。從左上角可以看到這個函數(shù)是Python 3.5.2版本的內(nèi)置函數(shù)缴守。


Python官方網(wǎng)站

單擊abs()函數(shù),頁面會跳到如圖7-2所示的位置澳窑,有對abs()函數(shù)的說明斧散。截圖中的意思是:返回一個數(shù)的絕對值。參數(shù)可能是整數(shù)或浮點(diǎn)數(shù)摊聋。如果參數(shù)是一個復(fù)數(shù)鸡捐,就返回它的大小。

abs()函數(shù)幫助說明

除了到Python官方網(wǎng)站查看文檔麻裁,還可以在交互式命令行通過help(abs)查看abs函數(shù)的幫助信息箍镜。在交互模式下輸入:

>>> help(abs)
Help on built-in function abs in module builtins:
abs(x, /)
    Return the absolute value of the argument.

可以看到,輸出了對應(yīng)的幫助信息煎源,但是沒有官方網(wǎng)站的詳細(xì)色迂。

下面實(shí)際操作abs()函數(shù),在交互模式輸入:

>>> abs(20)
20
>>> abs(-20)
20
>>> abs(3.14)
3.14
>>> abs(-3.14)
3.14

從上面的輸出結(jié)果可以看出手销,abs函數(shù)用于求絕對值歇僧。
調(diào)用abs()函數(shù)時(shí),如果傳入的參數(shù)數(shù)量不對,就會報(bào)TypeError的錯誤诈悍, Python會明確告訴你:abs()有且只有一個參數(shù)祸轮,但給出了兩個,例如:

>>> abs(5,6)
Traceback (most recent call last):
  File "<pyshell#171>", line 1, in <module>
    abs(5,6)
TypeError: abs() takes exactly one argument (2 given)

如果傳入的參數(shù)數(shù)量是對的侥钳,但參數(shù)類型不能被函數(shù)接收适袜,也會報(bào)TypeError的錯誤。給出錯誤信息:str是錯誤的參數(shù)類型舷夺,例如:

>>> abs('hello')
Traceback (most recent call last):
  File "<pyshell#172>", line 1, in <module>
    abs('hello')
TypeError: bad operand type for abs(): 'str'

函數(shù)名其實(shí)是指向一個函數(shù)對象的引用苦酱,完全可以把函數(shù)名賦給一個變量,相當(dāng)于給這個函數(shù)起了一個“別名”给猾,在交互模式下輸入:

>>> fun=abs   # 變量fun 指向abs 函數(shù)
>>> fun(-5)  # 所以可以通過fun 調(diào)用abs 函數(shù)
5
>>> fun(-3.14)  # 所以可以通過fun 調(diào)用abs 函數(shù)
3.14
>>> fun(3.14)  # 所以可以通過fun 調(diào)用abs 函數(shù)
3.14

調(diào)用Python中的函數(shù)時(shí)疫萤,需要根據(jù)函數(shù)定義傳入正確的參數(shù)。如果函數(shù)調(diào)用出錯耙册,就要會看錯誤信息给僵,這時(shí)就要考驗(yàn)?zāi)愕挠⒄Z水平了。

定義函數(shù)

到目前為止详拙,我們用的都是Python內(nèi)置函數(shù)帝际。這些Python內(nèi)置函數(shù)的定義部分對我們來說是透明的。因此饶辙,我們只需關(guān)注這些函數(shù)的用法蹲诀,而不必關(guān)心函數(shù)是如何定義的。Python支持自定義函數(shù)弃揽,即由我們自己定義一個實(shí)現(xiàn)某個功能的函數(shù)脯爪。下面是自定義函數(shù)的簡單規(guī)則。
(1)函數(shù)代碼塊以def關(guān)鍵詞開頭矿微,后接函數(shù)標(biāo)識符名稱和圓括號“()”痕慢。
(2)所有傳入的參數(shù)和自變量都必須放在圓括號中,可以在圓括號中定義參數(shù)涌矢。
(3)函數(shù)的第一行語句可以選擇性使用文檔字符串掖举,用于存放函數(shù)說明。
(4)函數(shù)內(nèi)容以冒號開始娜庇,并且要縮進(jìn)塔次。
(5)return [表達(dá)式]結(jié)束函數(shù),選擇性返回一個值給調(diào)用方。不帶表達(dá)式的return相當(dāng)于返回None。
Python定義函數(shù)使用def關(guān)鍵字梯嗽,一般格式如下:

def 函數(shù)名(參數(shù)列表):
  函數(shù)體
或者更直觀的表示為:
def <name>(arg1, arg2,... argN):
<statements>

函數(shù)的名字必須以字母開頭,可以包括下劃線“_”继榆。和定義變量一樣,不能把Python的關(guān)鍵字定義成函數(shù)的名字。函數(shù)內(nèi)的語句數(shù)量是任意的裕照,每個語句至少有一個空格的縮進(jìn)攒发,以表示該語句屬于這個函數(shù)调塌。函數(shù)體必須保持縮進(jìn)一致晋南,因?yàn)樵诤瘮?shù)中,縮進(jìn)結(jié)束就表示函數(shù)結(jié)束羔砾。
現(xiàn)在已經(jīng)知道定義函數(shù)的簡單規(guī)則和一般格式了负间。下面我們進(jìn)行實(shí)際操作,在文本中定義函數(shù)并調(diào)用:

#! /usr/bin/python3
# -*- coding:UTF-8 -*-

def hello():
    print('hello,world')

hello()

以上示例中的hello()就是我們自定義的函數(shù)姜凄。此處為了看到執(zhí)行結(jié)果政溃,在函數(shù)定義完后做了函數(shù)的自我調(diào)用。如果不自我調(diào)用态秧,執(zhí)行該函數(shù)就沒有任何輸出董虱,當(dāng)然也不會報(bào)錯(除非代碼有問題)。
在cmd命令下執(zhí)行以上py文件申鱼,執(zhí)行結(jié)果如下:

Hello,world!

需要注意以下幾點(diǎn):
(1)沒有return語句時(shí)愤诱,函數(shù)執(zhí)行完畢也會返回結(jié)果,不過結(jié)果為None捐友。
(2)return None可以簡寫為return淫半。
(3)在Python中定義函數(shù)時(shí),需要保持函數(shù)體中同一層級的代碼縮進(jìn)一致匣砖。
根據(jù)以上示例科吭,是不是一個函數(shù)中只能有一條語句呢?除了輸出操作猴鲫,還能執(zhí)行其他操作嗎对人?
在一個函數(shù)中可以輸出多條語句,并能做相應(yīng)的運(yùn)算操作拂共,以及輸出運(yùn)算結(jié)果牺弄。
例如,定義輸出多條語句的函數(shù)并執(zhí)行:

#! /usr/bin/python3
# -*- coding:UTF-8 -*-

def printmore():
    print('該函數(shù)可以輸出多條語句匣缘,我是第一條猖闪。')
    print('我是第二條')
    print('我是第三條')

printmore() #調(diào)用函數(shù)

執(zhí)行結(jié)果如下:

該函數(shù)可以輸出多條語句,我是第一條肌厨。
我是第二條
我是第三條

定義輸出數(shù)字和計(jì)算的函數(shù)并執(zhí)行:

#! /usr/bin/python3
# -*- coding:UTF-8 -*-

def mixoperation():
    a=10
    b=20
    print(a)
    print(b)
    print(a+b)
    print('a+b 的和等于:',a+b)

mixoperation()   #調(diào)用函數(shù)

執(zhí)行結(jié)果如下:

10
20
30
a+b 的和等于: 30

以上示例驗(yàn)證了前面的內(nèi)容培慌。
定義一個什么都不做的函數(shù)可以嗎?當(dāng)然可以柑爸。如果想定義一個什么都不做的空函數(shù)吵护,可以用pass語句,定義如下函數(shù)并執(zhí)行:

#! /usr/bin/python3
# -*- coding:UTF-8 -*-

def donothing():
         pass

donothing()

執(zhí)行結(jié)果為沒有任何輸出。
pass語句什么都不做馅而,有什么用呢祥诽?實(shí)際上pass可以作為占位符,比如現(xiàn)在還沒想好怎么寫函數(shù)的代碼瓮恭,可以先放一個pass雄坪,讓代碼能運(yùn)行起來。
函數(shù)的目的是把一些復(fù)雜操作隱藏起來屯蹦,用于簡化程序的結(jié)構(gòu)维哈,使程序更容易閱讀。函數(shù)在調(diào)用前必須先定義登澜。

函數(shù)的參數(shù)

我們在7.2節(jié)中講述了如何定義函數(shù)阔挠,不過只講述了定義簡單函數(shù),還有一類函數(shù)是帶參數(shù)的脑蠕,稱為帶參數(shù)的函數(shù)购撼。本節(jié)將探討如何定義帶參數(shù)的函數(shù)及其使用。
調(diào)用函數(shù)時(shí)可以使用以下參數(shù)類型:
(1)必須參數(shù)谴仙。
(2)關(guān)鍵字參數(shù)迂求。
(3)默認(rèn)參數(shù)。
(4)可變參數(shù)狞甚。
(5)組合參數(shù)锁摔。
下面我們分別進(jìn)行介紹。

1 必須參數(shù)

必須參數(shù)必須以正確的順序傳入函數(shù)哼审。調(diào)用時(shí)數(shù)量必須和聲明時(shí)一樣谐腰。
定義如下函數(shù)并執(zhí)行:執(zhí)行結(jié)果如下:

#! /usr/bin/python3
# -*- coding:UTF-8 -*-

def paramone(str):
         print('the param is:',str)
         print('我是一個傳入?yún)?shù),我的值是:',str)

paramone('hello,world')
the param is: hello,world
我是一個傳入?yún)?shù)涩盾,我的值是: hello,world

我們定義了一個必須傳入一個參數(shù)的函數(shù)paramone(str)十气,傳入的參數(shù)為str,結(jié)果是將“hello,world”傳給str春霍。
對于上例砸西,若不傳入?yún)?shù)或傳入一個以上的參數(shù),結(jié)果會怎樣呢址儒?例如:

paramone() #不輸入?yún)?shù)

執(zhí)行結(jié)果如下:

Traceback (most recent call last):
  File "<pyshell#208>", line 1, in <module>
    paramone()
TypeError: paramone() missing 1 required positional argument: 'str'

執(zhí)行結(jié)果告訴我們芹枷,函數(shù)缺少一個必需的定位參數(shù),參數(shù)類型為str莲趣。
paramone('hello','world') #輸入超過一個參數(shù)
執(zhí)行結(jié)果如下:

Traceback (most recent call last):
  File "<pyshell#209>", line 1, in <module>
    paramone('hello','world')
TypeError: paramone() takes 1 positional argument but 2 were given

執(zhí)行結(jié)果告訴我們鸳慈,函數(shù)只需一個位置參數(shù)卻給了兩個。
通過示例可以看到喧伞,對于定義的paramone()函數(shù)走芋,不傳入?yún)?shù)或傳入一個以上參數(shù)绩郎,都會報(bào)錯。所以對于此類函數(shù)翁逞,必須傳遞對應(yīng)正確個數(shù)的參數(shù)肋杖。

2 關(guān)鍵字參數(shù)

關(guān)鍵字參數(shù)和函數(shù)調(diào)用關(guān)系緊密,函數(shù)調(diào)用使用關(guān)鍵字參數(shù)確定傳入的參數(shù)值挖函。
使用關(guān)鍵字參數(shù)允許調(diào)用函數(shù)時(shí)參數(shù)的順序與聲明時(shí)不一致状植,因?yàn)镻ython解釋器能夠用參數(shù)名匹配參數(shù)值。
定義如下函數(shù)并執(zhí)行:

#! /usr/bin/python3
# -*- coding:UTF-8 -*-

def personinfo(age,name):
    print('年齡:',age)
    print('名稱:',name)
    return

print('-------按參數(shù)順序傳入?yún)?shù)-------')
personinfo(21,'小萌')
print('-------不按參數(shù)順序傳入?yún)?shù)挪圾,指定參數(shù)名-------')
personinfo(name='小萌',age=21)
print('-------按參數(shù)順序傳入?yún)?shù)浅萧,并指定參數(shù)名-------')
personinfo(age=21,name='小萌')

調(diào)用函數(shù)執(zhí)行結(jié)果如下:

-------按參數(shù)順序傳入?yún)?shù)-------
年齡: 21
名稱: 小萌
-------不按參數(shù)順序傳入?yún)?shù),指定參數(shù)名-------
年齡: 21
名稱: 小萌
-------按參數(shù)順序傳入?yún)?shù)哲思,并指定參數(shù)名-------
年齡: 21
名稱: 小萌

由以上輸出結(jié)果可以看到,對于personinfo()函數(shù)吩案,只要指定參數(shù)名棚赔,輸入?yún)?shù)的順序?qū)Y(jié)果就沒有影響,都能得到正確的結(jié)果徘郭。

3 默認(rèn)參數(shù)

調(diào)用函數(shù)時(shí)靠益,如果沒有傳遞參數(shù),就會使用默認(rèn)參數(shù)残揉。
使用默認(rèn)參數(shù)胧后,就是在定義函數(shù)時(shí),給參數(shù)一個默認(rèn)值抱环。如果沒有給調(diào)用的函數(shù)的參數(shù)賦值壳快,調(diào)用的函數(shù)就會使用這個默認(rèn)值。
例如镇草,定義如下函數(shù)并執(zhí)行:

#! /usr/bin/python3
# -*- coding:UTF-8 -*-

def defaultparam(name,age=23):
    print('hi眶痰,我叫:',name)
    print('我今年:',age)
    return

defaultparam('小萌')

調(diào)用函數(shù)執(zhí)行結(jié)果如下:

hi,我叫: 小萌
我今年: 23

從以上示例我們看到梯啤,在函數(shù)調(diào)用時(shí)沒有對age賦值竖伯,在輸出結(jié)果中使用了函數(shù)定義時(shí)的默認(rèn)值。如果我們對age賦值因宇,最后輸出結(jié)果會使用哪個值呢七婴?
重新調(diào)用上面的函數(shù):
defaultparam('小萌',21) #函數(shù)默認(rèn)age=23
得到的執(zhí)行結(jié)果如下:
hi,我叫: 小萌
我今年: 21
通過執(zhí)行函數(shù)我們看到察滑,執(zhí)行結(jié)果使用的是我們傳入的參數(shù)打厘。由此得知:當(dāng)對默認(rèn)參數(shù)傳值時(shí),函數(shù)執(zhí)行時(shí)調(diào)用的是我們傳入的值杭棵。
把函數(shù)的默認(rèn)參數(shù)放在前面是否可行呢婚惫?定義如下函數(shù)并執(zhí)行:

#! /usr/bin/python3
# -*- coding:UTF-8 -*-

def defaultparam(age=23,name):
        print('hi氛赐,我叫:',name)
        print('我今年:',age)
    return

defaultparam(age=21,name='小萌')

執(zhí)行結(jié)果如下:

SyntaxError: non-default argument follows default argument

執(zhí)行結(jié)果是編譯不通過,錯誤信息是:非默認(rèn)參數(shù)跟在默認(rèn)參數(shù)后面了先舷。
這里提醒我們艰管,默認(rèn)參數(shù)一定要放在非默認(rèn)參數(shù)后面。如果需要多個默認(rèn)參數(shù)蒋川,該怎么辦呢牲芋?我們看以下幾個函數(shù)定義的示例。
示例1:默認(rèn)參數(shù)在必須參數(shù)前

#! /usr/bin/python3
# -*- coding:UTF-8 -*-

def defaultparam1(age=23,name,addr='shanghai'):
        print('hi捺球,我叫:',name)
        print('我今年:',age)
        print('我現(xiàn)在在:',addr)
        return

def defaultparam2(age=23,addr='shanghai',name):
        print('hi缸浦,我叫:',name)
        print('我今年:',age)
        print('我現(xiàn)在在:',addr)
        return

defaultparam1(age=23, '小萌',addr='shanghai')
defaultparam2(age=23,addr='shanghai', '小萌')

執(zhí)行結(jié)果如下(報(bào)錯了):

SyntaxError: non-default argument follows default argument

示例2:

#! /usr/bin/python3
# -*- coding:UTF-8 -*-

def defaultparam(name,age=23,addr='shanghai'):
    print('hi,我叫:',name)
    print('我今年:',age)
    print('我現(xiàn)在在:',addr)
    return

print('-------傳入必須參數(shù)-------')
defaultparam('小萌')
print('-------傳入必須參數(shù)氮兵,更改第一個默認(rèn)參數(shù)值-------')
defaultparam('小萌',21)
print('-------傳入必須參數(shù)裂逐,默認(rèn)參數(shù)值都更改-------')
defaultparam('小萌',21,'beijing')
print('-------傳入必須參數(shù),指定默認(rèn)參數(shù)名并更改參數(shù)值-------')
defaultparam('小萌',addr='beijing')
print('-------傳入必須參數(shù)泣栈,指定參數(shù)名并更改值-------')
defaultparam('小萌',addr='beijing',age=23)
print('-------第一個默認(rèn)參數(shù)不帶參數(shù)名卜高,第二個帶-------')
defaultparam('小萌',21,addr='beijing')
print('-------兩個默認(rèn)參數(shù)都帶參數(shù)名-------')
defaultparam('小萌',age=23,addr='beijing')
print('-------第一個默認(rèn)參數(shù)帶參數(shù)名,第二個不帶南片,報(bào)錯-------')
defaultparam('小萌',age=23,'beijing')

執(zhí)行結(jié)果如下:

-------傳入必須參數(shù)-------
hi掺涛,我叫: 小萌
我今年: 23
我現(xiàn)在在: shanghai
-------傳入必須參數(shù),更改第一個默認(rèn)參數(shù)值-------
hi疼进,我叫: 小萌
我今年: 21
我現(xiàn)在在: shanghai
-------傳入必須參數(shù)薪缆,默認(rèn)參數(shù)值都更改-------
hi,我叫: 小萌
我今年: 21
我現(xiàn)在在: beijing
-------傳入必須參數(shù)伞广,指定默認(rèn)參數(shù)名并更改參數(shù)值-------
hi拣帽,我叫: 小萌
我今年: 23
我現(xiàn)在在: beijing
-------傳入必須參數(shù),指定參數(shù)名并更改值-------
hi赔癌,我叫: 小萌
我今年: 23
我現(xiàn)在在: beijing
-------第一個默認(rèn)參數(shù)不帶參數(shù)名诞外,第二個帶-------
hi,我叫: 小萌
我今年: 21
我現(xiàn)在在: beijing
-------兩個默認(rèn)參數(shù)都帶參數(shù)名-------
hi灾票,我叫: 小萌
我今年: 23
我現(xiàn)在在: beijing
-------第一個默認(rèn)參數(shù)帶參數(shù)名峡谊,第二個不帶,報(bào)錯-------
SyntaxError: positional argument follows keyword argument

從以上執(zhí)行結(jié)果可以發(fā)現(xiàn):
(1)無論有多少默認(rèn)參數(shù)刊苍,默認(rèn)參數(shù)都不能在必須參數(shù)之前既们。
(2)無論有多少默認(rèn)參數(shù),若不傳入默認(rèn)參數(shù)值正什,則使用默認(rèn)值啥纸。
(3)若要更改某一個默認(rèn)參數(shù)值,又不想傳入其他默認(rèn)參數(shù)婴氮,且該默認(rèn)參數(shù)的位置不是第一個斯棒,則可以通過參數(shù)名更改想要更改的默認(rèn)參數(shù)值盾致。
(4)若有一個默認(rèn)參數(shù)通過傳入?yún)?shù)名更改參數(shù)值,則其他想要更改的默認(rèn)參數(shù)都需要傳入?yún)?shù)名更改參數(shù)值荣暮,否則報(bào)錯庭惜。
(5)更改默認(rèn)參數(shù)值時(shí),傳入默認(rèn)參數(shù)的順序不需要根據(jù)定義的函數(shù)中的默認(rèn)參數(shù)的順序傳入穗酥,不過最好同時(shí)傳入?yún)?shù)名护赊,否則容易出現(xiàn)執(zhí)行結(jié)果與預(yù)期不一致的情況。
通過以上示例可以看出砾跃,默認(rèn)參數(shù)是比較有用的骏啰,通過默認(rèn)參數(shù)可以幫助我們少寫不少代碼,比如使用上面的代碼幫助某單位錄入人員信息抽高,如果有很多人的addr相同判耕,就不需要傳入每個人的addr值了。不過使用默認(rèn)參數(shù)時(shí)需要小心謹(jǐn)慎厨内。

4 可變參數(shù)

如果需要一個函數(shù)能夠處理的參數(shù)聲明時(shí)更多祈秕,這些參數(shù)叫作可變參數(shù)。和前面所述兩種參數(shù)不同雏胃,可變參數(shù)聲明時(shí)不會命名≈景埃基本語法如下:

def functionname([formal_args,] *var_args_tuple ):
  "函數(shù)_文檔字符串"
 function_suite
 return [expression]

加了星號(*)的變量名會存放所有未命名的變量參數(shù)瞭亮。如果變量參數(shù)在函數(shù)調(diào)用時(shí)沒有指定參數(shù),就是一個空元組固棚。我們也可以不向可變函數(shù)傳遞未命名的變量统翩。
下面通過實(shí)例說明可變函數(shù)的使用,定義如下函數(shù)并執(zhí)行:

#! /usr/bin/python3
# -*- coding:UTF-8 -*-

def personinfo(arg,*vartuple):
    print(arg)
    for var in vartuple:
         print('我屬于不定長參數(shù)部分:',var)
    return

print('------------不帶可變參數(shù)------------------')
personinfo('小萌')
print('------------帶兩個可變參數(shù)------------------')
personinfo('小萌',21,'beijing')
print('------------帶5 個可變參數(shù)----------------')
personinfo('小萌',21,'beijing',123,'shanghai','happy')

執(zhí)行結(jié)果如下:

------------不帶可變參數(shù)------------------
小萌
------------帶兩個可變參數(shù)------------------
小萌
我屬于不定長參數(shù)部分: 21
我屬于不定長參數(shù)部分: beijing
------------帶5 個可變參數(shù)----------------
小萌
我屬于不定長參數(shù)部分: 21
我屬于不定長參數(shù)部分: beijing
我屬于不定長參數(shù)部分: 123
我屬于不定長參數(shù)部分: shanghai
我屬于不定長參數(shù)部分: happy

這段代碼看起來很不可思議此洲,在定義函數(shù)時(shí)只定義了兩個參數(shù)厂汗,調(diào)用時(shí)卻可以傳入那么多參數(shù),難道該函數(shù)使用了洪荒之力呜师?
這其實(shí)就是可變參數(shù)的好處娶桦,我們在參數(shù)前面加了一個星號,在函數(shù)內(nèi)部汁汗,參數(shù)前的星號將所有值放在同一個元組中衷畦,通過這種方式將這些值收集起來,然后使用知牌。參數(shù)vartuple接收的是一個元組祈争,調(diào)用函數(shù)時(shí)可以傳入任意個數(shù)的參數(shù),也可以不傳角寸。
在這個示例中使用了前面所學(xué)的for循環(huán)菩混,通過for循環(huán)遍歷元組忿墅。
通過這種方式定義函數(shù),調(diào)用時(shí)是不是非常方便沮峡?我們在后續(xù)學(xué)習(xí)中會經(jīng)常遇到疚脐。
也可以使用這種方式處理前面學(xué)習(xí)的關(guān)鍵字參數(shù),例如:

other = {'城市': '北京', '愛好': '編程'}
def personinfo(name, number, **kw):
    print('名稱:', name, '學(xué)號:', number, '其他:', kw)

personinfo('小智', 1002, 城市=other['城市'], 愛好=other['愛好'])

函數(shù)執(zhí)行結(jié)果為:

名稱: 小智 學(xué)號: 1002 其他: {'愛好': '編程', '城市': '北京'}

由函數(shù)執(zhí)行結(jié)果看到帖烘,可以使用兩個“”號亮曹,即使用“*”處理關(guān)鍵字參數(shù)。函數(shù)調(diào)用時(shí)可以用更簡單的方式調(diào)用秘症,簡單形式如下:

personinfo('小智', 1002, **other)

函數(shù)執(zhí)行結(jié)果為:

名稱: 小智 學(xué)號: 1002 其他: {'愛好': '編程', '城市': '北京'}

執(zhí)行結(jié)果和前面一樣照卦,寫法上卻簡單了不少。此處other表示把other這個字典的所有key-value用關(guān)鍵字參數(shù)傳入到函數(shù)的kw參數(shù)乡摹,kw將獲得一個字典役耕,注意kw獲得的字典是other復(fù)制的,對kw的改動不會影響函數(shù)外的other聪廉。

5 組合參數(shù)

在Python中定義函數(shù)可以用必須參數(shù)瞬痘、關(guān)鍵字參數(shù)、默認(rèn)參數(shù)和可變關(guān)鍵字參數(shù)板熊,這4種參數(shù)可以組合使用框全。注意定義參數(shù)的順序必須是必須參數(shù)、默認(rèn)參數(shù)干签、可變參數(shù)和關(guān)鍵字參數(shù)津辩。
下面介紹組合參數(shù)的使用,請看如下函數(shù)定義:

#! /usr/bin/python3
# -*- coding:UTF-8 -*-

def exp(p1, p2, df=0, *vart, **kw):
    print('p1 =', p1, 'p2=', p2, 'df=', df, 'vart=', vart, 'kw =', kw)

exp(1,2)
exp(1,2,c=3)
exp(1,2,3,'a','b')
exp(1,2,3,'abc',x=9)

函數(shù)執(zhí)行結(jié)果如下:

p1 = 1 p2= 2 df= 0 vart= () kw = {}
p1 = 1 p2= 2 df= 0 vart= () kw = {'c': 3}
p1 = 1 p2= 2 df= 3 vart= ('a', 'b') kw = {}
p1 = 1 p2= 2 df= 3 vart= ('abc',) kw = {'x': 9}

由輸出結(jié)果看到容劳,使用了組合參數(shù)喘沿,在調(diào)用函數(shù)時(shí),Python解釋器會自動按照參數(shù)位置和參數(shù)名把對應(yīng)的參數(shù)傳進(jìn)去竭贩。
此處還可以用tuple和dict調(diào)用上述函數(shù)蚜印,使用方式如下:

args = (1, 2, 3, 4)      #定義tuple
kw = {'x': 8, 'y': '9'}  #定義dict
exp(*args, **kw)

執(zhí)行結(jié)果如下:

p1 = 1 p2= 2 df= 3 vart= (4,) kw = {'y': '9', 'x': 8}

由執(zhí)行結(jié)果看到,任意函數(shù)都可以通過類似func(args,*kw)的形式調(diào)用留量,無論參數(shù)是如何定義的窄赋。

4 執(zhí)行流程

我們前面列舉了不少函數(shù)的示例,不過對于函數(shù)的執(zhí)行流程還需要進(jìn)一步了解肪获,以便在后續(xù)章節(jié)中學(xué)習(xí)得更輕松寝凌。
為了保證函數(shù)的定義先于首次調(diào)用執(zhí)行,我們需要知道語句的執(zhí)行順序孝赫,即執(zhí)行流程较木。
執(zhí)行總是從程序的第一行代碼開始,從上到下青柄、從左到右伐债,按順序依次執(zhí)行第一條語句预侯。
函數(shù)定義并不會改變程序的執(zhí)行流程,不過函數(shù)代碼塊中的語句并不是立即執(zhí)行峰锁,而是等函數(shù)被程序調(diào)用時(shí)才執(zhí)行萎馅。
函數(shù)調(diào)用可以看作程序執(zhí)行流程中的一個迂回路徑,遇到函數(shù)調(diào)用時(shí)虹蒋,并不會直接繼續(xù)執(zhí)行下一條語句糜芳,而是跳到函數(shù)體的第一行,繼續(xù)執(zhí)行完函數(shù)代碼塊中的所有語句魄衅,再跳回原來離開的地方峭竣。
這樣看似比較簡單,但是會發(fā)現(xiàn)函數(shù)代碼塊中可以調(diào)用其他函數(shù)晃虫,當(dāng)程序流程運(yùn)行到一個函數(shù)時(shí)皆撩,可能需要執(zhí)行其他函數(shù)中的語句。但當(dāng)執(zhí)行這個函數(shù)的語句時(shí)哲银,又可能需要調(diào)用執(zhí)行另一個函數(shù)的語句扛吞。
幸好Python對于程序運(yùn)行到哪里有很好的記錄,所以在每個函數(shù)執(zhí)行結(jié)束后荆责,程序都能跳回它離開的地方滥比,直到執(zhí)行到整個程序的結(jié)尾才會結(jié)束。
當(dāng)我們看別人的Python代碼時(shí)做院,不一定要一行一行按照書寫順序閱讀守呜,有時(shí)按照執(zhí)行的流程閱讀更好理解代碼的含義。

5 形參和實(shí)參

我們前面已經(jīng)講述過函數(shù)的參數(shù)山憨,將給大家介紹Python函數(shù)的兩種類型參數(shù),一種是函數(shù)定義里的形參弥喉,一種是調(diào)用函數(shù)時(shí)傳入的實(shí)參郁竟。
經(jīng)常在使用一些內(nèi)置函數(shù)時(shí)需要傳入?yún)?shù),如調(diào)用math.sin時(shí)由境,需要傳入一個整型數(shù)字作為實(shí)參棚亩。有的函數(shù)需要多個參數(shù),如math.pow需要兩個參數(shù)虏杰,一個是基數(shù)(base)讥蟆,另一個是指數(shù)(exponent)
在函數(shù)內(nèi)部纺阔,會將實(shí)參的值賦給形參瘸彤,例如:

#! /usr/bin/python3
# -*- coding:UTF-8 -*-

def personinfo(age,name):
    print('年齡:',age)
    print('名稱:',name)
    return

在該函數(shù)中,函數(shù)名personinfo后面的參數(shù)列表age和name就是實(shí)參笛钝,在函數(shù)體中分別將age和name的值傳遞給age和name质况,函數(shù)體中的age和name就是形參愕宋。
提示:在函數(shù)體內(nèi)都是對形參進(jìn)行操作,不能操作實(shí)參结榄,即對實(shí)參做出更改中贝。
內(nèi)置函數(shù)的組合規(guī)則在自定義函數(shù)上同樣適用。例如臼朗,我們對自定義的personinfo函數(shù)可以使用任何表達(dá)式作為實(shí)參:

personinfo(21,'小萌'*2)

執(zhí)行結(jié)果如下:

年齡: 21
名稱: 小萌小萌

由執(zhí)行結(jié)果看到邻寿,可以用字符串的乘法表達(dá)式作為實(shí)參
在Python中视哑,作為實(shí)參的表達(dá)式會在函數(shù)調(diào)用前執(zhí)行绣否。例如,在上面的示例中黎炉,實(shí)際上先執(zhí)行'小萌'*2的操作枝秤,將執(zhí)行的結(jié)果作為一個實(shí)參傳遞到函數(shù)體中。
提示:作為實(shí)參傳入函數(shù)的變量名稱和函數(shù)定義里形參的名字沒有關(guān)系慷嗜。函數(shù)只關(guān)心形參的值淀弹,而不關(guān)心它在調(diào)用前叫什么名字。

變量作用域

簡單來說庆械,作用域就是一個變量的命名空間薇溃。在Python中,程序的變量并不是在任何位置都可以訪問的缭乘,訪問權(quán)限決定于這個變量是在哪里賦值的沐序,代碼中變量被賦值的位置決定哪些范圍的對象可以訪問這個變量,這個范圍就是命名空間堕绩。
變量的作用域決定哪一部分程序可以訪問特定的變量名稱策幼。Python中有兩種最基本的變量作用域:局部變量和全局變量。
下面我們分別對兩種作用域的變量進(jìn)行介紹奴紧。

1 局部變量

在函數(shù)內(nèi)定義的變量名只能被函數(shù)內(nèi)部引用特姐,不能在函數(shù)外引用,這個變量的作用域是局部的黍氮,也稱為局部變量唐含。
定義的變量如果是在函數(shù)體中第一次出現(xiàn),就是局部變量沫浆,例如:

#! /usr/bin/python3
# -*- coding:UTF-8 -*-
def func():
x = 100
print x

在func函數(shù)中捷枯,x是在函數(shù)體中被定義的,并且是第一次出現(xiàn)专执,所以x是局部變量淮捆。
局部變量只能在函數(shù)體中被訪問,超出函數(shù)體的范圍訪問就會報(bào)錯,例如:

#! /usr/bin/python3
# -*- coding:UTF-8 -*-

def func():
    x=100
    print ('變量x:%s' % x)
print('函數(shù)體外訪問變量x:%s' % x)
func()

函數(shù)執(zhí)行結(jié)果如下:

Traceback (most recent call last):
  File "D:/python/workspace/functiondef.py", line 7, in <module>
    print('函數(shù)體外訪問變量x:%s' % (x))
NameError: name 'x' is not defined

執(zhí)行結(jié)果告訴我們争剿,第7行的x沒有定義已艰;由輸入代碼可知,第7行語句沒有在函數(shù)體中蚕苇,因而執(zhí)行時(shí)報(bào)錯了哩掺。
如果把x作為實(shí)參傳入函數(shù)體中,在函數(shù)體中不定義變量x涩笤,會將x認(rèn)為是怎樣的變量呢嚼吞?定義如下函數(shù)并執(zhí)行:

#! /usr/bin/python3
# -*- coding:UTF-8 -*-

def func(x):
    print ('局部變量x 為',x)
func(10)

函數(shù)執(zhí)行結(jié)果如下:

局部變量x為: 10

由執(zhí)行結(jié)果看到,輸出了局部變量的值蹬碧。這里有一個疑問舱禽,在函數(shù)體中沒有定義局部變量,x只是作為一個實(shí)參傳入函數(shù)體中恩沽,怎么變成局部變量了呢誊稚?這是因?yàn)閰?shù)的工作原理類似于局部變量,一旦進(jìn)入函數(shù)體罗心,就成為局部變量了里伯。
如果在函數(shù)外定義了變量x并賦值,在函數(shù)體中能否使用x呢渤闷?定義如下函數(shù)并執(zhí)行:

#! /usr/bin/python3
# -*- coding:UTF-8 -*-

x = 50
def func():
    print('x 等于', x)

執(zhí)行結(jié)果如下:

x 等于50

由執(zhí)行結(jié)果看到疾瓮,在函數(shù)體中可以直接使用函數(shù)體外的變量。
如果在函數(shù)外定義了變量x并賦值飒箭,將x作為函數(shù)的實(shí)參狼电,在函數(shù)體中更改x的值,函數(shù)體外x的值是否跟著變更呢弦蹂?定義如下函數(shù)并執(zhí)行:

#! /usr/bin/python3
# -*- coding:UTF-8 -*-

x = 50
def func(x):
    print('x 等于', x)
    x = 2
    print('局部變量x 變?yōu)?, x)
func(x)
print('x 一直是', x)

執(zhí)行結(jié)果如下:

x 等于 50
局部變量x變?yōu)?2
x 一直是 50

由輸出結(jié)果看到肩碟,在函數(shù)體中更改變量的值并不會更改函數(shù)體外變量的值。這是因?yàn)檎{(diào)用func函數(shù)時(shí)創(chuàng)建了新的命名空間凸椿,它作用于func函數(shù)的代碼塊腾务。賦值語句x=2只在函數(shù)體的作用域內(nèi)起作用,不能影響外部作用域中的x削饵。可以看到未巫,函數(shù)外部調(diào)用x時(shí)窿撬,它的值并沒有改變。

2 全局變量

在函數(shù)外叙凡,一段代碼最開始賦值的變量可以被多個函數(shù)引用劈伴,這就是全局變量。全局變量可以在整個程序范圍內(nèi)訪問。
我們在前面已經(jīng)使用過全局變量跛璧,7.6.1小節(jié)中x=50就是全局變量严里。下面看一個全局變量的示例:

#! /usr/bin/python3
# -*- coding:UTF-8 -*-

total = 0; # 這是一個全局變量
def sum( arg1, arg2 ):
    total = arg1 + arg2; # total 在這里是局部變量.
    print ("函數(shù)內(nèi)是局部變量 : ", total)
    return total

def totalprint():
    print('total 的值是',total)
    return total

print('函數(shù)求和結(jié)果:',sum( 10, 20 ))
totalprint()
print ("函數(shù)外是全局變量 : ", total)

執(zhí)行結(jié)果如下:

函數(shù)內(nèi)是局部變量 : 30
函數(shù)求和結(jié)果: 30
total的值是 0
函數(shù)外是全局變量 : 0

由執(zhí)行結(jié)果看到,全局變量可在全局使用追城,在函數(shù)體中更改全局變量的值不會影響全局變量在其他函數(shù)或語句中的使用刹碾。
我們再看一個函數(shù)定義并執(zhí)行的示例:

#! /usr/bin/python3
# -*- coding:UTF-8 -*-

num = 100
def func():
    num = 200
    print('函數(shù)體中num的值為:',num)

func()
print('函數(shù)外num的值為:',num)

函數(shù)執(zhí)行結(jié)果為:

函數(shù)體中num的值為: 200
函數(shù)外num的值為: 100

由輸出結(jié)果看到,我們定義了一個名為num的全局變量座柱,在函數(shù)體中也定義了一個名為num的全局變量迷帜,在函數(shù)體中使用的是函數(shù)體中的num變量,在函數(shù)體外使用num變量時(shí)使用的是全局變量的值色洞。
由此我們得知:函數(shù)中使用某個變量時(shí)戏锹,如果該變量名既有全局變量又有局部變量,就默認(rèn)使用局部變量火诸。
要將全局變量變?yōu)榫植孔兞拷跽耄恍柙诤瘮?shù)體中定義一個和局部變量名稱一樣的變量即可。能否將函數(shù)體中的局部變量變?yōu)槿肿兞磕刂檬瘢慷x如下函數(shù)并執(zhí)行:

#! /usr/bin/python3
# -*- coding:UTF-8 -*-

num = 100
print('函數(shù)調(diào)用前num的值為:',num)
def func():
    global num
    num = 200
    print('函數(shù)體中num的值為:',num)
func()
print('函數(shù)調(diào)用結(jié)束后num的值為:',num)

函數(shù)執(zhí)行結(jié)果如下:

函數(shù)調(diào)用前num的值為: 100
函數(shù)體中num的值為: 200
函數(shù)調(diào)用結(jié)束后num的值為: 200

由函數(shù)定義及執(zhí)行結(jié)果看到奈搜,在函數(shù)體中的變量num前加了一個global關(guān)鍵字后,函數(shù)調(diào)用結(jié)束后盾碗,在函數(shù)外使用num變量時(shí)媚污,值變?yōu)楹秃瘮?shù)體中的值一樣了杨凑。
由此我們得知:要在函數(shù)中將某個變量定義為全局變量桨醋,在需要被定義的變量前加一個關(guān)鍵字global即可侦讨。
在函數(shù)體中定義global變量后观堂,在函數(shù)體中對變量做的其他操作也是全局性的洒缀。定義如下函數(shù)并執(zhí)行:

#! /usr/bin/python3
# -*- coding:UTF-8 -*-

num = 100
print('函數(shù)調(diào)用前num的值為:',num)
def func():
    global num
    num = 200
    num += 100
    print('函數(shù)體中num的值為:',num)

func()
print('函數(shù)調(diào)用結(jié)束后num的值為:',num)

函數(shù)執(zhí)行結(jié)果如下:

函數(shù)調(diào)用前num的值為: 100
函數(shù)體中num的值為: 300
函數(shù)調(diào)用結(jié)束后num的值為: 300

由執(zhí)行結(jié)果看到艰山,在函數(shù)體中對定義的全局變量num做了一次加100的操作醉锅,num的值由原來的200變?yōu)?00侣签,在函數(shù)體外獲得的num的值也變?yōu)?00了芥玉。

有返回值和無返回值函數(shù)

前面在定義函數(shù)時(shí)蛇摸,有些函數(shù)使用了return語句,有些函數(shù)沒有使用return語句灿巧,使用return語句與不使用return語句有什么區(qū)別呢赶袄?
由前面學(xué)習(xí)我們知道,若定義函數(shù)時(shí)沒有使用return語句抠藕,則默認(rèn)返回一個None饿肺。要返回一個None,可以只寫一個return盾似,但要返回具體的數(shù)值敬辣,就需要在return后面加上需要返回的內(nèi)容。對于函數(shù)的定義來說,使用return語句可以向外提供該函數(shù)執(zhí)行的一些結(jié)果溉跃;對于函數(shù)的調(diào)用者來說村刨,是否可以使用函數(shù)中執(zhí)行的一些操作結(jié)果,就在于函數(shù)是否使用return語句返回了對應(yīng)的執(zhí)行結(jié)果撰茎。
在Python中嵌牺,有的函數(shù)會產(chǎn)生結(jié)果(如數(shù)學(xué)函數(shù)),我們稱這種函數(shù)為有返回值函數(shù)(fruitful function)乾吻;有的函數(shù)執(zhí)行一些動作后不返回任何值髓梅,我們稱這類函數(shù)為無返回值函數(shù)。
當(dāng)我們調(diào)用有返回值函數(shù)時(shí)绎签,可以使用返回的結(jié)果做相關(guān)操作枯饿;當(dāng)我們使用無返回值或返回None的函數(shù)時(shí),只能得到一個None值诡必。
比如定義如下函數(shù)并執(zhí)行:

#! /usr/bin/python3
# -*- coding:UTF-8 -*-

def noreturn():
    print('noreturn 函數(shù)不寫return 語句')

def justreturn():
    print('justreturn 函數(shù)只寫return奢方,不返回具體內(nèi)容')

def returnval():
    x=10
    y=20
    z=x+y
    print('returnval 函數(shù)寫return 語句,并返回求和的結(jié)果爸舒。')
    return z

print('函數(shù)noreturn 調(diào)用結(jié)果:',noreturn())
print('函數(shù)returnval 調(diào)用結(jié)果:',returnval())

函數(shù)執(zhí)行結(jié)果如下:

noreturn 函數(shù)不寫return 語句
函數(shù)noreturn 調(diào)用結(jié)果: None
justreturn函數(shù)只寫return蟋字,不返回具體內(nèi)容
函數(shù)justreturn 調(diào)用結(jié)果: None
returnval 函數(shù)寫return 語句,并返回求和的結(jié)果扭勉。
函數(shù)returnval 調(diào)用結(jié)果: 30

由執(zhí)行結(jié)果看到鹊奖,定義函數(shù)時(shí)不寫return或只寫一個return語句返回的都是None。如果寫了返回具體內(nèi)容涂炎,調(diào)用函數(shù)時(shí)就可以獲取具體內(nèi)容忠聚。

為什么要有函數(shù)

隨著函數(shù)學(xué)習(xí)的不斷深入,不知你是否有這樣的疑問唱捣,為什么要有函數(shù)两蟀,定義函數(shù)的好處在哪里?
我們前幾章都是在交互模式下編碼的震缭,代碼量不大赂毯,操作也不復(fù)雜,在交互模式下操作沒什么問題拣宰,唯一一點(diǎn)就是不能保存操作記錄党涕。隨著代碼量越來越大,在交互模式下操作就不方便了巡社,后面我們引入了在文本中編輯程序遣鼓,在cmd命令下執(zhí)行的方式。
使用文本結(jié)合cmd命令的方式可以幫助我們記錄歷史記錄重贺,并能更簡潔地進(jìn)行代碼的編輯。不過在第6章的學(xué)習(xí)中我們體會到,代碼行數(shù)達(dá)到一定量時(shí)气笙,把所有代碼都放在一起的方式寫起來和看起來都有一些難度次企。
引入函數(shù)后,在編寫代碼的過程中潜圃,可以將一些實(shí)現(xiàn)寫成對應(yīng)的函數(shù)缸棵,通過調(diào)用函數(shù)做后續(xù)操作,并且可以重復(fù)調(diào)用谭期,使得代碼更簡潔堵第、易讀,一些代碼也可以重復(fù)使用了隧出。
對函數(shù)的好處概括如下:
(1)新建一個函數(shù)踏志,讓我們有機(jī)會為一組語句命名,成為一個代碼塊胀瞪,這樣更有利于閱讀代碼针余,并且組織后的代碼更容易調(diào)試。
(2)函數(shù)方法可以減少重復(fù)代碼的使用凄诞,讓程序代碼總行數(shù)更少圆雁,之后修改代碼時(shí)只需要少量修改就可以了。
(3)將一個很長的代碼片段拆分成幾個函數(shù)后帆谍,可以對每一個函數(shù)進(jìn)行單獨(dú)調(diào)試伪朽,單個函數(shù)調(diào)試通過后,再將它們組合起來形成一個完整的產(chǎn)品汛蝙。
(4)一個設(shè)計(jì)良好的函數(shù)可以在很多程序中復(fù)用烈涮,不需要重復(fù)編寫。

返回函數(shù)

我們前面講解了函數(shù)可以有返回值患雇,除了返回值跃脊,函數(shù)中是否可以返回函數(shù)呢?
例如苛吱,函數(shù)定義如下:

#! /usr/bin/python3
# -*- coding:UTF-8 -*-

def calc_sum(*args):
    ax = 0
    for n in args:
         ax = ax + n
    return ax

這里定義了一個可變參數(shù)的求和函數(shù)酪术,該函數(shù)允許傳入多個參數(shù),最后返回求得的和翠储。如果不需要立刻求和绘雁,而是在后面的代碼中根據(jù)需要再計(jì)算,怎么辦呢援所?例如庐舟,函數(shù)定義如下:

#! /usr/bin/python3
# -*- coding:UTF-8 -*-

def sum_late(*args):
    def calc_sum():
         ax = 0
         for n in args:
             ax = ax + n
         return ax
    return calc_sum

可以看到,此處返回了一個我們之前沒有看過的類型的值住拭,是返回了一個函數(shù)嗎挪略?是的历帚,此處確實(shí)返回了一個函數(shù)。對于此處定義的函數(shù)杠娱,我們沒有返回求和的結(jié)果挽牢,而是返回了一個求和函數(shù)。
操作執(zhí)行函數(shù):
'''
print('調(diào)用sum_late 的結(jié)果:',sum_late(1,2,3,4))
calc_sum=sum_late(1,2,3,4)
print('調(diào)用calc_sum 的結(jié)果:',calc_sum())
'''
得到函數(shù)的執(zhí)行結(jié)果如下:

調(diào)用sum_late 的結(jié)果: <function sum_late.<locals>.calc_sum at 0x000000000077DE18>
調(diào)用calc_sum 的結(jié)果: 10

由執(zhí)行結(jié)果看到摊求,調(diào)用定義的函數(shù)時(shí)沒有直接返回求和結(jié)果禽拔,而是返回了一串字符(這個字符其實(shí)就是函數(shù))。當(dāng)執(zhí)行返回的函數(shù)時(shí)室叉,才真正計(jì)算求和的結(jié)果睹栖。
在這個例子中,在函數(shù)sum_late中又定義了函數(shù)calc_sum茧痕,并且內(nèi)部函數(shù)calc_sum可以引用外部函數(shù)sum_late的參數(shù)和局部變量野来。當(dāng)sum_late返回函數(shù)calc_sum時(shí),相關(guān)參數(shù)和變量都保存在返回的函數(shù)中凿渊,稱為閉包(Closure)梁只。這種程序結(jié)構(gòu)威力極大。
有一點(diǎn)需要注意埃脏,當(dāng)調(diào)用sum_late()函數(shù)時(shí)搪锣,每次調(diào)用都會返回一個新的函數(shù),即使傳入相同的參數(shù)也是如此彩掐,例如:

f1=sum_late(1,2,3)
f2=sum_late(1,2,3)
print('f1==f2 的結(jié)果為:',f1==f2)

執(zhí)行結(jié)果如下:

f1==f2 的結(jié)果為: False

由執(zhí)行結(jié)果看到构舟,返回的函數(shù)f1和f2不同。
我們在此處提到了閉包(Closure)堵幽,什么是閉包呢狗超?
閉包的定義:如果在一個內(nèi)部函數(shù)里對外部函數(shù)(不是在全局作用域)的變量進(jìn)行引用,內(nèi)部函數(shù)就被認(rèn)為是閉包朴下。
在上面的示例中努咐,返回的函數(shù)在定義內(nèi)部引用了局部變量args,當(dāng)函數(shù)返回一個函數(shù)后殴胧,內(nèi)部的局部變量會被新函數(shù)引用渗稍。
我們定義一個函數(shù):

#! /usr/bin/python3
# -*- coding:UTF-8 -*-

def count():
    fs = []
    for i in range(1, 4):
         def f():
              return i*i
         fs.append(f)
    return fs
f1, f2, f3 = count()

該示例中,每次循環(huán)都會創(chuàng)建一個新函數(shù)团滥,最后把創(chuàng)建的3個函數(shù)都返回了竿屹。執(zhí)行該函數(shù)得到結(jié)果是怎樣的呢?調(diào)用f1()灸姊、f2()和f3()的結(jié)果是1拱燃、4、9嗎力惯?

我們?nèi)缦聢?zhí)行函數(shù):

print('f1 的結(jié)果是:',f1())
print('f2 的結(jié)果是:',f2())
print('f3 的結(jié)果是:',f3())

執(zhí)行結(jié)果如下:

f1 的結(jié)果是: 9
f2 的結(jié)果是: 9
f3 的結(jié)果是: 9

由執(zhí)行結(jié)果看到碗誉,3個函數(shù)返回的結(jié)果都是9召嘶,怎么全是9呢?
原因在于返回的函數(shù)引用了變量i哮缺,但它并非立刻執(zhí)行苍蔬。等到3個函數(shù)都返回時(shí),它們所引用的變量i已經(jīng)變成了3蝴蜓,因此最終結(jié)果為9。
注意:返回閉包時(shí)俺猿,返回函數(shù)不要引用任何循環(huán)變量或后續(xù)會發(fā)生變化的變量茎匠,否則很容易出現(xiàn)你意想不到的問題。

如果一定要引用循環(huán)變量怎么辦呢押袍?

我們定義如下函數(shù)并執(zhí)行:

#! /usr/bin/python3
# -*- coding:UTF-8 -*-

def count():
    def f(j):
         def g():
             return j*j
         return g
    fs = []
    for i in range(1, 4):
         fs.append(f(i)) # f(i)立刻被執(zhí)行诵冒,因此i的當(dāng)前值被傳入f()
    return fs

f1, f2, f3 = count()
print('f1 的結(jié)果是:',f1())
print('f2 的結(jié)果是:',f2())
print('f3 的結(jié)果是:',f3())

函數(shù)執(zhí)行結(jié)果如下:

f1 的結(jié)果是: 1
f2 的結(jié)果是: 4
f3 的結(jié)果是: 9

由執(zhí)行結(jié)果看到,這次輸出結(jié)果和我們預(yù)期的一致谊惭。此處的代碼看起來有點(diǎn)費(fèi)力汽馋,大家可以想想其他更好的辦法。

遞歸函數(shù)

前面學(xué)習(xí)了在函數(shù)中返回函數(shù)圈盔,也學(xué)習(xí)了在一個函數(shù)中調(diào)用另一個函數(shù)豹芯,函數(shù)是否可以調(diào)用自己呢?答案是可以的驱敲。如果一個函數(shù)在內(nèi)部調(diào)用自身铁蹈,這個函數(shù)就稱作遞歸函數(shù)。
遞歸函數(shù)的簡單定義如下:

def recurision():
 return recursion()

這只是一個簡單定義众眨,什么也做不了握牧。
當(dāng)然,你可以嘗試會發(fā)生什么結(jié)果娩梨。理論上會永遠(yuǎn)運(yùn)行下去沿腰,但實(shí)際操作時(shí)可能不一會兒程序就崩潰了(發(fā)生異常)。因?yàn)槊看握{(diào)用函數(shù)都會用掉一點(diǎn)內(nèi)存狈定,在足夠多的函數(shù)調(diào)用發(fā)生后颂龙,空間幾乎被占滿,程序就會報(bào)異常掸冤。
這類遞歸被稱作無窮遞歸(infinite recursion)厘托,理論上永遠(yuǎn)不結(jié)束。當(dāng)然稿湿,我們需要能實(shí)際做事情的函數(shù)铅匹,有用的遞歸函數(shù)應(yīng)該滿足如下條件:
(1)當(dāng)函數(shù)直接返回值時(shí)有基本實(shí)例(最小可能性問題)。
(2)遞歸實(shí)例饺藤,包括一個或多個問題最小部分的遞歸調(diào)用包斑。
使用遞歸關(guān)鍵在于將問題分解為小部分流礁,遞歸不能永遠(yuǎn)繼續(xù)下去,因?yàn)樗偸且宰钚】赡苄詥栴}結(jié)束罗丰,而這些問題又存儲在基本實(shí)例中神帅。
函數(shù)調(diào)用自身怎么實(shí)現(xiàn)呢?
其實(shí)函數(shù)每次被調(diào)用時(shí)都會創(chuàng)建一個新命名空間萌抵,也就是當(dāng)函數(shù)調(diào)用“自身”時(shí)找御,實(shí)際上運(yùn)行的是兩個不同的函數(shù)(也可以說一個函數(shù)具有兩個不同的命名空間)。
我們來看一個遞歸示例绍填,計(jì)算階乘n!=1×2×3×…×n霎桅,用函數(shù)fact(n)表示,可以看出:

fact(n) = n! = 1×2×3×…×(n-1)×n = (n-1)! x n = fact(n-1)×n

所以讨永,fact(n)可以表示為n×fact(n-1)滔驶,只有n=1時(shí)需要特殊處理。
于是卿闹,fact(n)用遞歸方式定義函數(shù)如下:

#! /usr/bin/python3
# -*- coding:UTF-8 -*-

def fact(n):
    if n==1:
         return 1
    return n * fact(n - 1)

執(zhí)行該函數(shù):

print('調(diào)用遞歸函數(shù)執(zhí)行結(jié)果為:',fact(5))

執(zhí)行結(jié)果如下:

調(diào)用遞歸函數(shù)執(zhí)行結(jié)果為: 120

由執(zhí)行結(jié)果看到揭糕,函數(shù)已正確輸出5的階乘的結(jié)果。
計(jì)算fact(5)時(shí)可以根據(jù)函數(shù)定義看到計(jì)算過程:

===> fact(5)
===> 5 * fact(4)
===> 5 * (4 * fact(3))
===> 5 * (4 * (3 * fact(2)))
===> 5 * (4 * (3 * (2 * fact(1))))
===> 5 * (4 * (3 * (2 * 1)))
===> 5 * (4 * (3 * 2))
===> 5 * (4 * 6)
===> 5 * 24
===> 120

由函數(shù)定義可以得知锻霎,遞歸函數(shù)的優(yōu)點(diǎn)是定義簡單著角、邏輯清晰。
理論上量窘,所有遞歸函數(shù)都可以寫成循環(huán)的方式雇寇,不過循環(huán)的邏輯不如遞歸清晰。
使用遞歸函數(shù)需要注意防止棧溢出蚌铜。在計(jì)算機(jī)中锨侯,函數(shù)調(diào)用是通過棧(stack)這種數(shù)據(jù)結(jié)構(gòu)實(shí)現(xiàn)的。每當(dāng)進(jìn)入一個函數(shù)調(diào)用冬殃,棧就會加一層棧幀囚痴;每當(dāng)函數(shù)返回,棧就會減一層棧幀审葬。由于棧的大小不是無限的深滚,因此遞歸調(diào)用的次數(shù)過多會導(dǎo)致棧溢出』辆酰可以試試fact(1000)痴荐,執(zhí)行結(jié)果如下:

Traceback (most recent call last):
  File "D:/python/workspace/functiondef.py", line 271, in <module>
    print('調(diào)用遞歸函數(shù)執(zhí)行結(jié)果為:',fact(1000))
  File "D:/python/workspace/functiondef.py", line 269, in fact
    return n * fact(n - 1)
  File "D:/python/workspace/functiondef.py", line 269, in fact
    return n * fact(n - 1)
  . . .
  File "D:/python/workspace/functiondef.py", line 267, in fact
    if n==1:
RecursionError: maximum recursion depth exceeded in comparison

由執(zhí)行結(jié)果看到,執(zhí)行出現(xiàn)異常官册,異常提示超過最大遞歸深度生兆。
這個問題怎么解決呢?
解決遞歸調(diào)用棧溢出的方法是通過尾遞歸優(yōu)化膝宁,事實(shí)上尾遞歸和循環(huán)的效果一樣鸦难,把循環(huán)看成一種特殊尾遞歸函數(shù)也可以根吁。
尾遞歸是指在函數(shù)返回時(shí)調(diào)用函數(shù)本身,并且return語句不能包含表達(dá)式合蔽。這樣击敌,編譯器或解釋器就可以對尾遞歸進(jìn)行優(yōu)化,使遞歸本身無論調(diào)用多少次都只占用一個棧幀拴事,從而避免棧溢出的情況沃斤。
由于上面的fact(n)函數(shù)return n * fact(n - 1)引入了乘法表達(dá)式,因此不是尾遞歸刃宵。要改成尾遞歸方式需要多一點(diǎn)代碼轰枝,主要是把每一步乘積傳入遞歸函數(shù)中,看如下函數(shù)定義方式:

#! /usr/bin/python3
# -*- coding:UTF-8 -*-

def fact(n):
    return fact_iter(n, 1)

def fact_iter(num, product):
    if num == 1:
         return product
    return fact_iter(num - 1, num * product)

可以看到组去,return fact_iter(num - 1, num * product)僅返回遞歸函數(shù)本身,num - 1和num * product在函數(shù)調(diào)用前就會被計(jì)算步淹,不影響函數(shù)調(diào)用从隆。
fact(5)對應(yīng)的fact_iter(5, 1)的調(diào)用如下:

===> fact_iter(5, 1)
===> fact_iter(4, 5)
===> fact_iter(3, 20)
===> fact_iter(2, 60)
===> fact_iter(1, 120)
===> 120

由操作結(jié)果看到,調(diào)用尾遞歸時(shí)如果做了優(yōu)化缭裆,棧就不會增長键闺,因此無論多少次調(diào)用都不會導(dǎo)致棧溢出。

匿名函數(shù)

什么是匿名函數(shù)呢澈驼?
匿名函數(shù)就是不再使用def語句這樣的標(biāo)準(zhǔn)形式定義一個函數(shù)辛燥。
Python使用lambda創(chuàng)建匿名函數(shù)。
lambda只是一個表達(dá)式缝其,函數(shù)體比def簡單很多挎塌。
lambda的主體是一個表達(dá)式,而不是一個代碼塊内边,僅能在lambda表達(dá)式中封裝有限的邏輯榴都。lambda函數(shù)擁有自己的命名空間,不能訪問自有參數(shù)列表之外或全局命名空間的參數(shù)漠其。
lambda函數(shù)的語法只包含一個語句嘴高,語句如下:

lambda [arg1 [,arg2,.....argn]]:expression

匿名函數(shù)應(yīng)該如何應(yīng)用呢?
先看一個求兩個數(shù)的和的示例和屎。
使用def語句:

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

使用lambda表達(dá)式:

lambda x,y: x+y

由上面的代碼可以看到拴驮,使用lambda表達(dá)編寫的代碼比使用def語句少。這里不太明顯柴信,再看一個代碼更多的示例套啤。
比如求一個列表中大于3的元素。
通過過程式編程實(shí)現(xiàn)颠印,也是常規(guī)的方法纲岭。在交互模式下輸入如下:

>>> L1=[1,2,3,4,5]
>>> L2=[]
>>> for i in L1:
               if i>3:
                    L2.append(i)

>>> print('列表中大于3 的元素有:',L2)
列表中大于3 的元素有: [4, 5]

通過函數(shù)式編程實(shí)現(xiàn)抹竹,運(yùn)用filter,給出一個判斷條件:

def func(x):
    return x>3
f_list=filter(func,[1,2,3,4,5])
print('列表中大于3 的元素有:',[item for item in f_list])

執(zhí)行結(jié)果如下:

列表中大于3 的元素有: [4, 5]

如果運(yùn)用匿名函數(shù)止潮,就會更加精簡窃判,一行代碼即可:

print('列表中大于3 的元素有:',[item for item in filter(lambda x:x>3,[1,2,3,4,5])])

執(zhí)行結(jié)果如下:

列表中大于3 的元素有: [4, 5]

從上面的操作可以看出,lambda一般應(yīng)用于函數(shù)式編程喇闸,代碼簡潔袄琳,常和filter等函數(shù)結(jié)合使用。
我們對上面使用lambda的示例進(jìn)行解析燃乍。
在表達(dá)式中:

x 為lambda 函數(shù)的一個參數(shù)唆樊。
:為分割符。
x>3 則是返回值刻蟹,在lambda 函數(shù)中不能有return逗旁,其實(shí)冒號(:)后面就是返回值。

item for item in filter是Python 3中filter函數(shù)的取值方式舆瘪,因?yàn)閺腜ython 3起片效,filter函數(shù)返回的對象從列表改為迭代器(filter object)。filter
object支持迭代操作英古,比如for循環(huán):

for item in a_filter_object:
 print(item)

如果還是需要一個列表淀衣,就可以這樣得到它:

filter_list = [item for item in a_filter_object]

由這些示例可以看到,匿名函數(shù)確實(shí)有它的優(yōu)點(diǎn)召调。
這里有一個疑問膨桥,在什么情況下使用匿名函數(shù)呢?
一般以下情況多考慮使用匿名函數(shù):
(1)程序一次性使用、不需要定義函數(shù)名時(shí)唠叛,用匿名函數(shù)可以節(jié)省內(nèi)存中變量定義空間只嚣。
(2)如果想讓程序更加簡潔,使用匿名函數(shù)就可以做到艺沼。
當(dāng)然介牙,匿名函數(shù)有3個規(guī)則要記住:
(1)一般有一行表達(dá)式,必須有返回值澳厢。
(2)不能有return环础。
(3)可以沒有參數(shù),也可以有一個或多個參數(shù)剩拢。
下面看幾個匿名函數(shù)的示例(在交互模式下輸入)线得。
無參匿名函數(shù):

>>> t = lambda : True #分號前無任何參數(shù)
>>> t()
True

帶參數(shù)匿名函數(shù):

>>> lambda x: x**3 #一個參數(shù)
>>> lambda x,y,z:x+y+z #多個參數(shù)
>>> lambda x,y=3: x*y #允許參數(shù)存在默認(rèn)值

匿名函數(shù)調(diào)用:

>>> c = lambda x,y,z: x*y*z
>>> c(2,3,4)
24
>>> c = lambda x,y=2: x+y #使用了默認(rèn)值
>>> c(10) #如果不輸入,就使用默認(rèn)值2
12

偏函數(shù)

偏函數(shù)是從Python 2.5引入的概念徐伐,通過functools模塊被用戶調(diào)用贯钩。注意這里的偏函數(shù)和數(shù)學(xué)意義上的偏函數(shù)不一樣。
偏函數(shù)是將所要承載的函數(shù)作為partial()函數(shù)的第一個參數(shù),原函數(shù)的各個參數(shù)依次作為partial()函數(shù)的后續(xù)參數(shù)角雷,除非使用關(guān)鍵字參數(shù)祸穷。
通過語言描述可能無法理解偏函數(shù)怎么使用,下面舉一個常見的例子說明勺三。在這個例子里雷滚,將實(shí)現(xiàn)一個取余函數(shù),取得整數(shù)100對不同數(shù)m的100%m的余數(shù)吗坚。編寫代碼如下:

#! /usr/bin/python3
# -*- coding:UTF-8 -*-

from functools import partial

def mod( n, m ):
  return n % m

mod_by_100 = partial( mod, 100 )

print('自定義函數(shù)祈远,100 對7 取余結(jié)果為:',mod( 100, 7 ))
print('調(diào)用偏函數(shù),100 對7 取余結(jié)果為:',mod_by_100( 7 ))

函數(shù)執(zhí)行結(jié)果為:

自定義函數(shù)商源,100 對7 取余結(jié)果為: 2
調(diào)用偏函數(shù)车份,100 對7 取余結(jié)果為: 2

由執(zhí)行結(jié)果看到,使用偏函數(shù)所需代碼量比自定義函數(shù)更少牡彻、更簡潔扫沼。
在介紹函數(shù)的參數(shù)時(shí),我們講到通過設(shè)定參數(shù)的默認(rèn)值可以降低函數(shù)調(diào)用的難度庄吼。從上面的示例來看充甚,偏函數(shù)也可以做到這一點(diǎn)。

經(jīng)典排序之快速排序?qū)崿F(xiàn)

快速排序(quick sort)是一種分治排序算法霸褒。該算法首先選取一個劃分元素(partition element,也稱為pivot)盈蛮;然后重排列表废菱,將其劃分為3部分,即left(小于劃分元素pivot的部分)抖誉、pivot(劃分元素)殊轴、right(大于劃分元素pivot的部分),此時(shí)劃分元素pivot已經(jīng)在列表的最終位置上袒炉;最后分別對left和right兩部分進(jìn)行遞歸排序旁理。
其中,劃分元素的選取直接影響快速排序算法的效率我磁,通常選擇列表的第一個元素孽文、中間元素或最后一個元素作為劃分元素,當(dāng)然也有更復(fù)雜的選擇方式夺艰。劃分過程根據(jù)劃分元素重排列表芋哭,是快速排序算法的關(guān)鍵所在。
快速排序算法的優(yōu)點(diǎn)是原位排序(只使用很小的輔助棧)郁副,平均時(shí)間復(fù)雜度為O(n log n)减牺。快速排序算法的缺點(diǎn)是不穩(wěn)定,最壞情況下時(shí)間復(fù)雜度為O(n2)拔疚。
代碼實(shí)現(xiàn)如下:

#! /usr/bin/python3
# -*- coding:UTF-8 -*-

def quicksort(L):
  qsort(L, 0, len(L) - 1)

def qsort(L, first, last):
  if first < last:
    split = partition(L, first, last)
    qsort(L, first, split - 1)
    qsort(L, split + 1, last)

def partition(L, first, last):
  # 選取列表中的第一個元素作為劃分元素
  pivot = L[first]
  leftmark = first + 1
  rightmark = last
  while True:
  while L[leftmark] <= pivot:
   # 如果列表中存在與劃分元素pivot相等的元素肥隆,讓它位于left 部分
   # 以下檢測用于劃分元素pivot是列表中的最大元素時(shí)
   # 防止leftmark 越界
    if leftmark == rightmark:
      break
    leftmark += 1
  while L[rightmark] > pivot:
    # 這里不需要檢測,劃分元素pivot是列表中的最小元素時(shí)
    # rightmark 自動停在first 處
    rightmark -= 1
  if leftmark < rightmark:
    # 此時(shí)稚失,leftmark 處的元素大于pivot
    # rightmark 處的元素小于等于pivot栋艳,交換兩者
    L[leftmark], L[rightmark] = L[rightmark], L[leftmark]
  else:
    break
# 交換first 處的劃分元素與rightmark 處的元素
L[first], L[rightmark] = L[rightmark], L[first]
# 返回劃分元素pivot的最終位置
return rightmark

函數(shù)調(diào)用示例:

num_list = [5, -4, 6, 3, 7, 11, 1, 2]
print('排序之前: ' + str(num_list))
quicksort(num_list)
print('排序之后: ' + str(num_list))

執(zhí)行結(jié)果如下:

排序之前: [5, -4, 6, 3, 7, 11, 1, 2]
排序之后: [-4, 1, 2, 3, 5, 6, 7, 11]

調(diào)試

前面對調(diào)試的介紹都是基于刻意犯錯進(jìn)行的,本章開始介紹一些調(diào)試技巧墩虹。
將一個大程序分解為小函數(shù)嘱巾,自然引入了調(diào)試的檢查點(diǎn)。如果一個函數(shù)不能正常工作诫钓,可以先考慮以下3點(diǎn):
(1)函數(shù)獲得的實(shí)參有問題旬昭,某個前置條件沒有達(dá)到。
(2)函數(shù)本身有問題菌湃,某個后置條件沒有達(dá)到问拘。
(3)函數(shù)的返回值有問題或使用方式不對。
要檢查第一個問題惧所,可以在函數(shù)體開始處加上print語句骤坐,顯示實(shí)參的值或類型,用于顯示檢查前置條件下愈。
如果實(shí)參沒有問題纽绍,就在每個return語句前添加print語句,顯示返回值势似。如果有可能拌夏,手動檢查返回值,使用更容易檢查結(jié)果的參數(shù)調(diào)用函數(shù)履因。
如果函數(shù)沒有問題障簿,就檢查調(diào)用代碼,確保函數(shù)返回值被正確使用栅迄。
使用中要學(xué)會充分使用print語句站故,該語句能幫我們清晰了解函數(shù)的執(zhí)行流程。

?著作權(quán)歸作者所有,轉(zhuǎn)載或內(nèi)容合作請聯(lián)系作者
  • 序言:七十年代末毅舆,一起剝皮案震驚了整個濱河市西篓,隨后出現(xiàn)的幾起案子,更是在濱河造成了極大的恐慌憋活,老刑警劉巖污淋,帶你破解...
    沈念sama閱讀 216,372評論 6 498
  • 序言:濱河連續(xù)發(fā)生了三起死亡事件,死亡現(xiàn)場離奇詭異余掖,居然都是意外死亡寸爆,警方通過查閱死者的電腦和手機(jī)礁鲁,發(fā)現(xiàn)死者居然都...
    沈念sama閱讀 92,368評論 3 392
  • 文/潘曉璐 我一進(jìn)店門,熙熙樓的掌柜王于貴愁眉苦臉地迎上來赁豆,“玉大人仅醇,你說我怎么就攤上這事∧е郑” “怎么了析二?”我有些...
    開封第一講書人閱讀 162,415評論 0 353
  • 文/不壞的土叔 我叫張陵,是天一觀的道長节预。 經(jīng)常有香客問我叶摄,道長,這世上最難降的妖魔是什么安拟? 我笑而不...
    開封第一講書人閱讀 58,157評論 1 292
  • 正文 為了忘掉前任蛤吓,我火速辦了婚禮,結(jié)果婚禮上糠赦,老公的妹妹穿的比我還像新娘会傲。我一直安慰自己,他們只是感情好拙泽,可當(dāng)我...
    茶點(diǎn)故事閱讀 67,171評論 6 388
  • 文/花漫 我一把揭開白布淌山。 她就那樣靜靜地躺著,像睡著了一般顾瞻。 火紅的嫁衣襯著肌膚如雪泼疑。 梳的紋絲不亂的頭發(fā)上,一...
    開封第一講書人閱讀 51,125評論 1 297
  • 那天荷荤,我揣著相機(jī)與錄音退渗,去河邊找鬼。 笑死梅猿,一個胖子當(dāng)著我的面吹牛,可吹牛的內(nèi)容都是我干的秒裕。 我是一名探鬼主播袱蚓,決...
    沈念sama閱讀 40,028評論 3 417
  • 文/蒼蘭香墨 我猛地睜開眼,長吁一口氣:“原來是場噩夢啊……” “哼几蜻!你這毒婦竟也來了喇潘?” 一聲冷哼從身側(cè)響起,我...
    開封第一講書人閱讀 38,887評論 0 274
  • 序言:老撾萬榮一對情侶失蹤梭稚,失蹤者是張志新(化名)和其女友劉穎颖低,沒想到半個月后,有當(dāng)?shù)厝嗽跇淞掷锇l(fā)現(xiàn)了一具尸體弧烤,經(jīng)...
    沈念sama閱讀 45,310評論 1 310
  • 正文 獨(dú)居荒郊野嶺守林人離奇死亡忱屑,尸身上長有42處帶血的膿包…… 初始之章·張勛 以下內(nèi)容為張勛視角 年9月15日...
    茶點(diǎn)故事閱讀 37,533評論 2 332
  • 正文 我和宋清朗相戀三年,在試婚紗的時(shí)候發(fā)現(xiàn)自己被綠了。 大學(xué)時(shí)的朋友給我發(fā)了我未婚夫和他白月光在一起吃飯的照片莺戒。...
    茶點(diǎn)故事閱讀 39,690評論 1 348
  • 序言:一個原本活蹦亂跳的男人離奇死亡伴嗡,死狀恐怖,靈堂內(nèi)的尸體忽然破棺而出从铲,到底是詐尸還是另有隱情瘪校,我是刑警寧澤,帶...
    沈念sama閱讀 35,411評論 5 343
  • 正文 年R本政府宣布名段,位于F島的核電站阱扬,受9級特大地震影響,放射性物質(zhì)發(fā)生泄漏伸辟。R本人自食惡果不足惜麻惶,卻給世界環(huán)境...
    茶點(diǎn)故事閱讀 41,004評論 3 325
  • 文/蒙蒙 一、第九天 我趴在偏房一處隱蔽的房頂上張望自娩。 院中可真熱鬧用踩,春花似錦、人聲如沸忙迁。這莊子的主人今日做“春日...
    開封第一講書人閱讀 31,659評論 0 22
  • 文/蒼蘭香墨 我抬頭看了看天上的太陽姊扔。三九已至惠奸,卻和暖如春,著一層夾襖步出監(jiān)牢的瞬間恰梢,已是汗流浹背佛南。 一陣腳步聲響...
    開封第一講書人閱讀 32,812評論 1 268
  • 我被黑心中介騙來泰國打工, 沒想到剛下飛機(jī)就差點(diǎn)兒被人妖公主榨干…… 1. 我叫王不留嵌言,地道東北人嗅回。 一個月前我還...
    沈念sama閱讀 47,693評論 2 368
  • 正文 我出身青樓,卻偏偏與公主長得像摧茴,于是被迫代替她去往敵國和親绵载。 傳聞我的和親對象是個殘疾皇子,可洞房花燭夜當(dāng)晚...
    茶點(diǎn)故事閱讀 44,577評論 2 353

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