函數、函數參數、參數解構
python函數與其它語言不太一樣;
函數:
數學定義,y=f(x),y是x的函數,x是自變量;
python函數,由語句塊、函數名稱、參數列表、若干語句構成,它是組織代碼的最小單元,完成一定的功能;
作用:
結構化編程對代碼的最基本的封裝,一般按照功能組織一段代碼;
封裝的目的爲了複用,減少冗餘代碼;
代碼更加簡潔美觀、可讀易懂(工業化要求);
分類:
內建函數built-in,如max(),reversed()等;
庫函數,如math.ceil()等、第三方庫;
函數定義(聲明):
def語句定義函數;
def 函數名(參數列表):
函數體(代碼塊)
[return 返回值]
函數名,就是標識符,命名要求一樣(見明知義);
語句塊必須縮進,約定4個空格;
python的函數沒有return語句,隱式會返回一個None值;在一些語言中函數沒有返回值稱爲過程,有返回值稱爲函數;
函數中若有兩個return,只返回第一個return的結果,return a,b返回的是tuple類型;
定義中的參數列表稱爲形式參數,只是一種符號表達,簡稱形參,即y=f(x)中的x;
定義要在調用前,否則拋NameError異常;
函數調用:
函數定義,只是聲明瞭一個函數,它不會被執行,需要調用,不調用的函數與變量一樣;
調用的方式,就是函數名加上小括號,括號內寫上參數;
調用時寫的參數是實際參數,是實實在在傳入的值,簡稱實參;
函數是可調用對象,可用callable()判斷;
例:
In [1]: def add(x,y):
...: result = x + y
...: return result
...:
In [2]: out=add(4,5)
In [3]: out
Out[3]: 9
In [4]: callable(add)
Out[4]: True
函數參數:
參數調用時傳入的參數要和定義的個數相匹配(可變參數例外,內建函數中列表是作爲整體傳入的);
位置參數, def f(x,y,z):,調用時使用f(1,3,5),按照參數定義順序傳入實參;
關鍵字參數(命名參數keyword),def f(x,y,z):,調用時使用f(x=1,y=3,z=5),使用形參的名字來傳入實參的方式,如果使用了形參名字,那麼傳參順序可和定義順序不同;
傳參,位置參數必須在關鍵字參數之前傳入,位置參數是按位置對應的(簡單的往前放,複雜的往後放);
例:
In [6]: def f(x,y,z):
...: print(x,y,z)
...:
In [7]: f(z=None,y=10,x=[1])
[1] 10 None
In [8]: f((1,),z=6,y=4.1)
(1,) 4.1 6
In [9]: f(y=5,z=6,2) #錯誤,位置參數必須在關鍵字參數之前傳入
File "<ipython-input-9-c873e911554f>", line 1
f(y=5,z=6,2)
^
SyntaxError: positional argument follows keyword argument
函數參數默認值:
定義時,在形參後賦一個值;
作用:參數的默認值,可在未傳入足夠的實參的時候,對沒有給定的參數賦值爲默認值;參數非常多的時候,並不需要用戶每次都輸入所有的參數,簡化函數調用;
調用時必須要傳的參數,在定義時寫在前面;
調用時可用默認值的參數,在定義時寫在後面;
定義函數時,參數超過2個,都開始用缺省值參數,把重要的參數往前放;
例:
In [1]: def add(x=4,y=5): #x和y類型不一樣時會異常
...: return x+y
...:
In [2]: add()
Out[2]: 9
In [3]: add(6,10)
Out[3]: 16
In [4]: add(6,y=7)
Out[4]: 13
In [5]: add(x=5)
Out[5]: 10
In [6]: add(y=7)
Out[6]: 11
In [7]: add(x=5,6)
File "<ipython-input-7-9f5938d26e25>", line 1
add(x=5,6)
^
SyntaxError: positional argument follows keyword argument
In [8]: add(y=8,4)
File "<ipython-input-8-013636d44ec1>", line 1
add(y=8,4)
^
SyntaxError: positional argument follows keyword argument
In [9]: add(x=5,y=6)
Out[9]: 11
In [9]: add(x=5,y=6)
Out[9]: 11
In [10]: add(y=6,x=5)
Out[10]: 11
In [11]: def add(x=4,y): #缺省值要往後放
...: pass
File "<ipython-input-11-797c4f959f43>", line 1
def add(x=4,y):
^
SyntaxError: non-default argument follows default argument
In [12]: def add(x,y=5):
...: return x+y
...:
In [13]: add()
---------------------------------------------------------------------------
TypeError Traceback (most recent call last)
<ipython-input-13-d5d29de3ed94> in <module>()
----> 1 add()
TypeError: add() missing 1 required positional argument: 'x'
In [14]: add(10)
Out[14]: 15
例:
定義一個函數login,參數名稱爲host,port,username,password?
In [17]: def login(host='127.0.0.1',port='8080',username='jowin',password='chai'):
...: print('{}:{}@{}/{}'.format(host,port,username,password))
...:
In [18]: login()
127.0.0.1:8080@jowin/chai
In [19]: login('127.0.0.1',80,'jerry','jerry')
127.0.0.1:80@jerry/jerry
In [20]: login('localhost',port=80,password='test')
localhost:80@jowin/test
In [21]: login(port=80,password='test',host='localhost')
localhost:80@jowin/test
可變參數:
一個形參可以匹配任意個(包括0個)參數(實參);
位置可變參數:
在形參前使用*,表示該形參是可變參數,可以接收多個實參,函數名約定用*args;
收集多個實參爲一個tuple;
關鍵字參數也能傳遞任意多個嗎?不能
關鍵字可變參數:
形參前使用**,表示可接收多個關鍵字參數,參數名約定用**kwargs;
收集的實參名稱和值組成一個字典;
可變參數總結:
有位置可變參數和關鍵字可變參數;
位置可變參數在形參前使用*;
關鍵字可變參數在形參前使用**;
位置可變參數和關鍵字可變參數都可收集若干個實參,位置可變參數收集形成一個tuple,關鍵字可變參數收集形成一個dict;
混合使用參數時,可變參數要放到參數列表的最後(普通參數需放到參數列表前面),位置可變參數需要在關鍵字可變參數之前;
keyword-only參數:
python3加入;
如果在一個星號參數後,或者一個位置可變參數後,出現的普通參數,實際上已經不是普通參數了,而是keyword-only參數;
使用方式一,def fn(*args,x):或def fn(*args,x,y,**kwargs):;
使用方式二,def fn(*,x,y):或def fn(z,*,x,y),只允許x,y兩個參數,且這兩個參數只能是keyword-only參數,*之後,普通形參都變成了必須給出的keyword-only參數;
def fn(**kwargs,x):,SyntaxError,**kwargs後不需要再跟keyword,多此一舉,可理解爲**kwargs會截取所有的關鍵字參數,就算有x=5,x也永遠得不到這個值,所以語法錯誤;
例:
In [22]: def add(nums): #需求,多個數累加求和
...: sum=0
...: for i in nums:
...: sum+=i
...: return sum
...:
In [23]: add([1,3,5]) #傳入一個可迭代對象,迭代元素求和
Out[23]: 9
In [24]: add((2,4,6))
Out[24]: 12
In [27]: def add(*args):
...: sum=0
...: print(type(args)) #tuple,傳進後是不可變類型
...: for i in args:
...: sum+=i
...: print(sum)
...: return sum
...:
In [28]: add(3,6,7)
<class 'tuple'>
16
Out[28]: 16
In [29]: val=add(3,5,7) #函數中若無return,val的值爲None;不認關鍵字參數
<class 'tuple'>
15
In [30]: val
Out[30]: 15
例:
In [31]: def showconfig(**kwargs):
...: print(type(kwargs))
...: for k,v in kwargs.items(): #dict.items()在python3中叫dictionary view
...: print('{}={}'.format(k,v))
...:
In [32]: showconfig(host='localhost',port=8080,username='joiwn',password='chai')
<class 'dict'>
password=chai
port=8080
username=joiwn
host=localhost
可變參數混合使用:
def showconfig(username,password,**kwargs):
def showconfig(username,*args,**kwargs):
def showconfig(username,password,**kwargs,*args): #錯誤,位置可變參數需在關鍵字可變參數之前
例:
In [33]: def fn(x,y,*args,**kwargs):
...: print(x)
...: print(y)
...: print(args)
...: print(kwargs)
...:
In [34]: fn(3,5,7,9,10,a=1,b='python')
3
5
(7, 9, 10)
{'a': 1, 'b': 'python'}
In [35]: fn(3,5)
3
5
()
{}
In [36]: fn(3,5,7)
3
5
(7,)
{}
In [37]: fn(3,5,a=1,b='python')
3
5
()
{'a': 1, 'b': 'python'}
In [38]: fn(7,9,y=5,x=3,a=1,b='python') #y=5,x=3與前面的7,9重複了,multiple values
---------------------------------------------------------------------------
TypeError Traceback (most recent call last)
<ipython-input-38-58efe90dffe9> in <module>()
----> 1 fn(7,9,y=5,x=3,a=1,b='python')
TypeError: fn() got multiple values for argument 'y'
例:
In [39]: def fn(*args,x,y,**kwargs): #此種方式定義屬keyword-only,在調用傳參時x,y要用關鍵字參數形式,要先滿足keyword-only,即x,y,其它的纔有着落
...: print(args)
...: print(x)
...: print(y)
...: print(kwargs)
...:
In [40]: fn(7,9,y=5,x=3,a=1,b='python') #*args後的x,y屬keyword-only參數,要先滿足它倆
(7, 9)
3
5
{'a': 1, 'b': 'python'}
In [41]: fn(3,5)
---------------------------------------------------------------------------
TypeError Traceback (most recent call last)
<ipython-input-41-c5325f4c0ff0> in <module>()
----> 1 fn(3,5)
TypeError: fn() missing 2 required keyword-only arguments: 'x' and 'y'
In [42]: fn(3,5,7)
---------------------------------------------------------------------------
TypeError Traceback (most recent call last)
<ipython-input-42-da5434142bea> in <module>()
----> 1 fn(3,5,7)
TypeError: fn() missing 2 required keyword-only arguments: 'x' and 'y'
In [43]: fn(3,5,a=1,b='python')
---------------------------------------------------------------------------
TypeError Traceback (most recent call last)
<ipython-input-43-018d4ba64920> in <module>()
----> 1 fn(3,5,a=1,b='python')
TypeError: fn() missing 2 required keyword-only arguments: 'x' and 'y'
例:
In [44]: def fn(*args,x): #args可以看作已經截獲了所有的位置參數,x不使用關鍵字參數就不可能拿到實參
...: print(args)
...: print(x)
...:
In [45]: fn(3,5,x=7) #fn(3,5)和fn(3,5,7)均是錯的
(3, 5)
7
例:
In [47]: def fn(*,x,y):
...: print(x,y)
...:
In [48]: fn(x=1,y=2) #fn(4,5)和fn(6,x=1,y=2)錯誤
1 2
In [49]: def fn(z,*,x,y): #x,y必須是keyword-only參數,z不作要求
...: print(x,y)
...:
In [50]: fn(1,x=2,y=3)
2 3
例(可變參數和參數默認值):
In [1]: def fn(*args,x=5):
...: print(args)
...: print(x)
...:
In [2]: fn()
()
5
In [3]: fn(5) #2個5不一樣
(5,)
5
In [4]: fn(x=6)
()
6
In [5]: def fn(y,*args,x=5):
...: print('x={},y={}'.format(x,y))
...: print(args)
...:
In [6]: fn(5) #fn()和fn(x=6)錯誤,至少要給y一個位置參數
x=5,y=5
()
In [7]: fn(y=17,2,3,x=10) #關鍵字參數一定要放到位置參數後面
File "<ipython-input-7-89c585e00820>", line 1
fn(y=17,2,3,x=10)
^
SyntaxError: positional argument follows keyword argument
In [8]: fn(1,2,y=3,x=10) #y重複了
---------------------------------------------------------------------------
TypeError Traceback (most recent call last)
<ipython-input-8-ca712b8f6fdb> in <module>()
----> 1 fn(1,2,y=3,x=10)
TypeError: fn() got multiple values for argument 'y'
In [9]: fn(1,2,3,x=10)
x=10,y=1
(2, 3)
In [10]: def fn(x=5,**kwargs):
...: print('x={}'.format(x))
...: print(kwargs)
...:
In [11]: fn()
x=5
{}
In [12]: fn(5)
x=5
{}
In [13]: fn(x=6)
x=6
{}
In [14]: fn(y=3,x=10)
x=10
{'y': 3}
In [15]: fn(3,y=10)
x=3
{'y': 10}
參數規則:
參數列表一般順序是,普通參數-->缺省參數-->可變位置參數-->keyword-only參數(可帶缺省值)-->可變關鍵字參數;
注:
代碼應該易讀易懂,而不是爲難別人;
要按照書面習慣,定義函數;
例:
In [16]: def fn(x,y,z=3,*args,m=4,n,**kwargs): #def fn(x,y,z=3,*,n,m=4,**kwargs):這樣最好
...: print(x,y,z,m,n)
...: print(args)
...: print(kwargs)
...:
In [17]: fn(1,2,n=3)
1 2 3 4 3
()
{}
In [18]: fn(1,2,10,11,t=7,n=5)
1 2 10 4 5
(11,)
{'t': 7}
In [19]: fn(1,2,10,11,n=5,t=100)
1 2 10 4 5
(11,)
{'t': 100}
In [20]: fn(1,2,10,11,x=3,n=4) #x重複
---------------------------------------------------------------------------
TypeError Traceback (most recent call last)
<ipython-input-20-05ecc476ee5d> in <module>()
----> 1 fn(1,2,10,11,x=3,n=4)
TypeError: fn() got multiple values for argument 'x'
例:
In [22]: def connect(host='localhost',port='3306',user='admin',passwd='admin',**kwargs):
...: print(host,port)
...: print(user,passwd)
...: print(kwargs)
...:
In [23]: connect(db='cmdb')
localhost 3306
admin admin
{'db': 'cmdb'}
In [24]: connect(host='192.168.7.47',db='cmdb')
192.168.7.47 3306
admin admin
{'db': 'cmdb'}
In [25]: connect(host='192.168.7.47',db='cmdb',passwd='myadmin')
192.168.7.47 3306
admin myadmin
{'db': 'cmdb'}
參數解構:
給函數提供實參的時候,可在集合類型前使用*或**,把集合類型的結構解開,提取出所有元素作爲函數的實參;
非字典類型使用*,解構成位置參數;
字典類型使用**,解構成關鍵字參數;
提取出來的元素數目要和參數的要求匹配,也要和參數的類型匹配;
例:
In [26]: def add(x,y):
...: return x+y
...:
In [27]: add(4,5)
Out[27]: 9
In [28]: add((4,5))
---------------------------------------------------------------------------
TypeError Traceback (most recent call last)
<ipython-input-28-42490dde0648> in <module>()
----> 1 add((4,5))
TypeError: add() missing 1 required positional argument: 'y'
In [29]: t=(4,5)
In [30]: add(t[0],t[1])
Out[30]: 9
In [31]: add(*t)
Out[31]: 9
In [32]: add(*range(4,6))
Out[32]: 9
例:
In [1]: def add(x,y):
...: return x+y
...:
In [2]: d={'x':5,'y':6}
In [3]: add(**d)
Out[3]: 11
In [4]: add(**{'a':5,'b':6}) #關鍵字參數中沒有a,b
---------------------------------------------------------------------------
TypeError Traceback (most recent call last)
<ipython-input-4-33098ef826f4> in <module>()
----> 1 add(**{'a':5,'b':6})
TypeError: add() got an unexpected keyword argument 'a'
In [6]: add(*d.keys())
Out[6]: 'yx'
In [7]: add(*d.values())
Out[7]: 11
參數解構和可變參數:
例:
In [8]: def add(*iterable):
...: result=0
...: for i in iterable:
...: result+=i
...: return result
...:
In [9]: add(1,2,3)
Out[9]: 6
In [10]: add(*[1,2,3]) #常用
Out[10]: 6
In [11]: add(*range(10))
Out[11]: 45
例:
In [13]: def fn(a,b):
...: return a,b
...:
In [14]: type(fn(4,5)) #return a,b返回的tuple類型,若函數中有多個return語句,只返回第一個return的結果
Out[14]: tuple
習題:
1、編寫一個函數,能接受至少2個參數,返回最小值和最大值?
def double_values(*args):
print(args)
return max(args),min(args)
fn(3,2,22,2,1)
#####################
import random
def double_values(*args):
print(args)
return max(args),min(args)
print(*double_values(*[random.randint(10,20) for _ in range(10)])) #兩處解構,double_values前面若無*則打印tuple,print()函數有隱式轉換str(),即使是print(1,2)也會把1,2轉爲string類型再打印
2、編寫一個函數,接受一個參數n(n爲正整數),左右兩種打印方式,要求數字必須對齊?
################################
#############################
方一:
lst = [str(i) for i in range(12,0,-1)]
strs = ' '.join(lst)
length = len(strs)
print(strs,length,sep='\n')
for i in range(1,12):
print('{:>{}}'.format(' '.join([str(j) for j in range(i,0,-1)]),length))
print(strs)
#################
lst = [str(i) for i in range(12,0,-1)]
strs = ' '.join(lst)
length = len(strs)
for col in range(1,12):
row = ' '.join([str(j) for j in range(col,0,-1)])
#print(row)
print('{}{}'.format(' '*(length-len(row)),row))
print(strs)
#################
def show(num):
tail = ' '.join([str(i) for i in range(num,0,-1)])
width = len(tail)
for i in range(1,num):
print('{:>{}}'.format(' '.join([str(j) for j in range(i,0,-1)]),width))
print(tail)
show(12)
#################
lst = [str(i) for i in range(12,0,-1)]
strs = ' '.join(lst)
length = len(strs)
print(strs)
for col in range(11,0,-1):
row = ' '.join([str(j) for j in range(col,0,-1)])
#print(row)
print('{}{}'.format(' '*(length-len(row)),row))
####################
方二:
def show(num):
tail = ' '.join([str(i) for i in range(num,0,-1)])
print(tail)
for i in range(len(tail)): #無需再次生成列表
if tail[i] == ' ':
print(' '*i,tail[i+1:])
show(15)
#####################
方三:
def show(num):
for i in range(1,num+1):
for j in range(num,0,-1):
if i < j:
print(' '*len(str(j)),end=' ')
else:
print(j,end=' ')
print()
show(15)
#########################