day10 函数基本使用及函数参数
一,函数的基本使用
01、什么是函数
函数就是相当于具备某一功能的工具,函数的使用必须遵循一个原则:
先定义、后调用
02、为何要用函数
1、代码冗余,程序的组织结构不清楚,可读性差
2、可维护性、扩展性差
03、如何使用函数
1.先定义: 三种定义方式
#定义的语法:
def 函数名(参数1,参数2,...):
"""文档描述"""
函数体
return 值
#def: 定义函数的关键字;
#函数名:函数名指向函数内存地址,是对函数体代码的引用。函数的命名应该反映出函数的功能;
#括号:括号内定义参数,参数是可有可无的,且无需指定参数的类型;
#冒号:括号后要加冒号,然后在下一行开始缩进编写函数体的代码;
#"""文档描述""": 描述函数功能,参数介绍等信息的文档,非必要,但是建议加上,从而增强函数的可读性;
#函数体:由语句和表达式组成;
#return 值:定义函数的返回值,return是可有可无的。
# 定义函数发生的事情
# 1、申请内存空间保存函数体代码
# 2、将上述内存地址绑定函数名
# 3、定义函数不会执行函数体代码,但是会检查函数体语法
# 调用函数发生的事情
# 1、通过函数名找到函数的内存地址
# 2、加括号就是在触发函数体代码的执行
①形式一:无参函数
def func():
print("hahaha")
②形式二:有参函数
def func(x,y): # x= 1 y = 2
print(x,y)
func(1,2)
③形式三:空函数,函数体代码为pass
def func(x,y):
pass
④三种函数的应用场景
无参函数应用场景
def interactive():
name = input('username>>:')
age = input('age>>:')
geder = input('gender>>:')
msg = '名字:{} 年龄:{} 性别:{}'.format(name,age,geder)
print(msg)
interactive()
有参函数应用场景
def add(x,y): #参数--》原材料
# x = 20
# y = 30
res = x + y
# print(res)
return res # 返回值--》产品
res = add(10,2)
print(res)
空函数应用场景
函数体为pass代表什么都不做,称之为空函数。定义空函数通常是有用的,因为在程序设计的开始,往往是先想好程序都需要完成什么功能,然后把所有功能都列举出来用pass充当函数体“占位符”,这将使得程序的体系结构立见,清晰且可读性强。
2.后调用:三种调用方式
①语句形式:只加括号调用
interactive()
add(1,2)
②表达式形式
def add(x,y):
res = x + y
return res
#赋值表达式
res = add(10,2)
print(res)
#数学表达式
res = add(1,2)*10
print(res)
③函数调用可以当做参数
res = add(add(1,2),10)
print(res)
3.返回值:三种返回值形式
return是函数结束的标志,即函数体代码一旦运行到return会立刻终止代码的运行,并且会将return后的值当做本次运行的结果返回
①返回None
函数体内没有return
return
return None(不用)
②返回一个值:return 值
def func():
return 10
③返回多个值:用逗号分隔多个值,会被return返回为元组
def func():
return 10,'aa',[1,2]
res = func()
print(res,type(res))
#运行结果
(10, 'aa', [1, 2]) <class 'tuple'>
二,函数的参数
01、形参与实参介绍
函数的参数分为形式参数和实际参数,简称形参和实参:
形参:即在定义函数时,括号内声明的参数。形参本质就是一个变量名,用来接收外部传来的值。
实参:即在调用函数时,括号内传入的值,值可以是常量、变量、表达式或三者的组合:
#1:实参是常量
res=my_min(1,2)
#2:实参是变量
a=1
b=2
res=my_min(a,b)
#3:实参是表达式
res=my_min(10*2,10*my_min(3,4))
#4:实参可以是常量、变量、表达式的任意组合
a=2
my_min(1,a,10*my_min(3,4))
实参与形参的关系
在调用有参函数时,实参(值)会赋值给形参(变量名)。在Python中,变量名与值只是单纯的绑定关系,而对于函数来说,这种绑定关系只在函数调用时生效,在调用结束后解除。
02、形参与实参的具体使用
1.位置参数
位置即顺序,位置参数指的是按顺序定义的参数,需要从两个角度去看
①在定义函数时,按照从左到右的顺序依次定义形参,称为位置形参,凡是按照这种形式定义的形参都必须被传值
②在调用函数时,按照从左到右的顺序依次定义实参,称为位置实参,凡是按照这种形式定义的实参会按照从左到右的顺序与形参一一对应
def register(name,age,sex): #定义位置形参:name,age,sex,三者都必须被传值
print('Name:%s Age:%s Sex:%s' %(name,age,sex))
register() #TypeError:缺少3个位置实参数
2.关键字参数
在调用函数时,实参可以是key=value的形式,称为关键字参数,凡是按照这种形式定义的实参,可以完全不按照从左到右的顺序定义,但仍能为指定的形参赋值
>>> register(sex='male',name='lili',age=18)
Name:lili Age:18 Sex:male
需要注意在调用函数时,实参也可以是按位置或按关键字的混合使用,但必须保证关键字参数在位置参数后面,且不可以对一个形参重复赋值
>>> register('lili',sex='male',age=18) #正确使用
>>> register(name='lili',18,sex='male') #SyntaxError:关键字参数name=‘lili’在位置参数18之前
>>> register('lili',sex='male',age=18,name='jack') #TypeError:形参name被重复赋值
3.默认参数
在定义函数时,就已经为形参赋值,这类形参称之为默认参数,当函数有多个参数时,需要将值经常改变的参数定义成位置参数,而将值改变较少的参数定义成默认参数
>>> def register(name,age,sex='male'): #默认sex的值为male
... print('Name:%s Age:%s Sex:%s' %(name,age,sex))
...
定义时就已经为参数sex赋值,意味着调用时可以不对sex赋值,这降低了函数调用的复杂度
>>> register('tom',17) #大多数情况,无需为sex传值,默认为male
Name:tom Age:17 Sex:male
>>> register('Lili',18,'female') #少数情况,可以为sex传值female
Name:Lili Age:18 Sex:female
PS:
默认参数必须在位置参数之后
默认参数的值仅在函数定义阶段被赋值一次
>>> x=1
>>> def foo(arg=x):
... print(arg)
...
>>> x=5 #定义阶段arg已被赋值为1,此处的修改与默认参数arg无任何关系
>>> foo()
1
默认参数的值通常应设为不可变类型
def foo(n,arg=[]):
arg.append(n)
return arg
foo(1)
[1]
foo(2)
[1, 2]
foo(3)
[1, 2, 3]
每次调用是在上一次的基础上向同一列表增加值,修改如下
def foo(n,arg=None):
if arg is None:
arg=[]
arg.append(n)
return arg
foo(1)
[1]
foo(2)
[2]
foo(3)
[3]
4.可变长度的参数(与*的用法)
①可变长度的位置参数
如果在最后一个形参名前加*号,那么在调用函数时,溢出的位置实参,都会被接收,以元组的形式保存下来赋值给该形参
>>> def foo(x,y,z=1,*args): #在最后一个形参名args前加*号
... print(x)
... print(y)
... print(z)
... print(args)
...
>>> foo(1,2,3,4,5,6,7) #实参1、2、3按位置为形参x、y、z赋值,多余的位置实参4、5、6、7都被*接收,以元组的形式保存下来,赋值给args,即args=(4, 5, 6,7)
1
2
3
(4, 5, 6, 7)
如果我们事先生成了一个列表,仍然是可以传值给*args的
>>> def foo(x,y,*args):
... print(x)
... print(y)
... print(args)
...
>>> L=[3,4,5]
>>> foo(1,2,*L) # *L就相当于位置参数3,4,5, foo(1,2,*L)就等同于foo(1,2,3,4,5)
1
2
(3, 4, 5)
注意:如果在传入L时没有加*,那L就只是一个普通的位置参数了
如果形参为常规的参数(位置或默认),实参仍可以是*的形式
>>> def foo(x,y,z=3):
... print(x)
... print(y)
... print(z)
...
>>> foo(*[1,2]) #等同于foo(1,2)
1
2
3
如果我们想要求多个值的和,*args就派上用场了
>>> def add(*args):
... res=0
... for i in args:
... res+=i
... return res
...
>>> add(1,2,3,4,5)
15
②可变长度的关键字参数
如果在最后一个形参名前加**号,那么在调用函数时,溢出的关键字参数,都会被接收,以字典的形式保存下来赋值给该形参
>>> def foo(x,**kwargs): #在最后一个参数kwargs前加**
... print(x)
... print(kwargs)
...
>>> foo(y=2,x=1,z=3) #溢出的关键字实参y=2,z=3都被**接收,以字典的形式保存下来,赋值给kwargs
1
{'z': 3, 'y': 2}
如果我们事先生成了一个字典,仍然是可以传值给**kwargs的
>>> def foo(x,y,**kwargs):
... print(x)
... print(y)
... print(kwargs)
...
>>> dic={'a':1,'b':2}
>>> foo(1,2,**dic) #**dic就相当于关键字参数a=1,b=2,foo(1,2,**dic)等同foo(1,2,a=1,b=2)
1
2
{'a': 1, 'b': 2}
注意:如果在传入dic时没有加**,那dic就只是一个普通的位置参数了
>>> foo(1,2,dic) #TypeError:函数foo只需要2个位置参数,但是传了3个
如果形参为常规参数(位置或默认),实参仍可以是**的形式
>>> def foo(x,y,z=3):
... print(x)
... print(y)
... print(z)
...
>>> foo(**{'x':1,'y':2}) #等同于foo(y=2,x=1)
1
2
3
总结:
*与**在形参中是一种汇总行为
# 1、*在形参中的应用:*会将溢出的位置实参合并成一个元组,然后赋值给紧跟其后的那个形参名
def func(x,*args): # y = (2,3,4,5)
print(x)
print(args)
func(1,2,3,4,5)
def my_sum(*args):
res = 0
for i in args:
res += i
print(res)
my_sum(1,2)
*与**在实参中是一种打散行为
# *在实参中的应用:*后可以跟可以被for循环遍历的任意类型,*会将紧跟其后的那个值打散成位置实参
def func(x,y,z):
print(x)
print(y)
print(z)
func([11,22,33])
func(*[11,22,33]) # func(11,22,33)
func(*"hello") # func("h","e","l","l","o")
func(*{"k1":111,"k2":2222}) # func("k1","k2")
#**在实参中的应用:**只能跟字典类型,**会将字典打散成关键字实参
func(**{"k1":111,"k2":2222}) # func(k2=2222,k1=111)
func(**{"x":111,"y":2222,"z":333})
def index(x,y,z):
print(x,y,z)
def wrapper(*args,**kwargs): # args=(1,2,3,4,5,6) kwargs={"a":1,"b":2,"c":3}
index(*args,**kwargs) # index(*(1,2,3,4,5,6),**{"a":1,"b":2,"c":3})
# index(1,2,3,4,5,6,a=1,b=2,c=3)
# wrapper(1,2,3,4,5,6,a=1,b=2,c=3)
wrapper(1,y=2,z=3)
5.命名关键字参数
在定义了**kwargs参数后,函数调用者就可以传入任意的关键字参数key=value,如果函数体代码的执行需要依赖某个key,必须在函数内进行判断
>>> def register(name,age,**kwargs):
... if 'sex' in kwargs:
... #有sex参数
... pass
... if 'height' in kwargs:
... #有height参数
... pass
...
想要限定函数的调用者必须以key=value的形式传值,Python3提供了专门的语法:需要在定义形参时,用作为一个分隔符号,*号之后的形参称为命名关键字参数。对于这类参数,在函数调用时,必须按照key=value的形式为其传值,且必须被传值
>>> def register(name,age,*,sex,height): #sex,height为命名关键字参数
... pass
...
>>> register('lili',18,sex='male',height='1.8m') #正确使用
>>> register('lili',18,'male','1.8m') # TypeError:未使用关键字的形式为sex和height传值
>>> register('lili',18,height='1.8m') # TypeError没有为命名关键字参数height传值。
命名关键字参数也可以有默认值,从而简化调用
>>> def register(name,age,*,sex='male',height):
... print('Name:%s,Age:%s,Sex:%s,Height:%s' %(name,age,sex,height))
...
>>> register('lili',18,height='1.8m')
Name:lili,Age:18,Sex:male,Height:1.8m
需要强调的是:sex不是默认参数,height也不是位置参数,因为二者均在*后,所以都是命名关键字参数,形参sex=’male’属于命名关键字参数的默认值,因而即便是放到形参height之前也不会有问题。另外,如果形参中已经有一个args了,命名关键字参数就不再需要一个单独的*作为分隔符号了.
>>> def register(name,age,*args,sex='male',height):
... print('Name:%s,Age:%s,Args:%s,Sex:%s,Height:%s' %(name,age,args,sex,height))
...
>>> register('lili',18,1,2,3,height='1.8m') #sex与height仍为命名关键字参数
Name:lili,Age:18,Args:(1, 2, 3),Sex:male,Height:1.8m
6.组合使用
综上所述所有参数可任意组合使用,但定义顺序必须是:位置参数、默认参数、*args、命名关键字参数、**kwargs
可变参数*args与关键字参数**kwargs通常是组合在一起使用的,如果一个函数的形参为*args与**kwargs,那么代表该函数可以接收任何形式、任意长度的参数
>>> def wrapper(*args,**kwargs):
... pass
...
在该函数内部还可以把接收到的参数传给另外一个函数
>>> def func(x,y,z):
... print(x,y,z)
...
>>> def wrapper(*args,**kwargs):
... func(*args,**kwargs)
...
>>> wrapper(1,z=3,y=2)
1 2 3

浙公网安备 33010602011771号