函数
一、函数介绍
在程序中,函数就具备某一功能的工具。事先将工具准备好即函数的定义,遇到应用场景拿来就用即函数的调用。
注意点:
- 函数的使用必须遵循先定义,后调用的原则
函数的使用是为了解决以下的三个问题:
- 程序冗长
- 程序的扩展性差
- 程序的可读性差
二、函数的定义与调用
函数的定义方式:
#函数定义阶段
def func(参数1,参数2,...):
'''函数的描述'''
函数体
return 值
#函数调用阶段
func(a,b,...)
func表示函数名,()里面的是函数的参数,函数的描述一般记录函数的功能(非必要),return后为函数的返回值。func(a,b,…)表示调用函数并将参数传给函数。
注意点:
- func表示函数的地址,func()才表示调用函数。
- 函数定义阶段只检测函数体的语法,不执行函数体代码,函数的调用阶段才会执行函数的代码。
#代码1
def foo():
print('from foo')
bar()
def bar():
print('from bar')
foo()
#代码2
def foo():
print('from foo')
bar()
foo()
def bar():
print('from bar')
上方的两组代码中,代码1可以正常运行,而代码2会报错,这是为什么呢?我们知道python解释器执行代码是自上而下执行的,当解释器执行到代码1的bar()处,由于bar()在foo函数内部,而函数在定义阶段只会检测是否有语法错误,所以就不会报NameError,当解释器执行到foo()处才真正调用函数bar,而foo()上方已经定义了函数bar,所以代码1可以正常运行完。在代码2中解释器执行到foo处时会调用函数foo,在foo函数中有调用了bar函数,但是在foo()处以上并没有定义函数bar,所以会报NameError。
返回值是一个函数的处理结果,也就是return后的值。
- return是一个函数结束的标志,函数内可以有多个return,但只要执行一次,整个函数就会结束运行。
- return 的返回值无类型限制,即可以是任意数据类型。
- return的返回值无个数限制,即可以用逗号分隔开多个任意类型的值。0个return:返回None,不写return默认会在函数的最后一行添加return。1个return:返回的值就是该值本身。多个return:返回值是元组。
def func(a):
b=a+1
return
c=func(1)
c
<<<None
def func(a):
b=a+1
return b
c=func(1)
c
<<<2
def func(a):
b=a+1
c=b+1
return b,c
d=func(1)
d
<<<(2,3)
函数的提示信息:
def func(a:float) -> int:
return int(a)
func(1.5)
<<<1
上面的代码中的float表示提示用户a一个传入浮点数,->int则提示用户返回值应该为整数。提示信息只起到提示作用,不会影响程序的运行。
三、函数的三种形式
有参函数:
def sum2(x,y):
return x+y
sum2(10,20)
<<<30
无参函数:
def func():
print('hello world')
func()
<<<'hello world'
空函数:
def func():
pass
四、函数的参数
形参::在函数定义阶段括号内定义的参数,其本质就是变量名。
实参:在函数调用阶段括号内传入的值,其本质就是变量的值。
下面介绍以下参数的几种类型:
1.位置参数
位置形参:在函数定义阶段,按照从左到右的顺序依次定义的形参。
特点:但凡是按照位置定义的形参,都必须被传值。
def foo(x,y):
print('x:%s'%x)
print('y:%s'%y)
位置实参:在函数调用阶段,按照从左到右的顺序依次定义的实参。
特点:按照位置为对应的形参依次传值。
foo(1,2)
<<<x:1
<<<y:2
2.关键字实参
关键字实参:在调用函数时,按照key=value的形式为指定的参数传值。
特点:可以打破位置的限制。
注意点:
- 关键字实参必须放在位置实参后面
def foo(x,y,z):
print('x:%s'%x)
print('y:%s'%y)
print('z:%s'%z)
foo(1,z=3,y=2)
<<<x:1
<<<y:2
<<<z:3
3.默认形参
默认形参数:在函数定义阶段,就已经为形参赋值,该形参称为默认形参。
特点:在定义阶段就已经被赋值,意味着在调用可以不用为其赋值,也可以为其赋值。
注意点:
- 位置形参必须放到默认形参的前面,否则报语法错误
- 默认参数的值通常应该定义不可变类型,如果使用可变类型作为默认参数值,那么所有调用该函数但未提供实参的实例都会共享同一个可变对象。这可能会导致一些意想不到的副作用。
- 默认参数的值只在定义阶段赋值一次,即默认参数的值在函数定义阶段就已经固定死了
def foo(x,y=10):
print('x:',x)
print('y:',y)
foo(1)
<<<x:1
<<<y:10
foo(1,2)
<<<x:1
<<<y:2
#默认形参在函数定义阶段赋值
m=10
def foo(x=m,y=11):
print(x)
print(y)
m=1111111111111111111111111111111111111111111111111111111111
foo()
<<<10
<<<11
#把默认形参设为可变类型会出现的问题
#如果把默认形参定义为了可变类型,在函数运行完后,垃圾回收机制不会回收这个局部变量。
def func(x,y,z=[]):
z.append(y)
print('%s的爱好为%s'%(x,z))
func('张三','吃西瓜')
func('李四','游泳')
func('王五','睡觉')
<<<张三的爱好为['吃西瓜']
<<<李四的爱好为['吃西瓜', '游泳']
<<<王五的爱好为['吃西瓜', '游泳', '睡觉']
4.可变长度参数
可变长参数:指的是在调用函数时,传入的参数个数可以不固定。
而调用函数时,传值的方式无非两种,一种位置实参,另一种时关键字实参,所以对应的可变长度形参也必须有两种解决方案,来分别接收溢出的位置实参(*)与关键字实参(**)。
形参中某个参数带形参中的会将溢出的位置实参全部接收,然后存储元组的形式,然后把元组赋值给*后的变量名。
def foo(x,y,*args):
print(x)
print(y)
print(args)
foo(1,2,3,4,5,6,7)
<<<1
<<<2
<<<(3, 4, 5, 6, 7)
实参中带*,*会将该参数的值循环取出,打散成位置实参。
a=[1,2,3]
def func(x,y,z):
print(x,y,z)
func(*a)
<<<1 2 3
形参中的** 会将溢出的关键字实参全部接收,然后存储字典的形式,然后把字典赋值给**后的变量名。
def foo(x,y,**kwargs):
print(x)
print(y)
print(kwargs)
foo(1,2,a=3,b=4,c=5)
<<<1
<<<2
<<<{'a':3,'b':4,'c':5}
实参中带字典**,**会将该参数的值循环取出,打散成关键字实参
a={'x':1,'y':2,'z':3}
def func(x,y,z):
print(x,y,z)
func(**a)
<<<1 2 3
注:约定俗成形参中的*变量名的写法都是:*args,约定俗成形参中的**变量名的写法都是:**kwargs。
5.命名关键字参数
命名关键字形参:在函数定义阶段,*,后面的参数都是命名关键字参数
特点:在传值时,必须按照key=value的传,并且key必须命名为关键字参数指定的参数名
def func(a,b,*,c,d):
print(a,b,c,d)
func(1,2,c=3,d=4)
<<<1 2 3 4
#命名关键字参数可以有默认值
def func(a,b,*,c=5,d):
print(a,b,c,d)
func(1,2,d=4)
<<<1 2 5 4
#当形参中有*args时,命名关键字参数可写为如下形式
def func(a,b,*args,c=5,d):
print(a,b,args,c,d)
func(1,2,3,4,d=6)
<<<1 2 (3,4) 5 6
6.参数的组合使用
位置参数、默认参数、*args、命名关键字参数、**kwargs
#当函数中同时出现默认形参和*args时,如果想给args传参,默认形参就必须传值
def func(a,b=2,*args,c=5,**kwargs):
print(a,b,args,c,kwargs)
func(1,1,3,4,d=6,e=7,f=8)
<<<1 1 (3, 4) 5 {'d': 6, 'e': 7, 'f': 8}