函数
1 函数的意义
函数就相当于具备某一功能的工具
函数可以有效提高代码的复用性,可读性,维护性,可拓展性
函数的使用必须遵循一个原则:
先定义, 后调用
2 函数的语法
函数语法中必要的是def,函数名,():,函数体
可选的是参数,文档描述,return
def 函数名(参数1,参数2,...):
"""文档描述"""
# 函数体
return "值"
函数可以有两种产物,一是函数体的执行产物,二是return返回的值
可以使用变量接受return的值
3 函数的定义方式
定义函数发生的事情
1、申请内存空间保存函数体代码
2、将上述内存地址绑定函数名
3、定义函数不会执行函数体代码,但是会检测函数体语法
3.1 无参函数
顾名思义,无参函数就是没有参数的函数,它不需要传入任何参数就能运行
def func():
print('哈哈哈')
print('哈哈哈')
print('哈哈哈')
def interactive():
name=input('username>>: ')
age=input('age>>: ')
gender=input('gender>>: ')
msg='名字:{} 年龄:{} 性别{}'.format(name,age,gender)
print(msg)
interactive()
interactive()
interactive()
interactive()
3.2 有参函数
顾名思义,有参函数就是有参数的函数,它必须输入对应的参数才能执行
def func(x,y): # x=1 y=2
print(x,y)
func(1,2)
def add(x,y):
res=x + y
return res
res=add(20,30)
print(res)
3.3 空函数
空函数函数体代码为pass
空函数一般用于快速搭建框架
def auth_user():
"""user authentication function"""
pass
def download_file():
"""download file function"""
pass
def upload_file():
"""upload file function"""
pass
def ls():
"""list contents function"""
pass
def cd():
"""change directory"""
pass
4 函数的调用方式
调用函数发生的事情
1、通过函数名找到函数的内存地址
2、然后加括号就是在触发函数体代码的执行
def func():
pass
print(func)
# <function func at 0x000002326850DF28>
func()
4.1 只加括号调用函数的形式
interactive()
add(1,2)
这种函数调用形式只有代码执行的产物,没有接收返回值
4.2 表达式形式:
赋值表达式和数学表达式都属于这个类型
这个类型主要是为了接收返回值,并将返回值输出或继续进行别的操作
def add(x,y): # 参数->原材料
res=x + y
return res # 返回值->产品
# 赋值表达式
res=add(1,2)
print(res)
# 数学表达式
res=add(1,2)*10
print(res)
4.3 函数调用可以当做参数
这个类型主要是为了用返回值进行别的操作
res=add(add(1,2),10)
print(res)
5 定义与调用
5.1 示例1
示范1代码的执行为
1.定义bar
2.定义foo
3.调用foo,执行print(bar)
4.foo调用bar------------->执行print('from bar')
5.继续执行foo,执行print('from foo')
6.代码结束
# 示范1
def bar(): # bar=函数的内存地址
print('from bar')
def foo():
print(bar)
bar()
print('from foo')
foo()
# <function bar at 0x00000265609FDF28>
# from bar
# from foo
5.2 示例2
示范2代码的执行为
1.定义foo
2.定义bar
3.调用foo,执行print(bar)
4.foo调用bar------------->执行print('from bar')
5.继续执行foo,执行print('from foo')
6.代码结束
# 示范2
def foo():
print(bar)
bar()
print('from foo')
def bar(): # bar=函数的内存地址
print('from bar')
foo()
# <function bar at 0x00000265609FDF28>
# from bar
# from foo
# <function func at 0x00000265609FDEA0>
5.3 示例3
示范3代码的执行为
1.定义foo
2.调用foo,执行print(bar)
3.由于未定义bar,报错
4.定义bar-------未执行
# 示范3
def foo():
print(bar)
bar()
print('from foo')
foo()
def bar(): # bar=函数的内存地址
print('from bar')
# NameError: name 'bar' is not defined
6 函数返回值
return是函数结束的标志,即函数体代码一旦运行到return会立刻
终止函数的运行,并且会将return后的值当做本次运行的结果返回:
6.1 返回None:
返回None的三种情况:
-
函数体内没有return
-
return
-
return None
6.2 返回一个值:return 值
def func():
return 10
res=func()
print(res)
6.3 返回多个值:
用逗号分隔开多个值,会被return返回成元组
def func():
return 10, 'aa', [1, 2]
res = func()
print(res, type(res))
# (10, 'aa', [1, 2]) <class 'tuple'>
7 函数的参数
7.1 参数的类型
形参:在函数定义阶段定义的参数称之为形式参数,简称形参,相当于变量名
def func(x, y): # 定义阶段中的x,y全都是形参
print(x, y) # 定义阶段中的x,y全都是形参
实参:在调用函数阶段传入的值称之为实际参数,简称实参,相当于变量值
func(1,2)
实参: 调用函数时传入的值,但值可以是以下形式
# 形式一:直接传值
func(1,2)
# 形式二:传入变量值
a=1
b=2
func(a,b)
# 形式三:传入函数的结果
func(int('1'),2)
func(func1(1,2,),func2(2,3),333)
形参与实参的关系:
1、在调用阶段,实参(变量值)会绑定给形参(变量名)
2、这种绑定关系只能在函数体内使用
3、实参与形参的绑定关系在函数调用时生效,函数调用结束后解除绑定关系
7.2 形参
形参是在函数定义阶段定义的参数称之为形式参数,相当于变量名
可以分为位置参数,关键字参数,默认参数,不定长参数
7.2.1 位置参数
位置参数:在函数定义阶段,按照从左到右的顺序依次定义的参数称之为位置参数
特点:每个位置参数都必须被传值,多一个不行少一个也不行
def func(x,y):
print(x,y)
func(1,2) # 1,2
func(1,2,3) # 报错
func(1,) # 报错
7.2.2 默认参数
默认形参:在定义函数阶段,就已经被赋值的形参,称之为默认参数
特点:在定义阶段就已经被赋值,在调用阶段可以不用为其传参
def func(x,y=3):
print(x,y)
func(x=1)
# 1,3
func(x=1,y=44444)
# 1,44444
# 默认参数的运用场景:
# 某个参数一般都为同一个值,很少改变时可以用默认参数减少调用时的传参数
def register(name,age,gender='男'):
print(name,age,gender)
register('三炮',18)
register('二炮',19)
register('大炮',19)
register('没炮',19,'女')
7.2.3 位置形参与默认形参混用
位置形参与默认形参混用时必须遵守的规则:
1、位置形参必须在默认形参的左边
def func(y=2,x):
pass
# 报错
2、默认参数的值是在函数定义阶段被赋值的,准确地说被赋予的是值的内存地址
# 示范1:
m=2
def func(x,y=m):
print(x,y)
# 默认参数y此时指向2的内存地址,而不是m的值
m=3333
func(1)
# 输出1,2
# 示范2:
m = [1, ]
def func(x, y=m):
print(x, y)
# 默认参数y此时指向[111111, ]的内存地址
m.append(3)
# 在该列表中增加值,未改变内存地址
func(1)
# 输出1,[1,3]
3、默认参数不使用可变数据类型,如有需求可以将可变数据类型写入函数体中
虽然默认值指定为任意数据类型不会报错,但是不推荐使用可变类型
函数最理想的状态:
函数的调用只跟函数本身有关系,不外界代码的影响
使用可变数据类型可能导致意外情况出现
# 如有需求可以将可变数据类型写入函数体中
def func(x,y,z,l=None):
if l is None:
l=[]
l.append(x)
l.append(y)
l.append(z)
print(l)
func(1,2,3)
# [1,2,3]
new_l=[111,222]
func(1,2,3,new_l)
# [111,222,1,2,3]
7.2.4 可变长度的形参(*args和**kwargs)
可变长度的形参可以接收所有传入的多余的实参
其中*args用来接收所有传入的多余的位置参数
**kwargs用来接收所有传入的多余的关键字参数
7.2.4.1 可变长度的位置参数
*+形参名:用来接收溢出的位置实参,*会把溢出的位置实参保存成元组的格式然后传给紧跟其后的形参名
在调用函数的过程中,溢出的位置实参会被*保存成元组的格式然后传给紧跟其后的形参名
*后跟的可以是任意名字,但是约定俗成应该是args,即*args
def func(x,y,*z): # z =(3,4,5,6)
print(x,y,z)
func(1,2,3,4,5,6)
# 1,2,(3,4,5,6)
def my_sum(*args):
res=0
for item in args:
res+=item
return res
res=my_sum(1,2,3,4)
print(res)
# 10
*可以用在实参中,实参中带*,会先把*后的值打散成实参
如把列表,字符串,元组变成多个独立的元素按位置传入参数
def func(x,y,z):
print(x,y,z)
func(*[11,22,33]) # func(11,22,33)
func(*[11,22]) # func(11,22)
l=[11,22,33]
func(*l)
形参与实参中都使用*的情况
def func(x,y,*args): # args=(3,4,5,6)
print(x,y,args)
func(1,2,[3,4,5,6]) # 此时为传入3个位置参数,分别为1,2,列表[3,4,5,6]
# 1 2 ([3, 4, 5, 6],) # 这是不打散,打包
func(1,2,*[3,4,5,6]) # 此时为传入6个位置参数,func(1,2,3,4,5,6)
# 1 2 (3, 4, 5, 6) # 这是打散,再打包
func(*'hello') # 传入5个位置参数func('h','e','l','l','o')
# h e ('l', 'l', 'o') # 这是打散,再打包
7.2.4.2 可变长度的关键字参数
**+形参名:用来接收溢出的关键字实参,**会将溢出的关键字实参保存成字典格式,然后赋值给紧跟其后的形参名
**后跟的可以是任意名字,但是约定俗成应该是kwargs,即**kwargs
def func(x,y,**kwargs):
print(x,y,kwargs)
func(1,y=2,a=1,b=2,c=3)
# 1 2 {'a': 1, 'b': 2, 'c': 3}
**可以用在实参中(**后跟的只能是字典),实参中带**,会把**后的字典打散成独立的关键字实参,即key=value的形式
def func(x,y,z):
print(x,y,z)
func(*{'x':1,'y':2,'z':3}) # func('x','y','z')
func(**{'x':1,'y':2,'z':3}) # func(x=1,y=2,z=3)
# 错误演示
func(**{'x':1,'y':2,}) # func(x=1,y=2)
func(**{'x':1,'a':2,'z':3}) # func(x=1,a=2,z=3)
形参与实参中都使用**的情况:
def func(x,y,**kwargs):
print(x,y,kwargs)
func(y=222,x=111,a=333,b=444)
# 传入了4个关键字参数,其中ab为多余的关键字参数,打包成字典
# 111 222 {"a": 333, "b": 444}
func(**{'y':222,'x':111,'a':333,'b':4444})
# 传入了4个关键字参数,其中ab为多余的关键字参数,打包成字典
# 先打散,再打包
# 111 222 {"a": 333, "b": 4444}
7.2.4.2 可变长度的参数混用
混用*与**时必须遵守的规则:
*args必须在**kwargs之前
def func(a,*args,**kwargs):
print(args)
print(kwargs)
func(1,2,3,4,5,6,7,8,x=1,y=2,z=3)
# 传入8个位置参数,其中7个为多余的,打包成元组(2,3,4,5,6,7,8)
# 传入3个关键字参数,全为多余的,打包成字典{"x":1,"y":2,"z":3}
# 运行结果:
# (2,3,4,5,6,7,8)
# {"x":1,"y":2,"z":3}
def index(x,y,z):
print('index=>>> ',x,y,z)
def wrapper(*args,**kwargs): #args=(1,) kwargs={'z':3,'y':2}
index(*args,**kwargs)
# index(*(1,),**{'z':3,'y':2})
# index(1,z=3,y=2)
wrapper(1,z=3,y=2)
# 为wrapper传递的参数是给index用的
# 传入wrapper的参数都为多余的,打包成(1,)和{"z":3,"y":2}
# 执行index(*args,**kwargs)时为实参,即*将实参元组打散,**将字典打散,即得1,z=3,y=2
# 传入index 得到结果1,2,3
# 原格式--->打包---->打散
# 原格式--->汇总---->打回原形
7.2.5 命名关键字参数
命名关键字参数:
在定义函数时,*后定义的参数,称之为命名关键字参数
特点:
1、必须按照key=value的形式才能为命名关键字参数传值,按照位置传参数时全会被*拦截,不可能传参数到命名关键字参数
2、传参时不要求顺序
3、不设定默认值时必须传值
def func(x,y,*,a,b): # 其中,a和b称之为命名关键字参数
print(x,y)
print(a,b)
func(1,2,b=222,a=111)
# 1,2
# 111,222
可以用key=value的形式为命名关键字参数设定默认值
设定默认值后可以不用传参数
def func(x,y,*,a=11111,b):
print(x,y)
print(a,b)
func(1,2,b=22222)
# 1,2
# 11111,22222
7.2.6 所有形式参数混用
形参混用的顺序:
位置新参,默认形参, *args, 命名关键字形参,**kwargs
def func(x,y=111,*args,z,**kwargs):
print(x,y,args)
print(z,kwargs)
7.3 实参
实参是调用函数时传入的值
默认参数可以不传入参数
实参可以通过以下方法传参:
1.按位置传参
2.按关键字传参
3.位置,关键字混合传参
7.3.1 按位置传参
按位置传参: 按照从左到右的顺序依次传入参数
特点:必须按照顺序,与形参一一对应
def fun(a,b):
print(a,b)
func(1,2)
# 1-->a , 2-->b
# 1,2
func(2,1)
# 2-->a , 1-->b
# 2,1
7.3.2 按关键字传参
按关键字传参:按照key=value的形式传入参数
特点:指名道姓给某个形参传值,可以完全不参照顺序
def func(x,y):
print(x,y)
func(y=2,x=1)
# 2-->y , 1-->x
# 1,2
7.3.3 位置传参与关键字传参混用
位置传参与关键字传参混用必须遵守的规则:
1、位置实参必须放在关键字实参前
def func(x,y):
print(x,y)
func(1,y=2) # 正确示范
# 1,2
func(y=2,1) # 错误示范
# 报错
2、不能能为同一个形参重复传值
def func(x,y):
print(x,y)
func(1,y=2) # 正确示范
# 1,2
func(1,y=2,x=3) # 错误示范
# 报错
7.3.3 所有传参方式混用
只要保证按位置传的参数写在按关键字传的参数之前就行
*args (打散)可以看作多个按位置传的参数
**kwargs (打散)可以看作多个按关键字传的参数
def func(x,y,z,a,b,c):
print(x,y,z,a,b,c)
func(111,y=222,*[333,444],**{'b':555,'c':666})
# 错误
func(111,y=222,333,444,b=555,c=666)
# 错误
func(111,*[333,444],a=222,**{'b':555,'c':666})
# 正确
func(111,333,444,a=222,b=555,c=66)
# 正确
func(111,*[333,444],**{'b':555,'c':666},a=222,)
# 正确
8 函数对象
函数也是一个对象,可以把函数当成变量去用
8.1 可以赋值
# func=内存地址
def func():
print('from func')
f=func
print(f,func)
f()
8.2 可以当做函数当做参数传给另外一个函数
# func=内存地址
def func():
print('from func')
def foo(x): # x = func的内存地址
# print(x)
x()
foo(func) # foo(func的内存地址)
8.3 可以当做函数当做另外一个函数的返回值
def foo(x): # x=func的内存地址
return x # return func的内存地址
res=foo(func) # foo(func的内存地址)
print(res) # res=func的内存地址
res()
8.4 可以当做容器类型的一个元素
l=[func,]
# print(l)
l[0]()
dic={'k1':func}
print(dic)
dic['k1']()
8.5 函数对象应用示范
示范1:
def login():
print('登录功能')
def transfer():
print('转账功能')
def check_banlance():
print('查询余额')
def withdraw():
print('提现')
def register():
print('注册')
func_dic={
'1':login,
'2':transfer,
'3':check_banlance,
'4':withdraw,
'5':register
}
while True:
print("""
0 退出
1 登录
2 转账
3 查询余额
4 提现
5 注册
""")
choice = input('请输入命令编号:').strip()
if not choice.isdigit():
print('必须输入编号,傻叉')
continue
if choice == '0':
break
if choice in func_dic:
func_dic[choice]()
else:
print('输入的指令不存在')
示范2:
def login():
print('登录功能')
def transfer():
print('转账功能')
def check_banlance():
print('查询余额')
def withdraw():
print('提现')
def register():
print('注册')
func_dic = {
'0': ['退出', None],
'1': ['登录', login],
'2': ['转账', transfer],
'3': ['查询余额', check_banlance],
'4': ['提现', withdraw],
'5': ['注册', register]
}
while True:
for k in func_dic:
print(k, func_dic[k][0])
choice = input('请输入命令编号:').strip()
if not choice.isdigit():
print('必须输入编号,傻叉')
continue
if choice == '0':
break
# choice='1'
if choice in func_dic:
func_dic[choice][1]()
else:
print('输入的指令不存在')
9 函数的嵌套
函数嵌套有两种:
调用的嵌套 和 定义的嵌套
9.1 函数的嵌套调用
函数的嵌套调用就是在调用一个函数的过程中又调用其他函数
def max2(x,y):
if x > y:
return x
else:
return y
# 比较大小,返回较大值
def max2(x,y):
if x > y:
return x
else:
return y
# 比较大小,返回较大值
# 嵌套调用
def max4(a,b,c,d):
# 第一步:比较a,b得到res1
res1=max2(a,b)
# 第二步:比较res1,c得到res2
res2=max2(res1,c)
# 第三步:比较res2,d得到res3
res3=max2(res2,d)
return res3
res=max4(1,2,3,4)
print(res)
9.2 函数的嵌套定义
函数的嵌套定义就是在函数内定义其他函数
def f1():
def f2():
pass
# 圆形
# 求圆形的求周长:2*pi*radius
# radius 半径
# pi=π
def circle(radius,action=0):
from math import pi
def perimiter(radius):
return 2*pi*radius
# 求圆形的求面积:pi*(radius**2)
def area(radius):
return pi*(radius**2)
if action == 0:
return 2*pi*radius
elif action == 1:
return area(radius)
circle(33,action=0)
1