剛開始學(xué)習(xí)Python函數(shù)的時(shí)候,覺得自己C語(yǔ)言用的很溜,Python函數(shù)應(yīng)該沒啥難度,結(jié)果越學(xué)越覺得自己就是孤陋寡聞,不明白的,一知半解的東西太多,覺得自己這幾年完全荒廢了,喪失了學(xué)習(xí)的勁頭;還好在2018年初被某件事情深深的刺激到了,徹底醒悟,調(diào)整方向及時(shí)抓住這波潮流,才能勉強(qiáng)安慰焦慮的心靈,否則再混下去,淘汰是不可避免的。好了,廢話不多說,下面就詳細(xì)以自己的理解說說Python的函數(shù)吧。
在Python中,定義一個(gè)函數(shù)要使用def
語(yǔ)句,依次寫出函數(shù)名、括號(hào)、括號(hào)中的參數(shù)和冒號(hào):
,然后,在縮進(jìn)塊中編寫函數(shù)體,函數(shù)的返回值用return
語(yǔ)句返回。例如定義一個(gè)求絕對(duì)值的函數(shù):
函數(shù)定義
def my_abs(x):
if x >= 0:
return x
else:
return –x
在Python交互環(huán)境中定義函數(shù)時(shí),注意Python會(huì)出現(xiàn)...
的提示。函數(shù)定義結(jié)束后需要按兩次回車重新回到>>>
提示符下。如果想定義一個(gè)什么事也不做的空函數(shù),可以用pass
語(yǔ)句:
def nop():
pass
pass語(yǔ)句什么都不做,那有什么用?實(shí)際上pass可以用來作為占位符
返回多個(gè)值
函數(shù)可以返回多個(gè)值嗎?答案是肯定的。
比如在游戲中經(jīng)常需要從一個(gè)點(diǎn)移動(dòng)到另一個(gè)點(diǎn),給出坐標(biāo)、位移和角度,就可以計(jì)算出新的新的坐標(biāo):
import math
def move(x, y, step,angle=0):
nx = x + step * math.cos(angle)
ny = y - step * math.sin(angle)
return nx, ny
import math語(yǔ)句表示導(dǎo)入math包。其實(shí)這只是一種假象,Python函數(shù)返回的仍然是單一值,返回值是一個(gè)tuple元組,在語(yǔ)法上,返回一個(gè)tuple可以省略括號(hào),而多個(gè)變量可以同時(shí)接收一個(gè)tuple,按位置賦給對(duì)應(yīng)的值,所以,Python的函數(shù)返回多值其實(shí)就是返回一個(gè)tuple。
函數(shù)的參數(shù)
1、位置參數(shù)
def power(x, n):
s = 1
while n > 0:
n = n - 1
s = s * x
return s
power(x,n)函數(shù)有兩個(gè)參數(shù):x和n,這兩個(gè)參數(shù)都是位置參數(shù),調(diào)用函數(shù)時(shí),傳入的兩個(gè)值按照位置順序依次賦給參數(shù)x和n。
2、默認(rèn)參數(shù)
def power(x, n=2):
s = 1
while n > 0:
n = n - 1
s = s * x
return s
當(dāng)調(diào)用power(5)
時(shí),相當(dāng)于調(diào)用power(5, 2)
設(shè)置默認(rèn)參數(shù)時(shí)必選參數(shù)在前,默認(rèn)參數(shù)在后,否則Python的解釋器會(huì)報(bào)錯(cuò),當(dāng)函數(shù)有多個(gè)參數(shù)時(shí),把變化大的參數(shù)放前面,變化小的參數(shù)放后面,變化小的參數(shù)就可以作為默認(rèn)參數(shù)。 定義默認(rèn)參數(shù)要牢記一點(diǎn):默認(rèn)參數(shù)必須指向不變對(duì)象。
3、可變參數(shù)
在Python函數(shù)中,還可以定義可變參數(shù)。顧名思義,可變參數(shù)就是傳入的參數(shù)個(gè)數(shù)是可變的,可以是1個(gè)、2個(gè)到任意個(gè),還可以是0個(gè)。以數(shù)學(xué)題為例子,給定一組數(shù)字a,b,c……,請(qǐng)計(jì)算a2 + b2 + c2 + ……。要定義出這個(gè)函數(shù),我們必須確定輸入的參數(shù)。由于參數(shù)個(gè)數(shù)不確定,我們首先想到可以把a,b,c……作為一個(gè)list或tuple傳進(jìn)來,這樣,函數(shù)可以定義如下:
def calc(numbers):
sum = 0
for n in numbers:
sum = sum + n * n
return sum
但是調(diào)用的時(shí)候,需要先組裝出一個(gè)list或tuple。把函數(shù)的參數(shù)改為可變參數(shù):def calc(*numbers):
sum = 0
for n in numbers:
sum = sum + n * n
return sum
定義可變參數(shù)和定義一個(gè)list或tuple參數(shù)相比,僅僅在參數(shù)前面加了一個(gè)*
號(hào)。在函數(shù)內(nèi)部,參數(shù)numbers
接收到的是一個(gè)tuple,因此,函數(shù)代碼完全不變。但是,調(diào)用該函數(shù)時(shí),可以傳入任意個(gè)參數(shù),包括0個(gè)參數(shù)。Python允許你在list或tuple前面加一個(gè)*
號(hào),把list或tuple的元素變成可變參數(shù)傳遞。
4、關(guān)鍵字參數(shù)
關(guān)鍵字參數(shù)允許你傳入0個(gè)或任意個(gè)含參數(shù)名的參數(shù),這些關(guān)鍵字參數(shù)在函數(shù)內(nèi)部自動(dòng)組裝為一個(gè)dict字典。
def person(name, age, **kw):
print('name:', name, 'age:', age, 'other:', kw)
函數(shù)person除了必選參數(shù)name和age外,還接受關(guān)鍵字參數(shù)kw。在調(diào)用該函數(shù)時(shí),可以只傳入必選參數(shù),也可以傳入任意個(gè)數(shù)的關(guān)鍵字參數(shù)。
>>> 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'}
關(guān)鍵字參數(shù)有什么用?它可以擴(kuò)展函數(shù)的功能。比如,在person函數(shù)里,保證能接收到name和age這兩個(gè)參數(shù),但是如果調(diào)用者愿意提供更多的參數(shù),函數(shù)也能接收。
試想你正在做一個(gè)用戶注冊(cè)的功能,除了用戶名和年齡是必填項(xiàng)外,其他都是可選項(xiàng),利用關(guān)鍵字參數(shù)來定義這個(gè)函數(shù)就能滿足注冊(cè)的需求。和可變參數(shù)類似,也可以先組裝出一個(gè)dict,然后把該dict轉(zhuǎn)換為關(guān)鍵字參數(shù)傳進(jìn)去:
>>>extra = {'city': 'Beijing', 'job': 'Engineer'}
>>> person('Jack', 24,city=extra['city'], job=extra['job'])
name: Jack age: 24 other: {'city':'Beijing', 'job': 'Engineer'}
當(dāng)然,上面復(fù)雜的調(diào)用可以用簡(jiǎ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
5、命名關(guān)鍵字參數(shù)
對(duì)于關(guān)鍵字參數(shù),函數(shù)的調(diào)用者可以傳入任意不受限制的關(guān)鍵字參數(shù)。至于到底傳入了哪些,就需要在函數(shù)內(nèi)部通過kw
檢查。仍以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)
但是調(diào)用者仍可以傳入不受限制的關(guān)鍵字參數(shù):
>>> 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ù)。
調(diào)用方式如下:
>>> 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ò):
>>> person('Jack', 24,'Beijing', 'Engineer')
Traceback (most recent call last):
File '<stdin>', line 1, in <module>
TypeError: person() takes 2 positionalarguments but 4 were given
由于調(diào)用時(shí)缺少參數(shù)名city
和job
,Python解釋器把這4個(gè)參數(shù)均視為位置參數(shù),但person()
函數(shù)僅接受2個(gè)位置參數(shù)。命名關(guān)鍵字參數(shù)可以有缺省值,從而簡(jiǎn)化調(diào)用:
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ù):
def person(name, age, city, job):
# 缺少 *,city和job被視為位置參數(shù)
pass
5、參數(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ù)。比如定義一個(gè)函數(shù),包含上述若干種參數(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)
在函數(shù)調(diào)用的時(shí)候,Python解釋器自動(dòng)按照參數(shù)位置和參數(shù)名把對(duì)應(yīng)的參數(shù)傳進(jìn)去。最神奇的是通過一個(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': '#'}
>>> args = (1, 2, 3)
>>> kw = {'d': 88, 'x': '#'}
>>> f2(*args, **kw)
a = 1 b = 2 c = 3 d = 88 kw = {'x':'#'}
所以,對(duì)于任意函數(shù),都可以通過類似func(*args, **kw)
的形式調(diào)用它,無(wú)論它的參數(shù)是如何定義的。雖然可以組合多達(dá)5種參數(shù),但不要同時(shí)使用太多的組合,否則函數(shù)接口的可理解性很差。
函數(shù)閉包
函數(shù)閉包就是在一個(gè)函數(shù)內(nèi)部定義一個(gè)函數(shù),然后返回定義的內(nèi)部函數(shù)對(duì)象,名義上是返回一個(gè)函數(shù)名(函數(shù)名即函數(shù)對(duì)象),但實(shí)際返回還包括內(nèi)部函數(shù)作用域內(nèi)的資源;將這些資源連同函數(shù)名打包一起返回才稱之為閉包。如下例所示:
閉包的意義:返回的函數(shù)對(duì)象,不僅僅是一個(gè)函數(shù)對(duì)象,在該函數(shù)外還包裹了一層作用域,這使得,該函數(shù)無(wú)論在何處調(diào)用,優(yōu)先使用自己外層包裹的作用域
裝飾器就是閉包函數(shù)的一種應(yīng)用場(chǎng)景。將在Python學(xué)習(xí)—裝飾器篇,專門講述裝飾器如何實(shí)現(xiàn)。函數(shù)式編程與高階函數(shù)
把函數(shù)作為參數(shù)傳入或者返回一個(gè)函數(shù),這樣的函數(shù)稱為高階函數(shù),函數(shù)式編程就是指這種高度抽象的編程范式。函數(shù)式編程就是一種抽象程度很高的編程范式,純粹的函數(shù)式編程語(yǔ)言編寫的函數(shù)沒有變量,因此,任意一個(gè)函數(shù),只要輸入是確定的,輸出就是確定的,這種純函數(shù)我們稱之為沒有副作用。而允許使用變量的程序設(shè)計(jì)語(yǔ)言,由于函數(shù)內(nèi)部的變量狀態(tài)不確定,同樣的輸入,可能得到不同的輸出,因此,這種函數(shù)是有副作用的。
函數(shù)式編程的一個(gè)特點(diǎn)就是,允許把函數(shù)本身作為參數(shù)傳入另一個(gè)函數(shù),還允許返回一個(gè)函數(shù)!Python對(duì)函數(shù)式編程提供部分支持。由于Python允許使用變量,因此,Python不是純函數(shù)式編程語(yǔ)言。
map()
和reduce()
函數(shù)map()
函數(shù)接收兩個(gè)參數(shù),一個(gè)是函數(shù),一個(gè)是Iterable
,map
將傳入的函數(shù)依次作用到序列的每個(gè)元素,并把結(jié)果作為新的Iterator
返回
reduce
把一個(gè)函數(shù)作用在一個(gè)序列[x1, x2, x3, ...]
上,這個(gè)函數(shù)必須接收兩個(gè)參數(shù),reduce
把結(jié)果繼續(xù)和序列的下一個(gè)元素做計(jì)算
輸入:['adam', 'LISA', 'barT']
,輸出:['Adam','Lisa', 'Bart']
:
# -*- coding: utf-8 -*-
#輸入名字,變成首字母大寫,其他字母小寫的標(biāo)準(zhǔn)格式
def normalize(name):
str1 = ''
for i, ch in enumerate(name):
if i == 0:
str1 =str1 + ch.upper() #str.upper() 方法讓字母轉(zhuǎn)大寫
else:
str1 =str1 + ch.lower() #str.lower() 方法讓字母轉(zhuǎn)小寫
return str1
def normalize(name):
name=name[0].upper()+name[1:].lower()
return name
L2 = list(map(normalize, L1))
prod()
函數(shù),可以接受一個(gè)list并利用reduce()
求積# -*- coding: utf-8 -*-
from functools import reduce
def prod(L):
def fn(x, y):
return x*y
returnreduce(fn, L)
測(cè)試代碼:
from functools import reduce
defprod(L):
deff(x,y):
return x*y
return reduce(f, L)
L1 = []
while1:
s = input('請(qǐng)往連乘數(shù)列中添加數(shù)字:\n')
if s == 'end':
break
#s = float(s)
L1.append(s)
print('連乘列表為:\n',L1)
print('計(jì)算結(jié)果為:\n',prod(L1))
filter()
函數(shù)用于過濾序列filter()
接收一個(gè)函數(shù)和一個(gè)序列。和map()
不同的是,filter()
把傳入的函數(shù)依次作用于每個(gè)元素,然后根據(jù)返回值是True
還是False
決定保留還是丟棄該元素
def is_palindrome(n):
return str(n) ==str(n)[::-1]
output=filter(is_palindrome, range(1,1001))
print(list(output))
匿名函數(shù)
在Python中,對(duì)匿名函數(shù)提供了有限支持。還是以map()函數(shù)為例,計(jì)算f(x)=x2時(shí),除了定義一個(gè)f(x)的函數(shù)外,還可以直接傳入匿名函數(shù):
>>>list(map(lambda x: x * x, [1, 2, 3, 4, 5, 6, 7, 8, 9]))
[1, 4, 9, 16, 25, 36,49, 64, 81]
通過對(duì)比可以看出,匿名函數(shù)lambda x: x * x實(shí)際上就是:
def f(x):
return x * x
關(guān)鍵字lambda表示匿名函數(shù),冒號(hào)前面的x表示函數(shù)參數(shù)。
匿名函數(shù)有個(gè)限制,就是只能有一個(gè)表達(dá)式,不用寫return,返回值就是該表達(dá)式的結(jié)果。
用匿名函數(shù)有個(gè)好處,因?yàn)楹瘮?shù)沒有名字,不必?fù)?dān)心函數(shù)名沖突。此外,匿名函數(shù)也是一個(gè)函數(shù)對(duì)象,也可以把匿名函數(shù)賦值給一個(gè)變量,再利用變量來調(diào)用該函數(shù):
>>> f =lambda x: x * x
>>> f
<function<lambda> at 0x101c6ef28>
>>> f(5)
25
同樣,也可以把匿名函數(shù)作為返回值返回,比如:
def build(x, y):
return lambda: x * x + y * yPython函數(shù)的內(nèi)容有很多,例如函數(shù)的對(duì)象概念、遞歸函數(shù)、偏函數(shù)等,不是一篇博文可以說完的,而且python還有很多常用的內(nèi)置函數(shù),模塊等,這些都需要在以后的使用中慢慢消化。這篇博客使用了不少廖雪峰python教程的示例,下面附下廖雪峰的python教程網(wǎng)址:https://www.liaoxuefeng.com/
聯(lián)系客服