函数基础语法及使用
函数
函数是用来盛放代码的容器,一个函数就是一个功能。
作用:
1、减少代码冗余。
2、增强代码可读性。
3、使程序易于扩展。
函数使用
原则:先定义,后调用。
函数名定义规则与变量名相同,函数名尽量用动词。
# 定义函数基本结构
def 函数名():
代码体
# 定义函数的完整结构
def 函数名(参数1,参数2,参数3。。。):
"""
注释信息
"""
代码体
return 返回值
定义阶段:只检查语法,不执行代码。
和定义变量相同,先申请一块内存空间,将代码放进去,将内存空间与函数名关联。
# abc虽然没有定义变量,但这是没定义变量引发的逻辑错误,并没有语法错误,所以不会报错。
def f():
a + b + c
调用阶段:执行函数体代码。
调用阶段是先通过函数名找到代码体对应的内存空间,括号()表示执行代码体的代码。
名字对应的是内存地址,打印函数名得到的是函数体的内存地址。
def f():
pass
print(f)
<function f at 0x0000000000507160>
函数定义的三种形式
无参函数
def f1():
代码体
有参函数
def f2(x,f):
代码体
空函数
用于先定义好程序大概所要用的功能,然后再根据思路补全函数。
def f3():
pass
函数的提示信息
Python不会限制传入参数的数据类型,但我们在定义时可以提示调用者函数参数和返回值的数据类型。
def func(x:'参数1的提示信息',y:'参数2的提示信息')->'返回值得提示信息':
pass
# 查看提示信息,返回一个字典。
print(func.__annotations__)
{'x': '参数1的提示信息', 'y': '参数2的提示信息', 'return': '返回值的提示信息'}
定义时的注释
def f1(x,y):
"""
函数功能注释
:param x: 参数x的注释
:param y: 参数y的注释
:return: 返回值得注释
"""
pass
# 使用help()内置函数查看函数注释信息.
help(f1)
Help on function f1 in module __main__:
f1(x, y)
函数功能注释等
:param x: 参数x的注释
:param y: 参数y的注释
:return: 返回值得注释
函数调用的三种形式
1、语句形式。
f1()
2、表达式形式。
ret = f() + 10
3、函数调用可以当做一个参数传给另一个函数。
f1(f2())
函数的返回值
return的两种作用
1、控制返回值。
情况1:没有return或return后为空。此时返回值为None。
def f1():
pass
print(f1())
None
def f2():
return
print(f2)
None
情况二:单个返回值。
def f1():
return 10
print(f1())
10
情况三:用逗号分隔返回多个值。多个值会以元组的形式返回。
def f1(a,b,c):
return a,b,c
print(f1(1,2,3))
(1, 2, 3)
返回值的id和函数调用的id是同一个。
def f(a):
return a
res = f(3)
print(id(res), id(f(3)))
8791056713440 8791056713440
2、终止函数
函数内可以有多个return,函数执行过程中,遇到return就会立即终止函数,并将return后面的值当做本次调用的结果返回。
def f1(a,b):
if a >b:
return a
else:
return b
print(f1(10,20))
20
函数的参数
两大类:
- 形参:在定义函数时,括号内指定的参数,称之为形式参数,简称形参,本质就是变量名。
- 实参:在函数调用时,括号内传入的值,称之为实际参数,简称实参,本质就是变量值。
def total(a,b):
print(a + b)
total(10,20)
# a,b就是形参。
# 10,20就是实参。
实参与形参的关系:
在调用函数时,实参的值会绑定给形参,该绑定关系可以在函数内使用。在函数调用结束后,会解除绑定关系。
def f1(a,b):
print(a + b) # 函数内可以访问到a和b
f1(1, 2)
3
# 函数外则无法访问到a和b
print(a, b)
NameError: name 'a' is not defined
形参:
在函数调用时调用一次函数形参只能被传一次值,不能重复传值。
位置形参:
在定义函数时,按照从左到右的顺序依次定义的变量名,称之为位置形参。
特点:必须被传值,多一个不行,少一个不行。
# 必须传入和位置参数同样数量的值。
def func(a, b):
pass
func(10)
TypeError: func() missing 1 required positional argument: 'b'
# 不能重复传值。
func(111,222,a=333)
TypeError: func() got multiple values for argument 'a'
默认形参(具有默认值得形参):
在定义函数时,就已经为某个形参赋值,该形参就称为默认参数。
特点:在调用阶段可以不用给默认形参传值。若有传值,新值会覆盖默认值。
def f(x, y=10):
print(x, y)
f(20)
20 10
注意:默认形参的值是在函数定义阶段规定死,之后的改变不会影响。默认形参的值最好为不可变类。
n = 10
print(f'函数定义前 n: {id(n)}')
def f1(x, y=n): # 在定义阶段 y所指向的内存地址已经固定。
print(f'函数内 y: {id(y)}')
n = 111 # n 的值为不可变类型,在值改变后内存地址也改变,但不会影响函数已定义的值。
print(f'函数定义后 n: {id(n)}')
f1(222)
函数定义前 n: 8790942025664
函数定义后 n: 8790942028896
函数内 y: 8790942025664
222 10
注:默认形参的值若为可变类型,则会被函数定义后的改变所影响。
n = [10,20]
print(f'函数定义前 n: {id(n)}')
def f1(x, y=n): # 在定义阶段 y所指向的内存地址已经固定。
print(f'函数内 y: {id(y)}')
print(x,y)
n.append(30) # n 的值为可变类型,在值改变后内存地址不变,还是指向同一个内存地址。
print(f'函数定义后 n: {id(n)}')
f1(222)
函数定义前 n: 41272960
函数定义后 n: 41272960
函数内 y: 41272960
222 [10, 20, 30]
写函数时,函数不应该被定义之后的外界因素影响。
也就是说,在函数定义完后,函数运行结果应该是可预测的。默认形参的默认值若为可变类型,会让最后的结果充满不确定性,给人带来困惑。
可变长参数*和**
*会将溢出位置实参汇总成元组,然后赋值给其后变量名,通常应该是args。
def f(a,b,*args):
print(a,b)
print(args)
f('哼','哧','哈','嘿')
哼 哧
('哈', '嘿')
# 也可以不给*传值。结果为空元组。
f('哼','哧')
哼 哧
()
利用*写一个接收能任意个数值的求和函数。
def total(*args):
n = 0
for i in args:
n += i
return n
print(total(1,2,3,4,5))
15
**会将溢出关键字实参汇总成字典,然后赋值给其后变量名,通常应该是kwargs。
def f(a,b,**kwargs):
print(a,b)
print(kwargs)
f('哼','哧',x='哈',y='嘿')
哼 哧
{'x': '哈', 'y': '嘿'}
# 同样也可以不给**传值,结果为空字典。
哼 哧
{}
*和**的组合使用,能接收符合语法的任意实参。
def f(*args,**kwargs):
print(args)
print(kwargs)
f(1,2,3,4,5,m=6,n=7,x=8,y=9)
(1, 2, 3, 4, 5)
{'m': 6, 'n': 7, 'x': 8, 'y': 9}
命名关键字形参
在*args之后,**kwargs之前的参数,也可以有默认值,仅接受关键字实参。
def f(a,b,*args,c='嘿',d,**kwargs):
print(a,b)
print(args)
print(c,d)
print(kwargs)
f(10,20,30,40,d='哈',k1='v1',k2='v2')
10 20
(30, 40)
嘿 哈
{'k1': 'v1', 'k2': 'v2'}
形参最终顺序
语法规定,违反则报语法错误。
func(位置形参,默认形参,*args,命名关键字形参,**kwargs)
实参:
位置实参:
在调用函数时,按照从左到右的顺序依次传入的值,称之为位置实参。
特点:按照位置与形参一一对应。
# 必须传入和位置参数同样数量的值,a和b能收到什么值取决于实参的位置。
def func(a, b):
pass
func(10,20)
func(20,10)
关键字实参:
在调用函数时,按照key=value的形式指定的实参,称之为关键字实参。
特点:可以打乱顺序,但仍能为指定的形参传值。
def func(a, b):
print(a,b)
func(10,20)
10 20
func(b=20,a=10)
10 20
# 指定关键字实参时,形参名不要带引号,否则会语法报错。
func('b'=20,'a'=10)
SyntaxError: expression cannot contain assignment, perhaps you meant "=="?
可变长实参*和**
在函数调用时,*会将后面的可迭代对象拆分成位置实参。拆分后的值应与位置形参的数量一致。如果这个可迭代对象为字典,传入函数的值为字典的key。
def func(a,b,c):
print(a,b,c)
func(*[1,2,3])
1 2 3
func(*{'k1':1,'k2':2,'k3':3})
k1 k2 k3
在函数调用时,**会将后面的字典(必须是字典)拆分成关键字实参。字典的key必须是形参名。
def func(a,b,c):
print(a,b,c)
func(**{'a':1,'c':2,'b':3})
1 3 2
实参最终顺序
func(位置实参,关键字实参,*iterable,**dict)
*和**的魔性用法
在函数嵌套中,外层函数使用*和**接收任意参数,内层函数使用*和**将接收的任意参数拆分。这个方法在装饰器中会使用到。
def wrapper(*args,**kwargs): # wrapper先接收为(1,2,3) {'a':4,'b':5},传给inner
def inner(*args,**kwargs): # 最后接收到1,2,3,a=4,b=5,再通过*和**聚合为(1,2,3) {'a':4,'b':5}传给args和kwargs
print(args,kwargs)
inner(*args,**kwargs) # 调用inner接收后用*和**拆分为1,2,3,a=4,b=5 传给inner函数。
wrapper(1,2,3,a=4,b=5)
(1, 2, 3) {'a': 4, 'b': 5} # 最终输出
在变量解压赋值中,*能将溢出的值聚合成一个列表赋值给后面的变量名,该名约定俗成为下划线 _ 。
t = (1,2,3,4)
a, *_ = t
print(a, _)
1 [2, 3, 4]
*_, a = t
print(_,a)
[1, 2, 3] 4
a, *_, b = t
print(a, _, b)
1 [2, 3] 4
函数对象
函数是第一等公民
1、函数可以当变量赋值
def f1():
print('from f1')
f2 = f1 # 将f1的内存地址关联给f2
f2() # f2加括号也能调用
from f1
2、可以当做参数传给另一个函数。
def f1():
print('from f1')
def f2(x):
x()
f2(f1) # 将f1赋值给x,在f2内调用f1()
from f1
3、可以当做一个函数的返回值。
def f1():
print('from f1')
def f2(x):
return x
ret = f2(f1)
ret() # 拿到返回值f1,加括号也能调用
from f1
4、函数可以当做容器类型的元素。
def func():
return 100
l1 = [1,2,func]
res = l1[-1]() # 通过索引取到func,加()能调用
print(res)
100
通过以上特性,可以写一个函数功能字典。
def haha():
print('哈哈')
def hehe():
print('呵呵')
# 若要添加功能,只需添加函数和功能字典即可,主逻辑代码无需改变。
def hei():
print('嘿嘿')
func_dict = {
'0':['退出'],
'1':['打印哈哈',haha],
'2':['打印呵呵',hehe],
# 添加一个key:value
'3':['打印嘿嘿',hei],
}
while 1:
for i in func_dict:
print(i,func_dict[i][0])
choise = input('>>>').strip()
if choise == '0':
break
elif choise in func_dict:
func_dict[choise][-1]()
else:
print('更多功能开发中...')
函数嵌套
嵌套定义
在函数内再定义函数。
def wrapper():
def inner():
print('from inner')
return inner
# 通过调用wrapper得到返回值为inner,然后将返回值加()即可调用.
res = wrapper()
res()
from inner
使用嵌套定义,可以写一个同类型多种功能的函数,通过输入参数选择不同的功能。这样在调用时只需知道一个函数的用法即可,而无需调用多个函数。
例如,写一个计算圆的周长或面积的函数。
from math import pi
def circle(radius,mode=0):
def perimiter(radius):
return 2 * pi * radius
def area(radius):
return pi * (radius ** 2)
# 判断如果mode为0则计算周长,如果为1则计算面积。
if mode == 0:
return perimiter(radius)
elif mode == 1:
return area(radius)
else:
return 'input 0 or 1'
res = circle(10,mode=1)
print(res)
314.1592653589793
嵌套调用
在一个函数内再调用其他函数。
例如,写一个比较四个数返回最大数的函数,可以先写一个比较两个数的函数,然后通过调用这个函数来比较四个数。
def max2(a,b):
return a if a > b else b
def max4(a,b,c,d):
ret1 = max(a,b)
ret2 = max(ret1,c)
ret3 = max(ret2,d)
return ret3
res = max4(1,2,3,4)
print(res)
4
使用嵌套调用,可以让一个功能复杂的函数拆分成多个功能简单的函数,然后再通过一个函数调用多个简单函数。这样能使代码逻辑更清晰。