函数、函数参数、参数解构
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 "
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 "
add(x=5,6)
^
SyntaxError: positional argument follows keyword argument
In [8]: add(y=8,4)
File "
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 "
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)
----> 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)
16
Out[28]: 16
In [29]: val=add(3,5,7) #函数中若无return,val的值为None;不认关键字参数
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')
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)
----> 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)
----> 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)
----> 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)
----> 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 "
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)
----> 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)
----> 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)
----> 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)
----> 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)
#########################