#12 Python函数
矩形的面积 S = ab,只要知道任一矩形的的长和宽,就可以带入上式求得面积。这样有什么好处呢?一个公式,适用于全部矩形,一个公式,重复利用,减少了大脑的记忆负担。像这类用变量代替不变量的思想在数学中称为函数,Python中,也有类似的思想!
一、什么是函数
在数学中,函数是指输入集合与输出集合存在一种特定的映射关系,这种映射关系可以被广泛使用。在Python中,函数是指将一定量的代码块使用一个指定的函数名通过特有的语法封装起来的代码块。
函数优点:
程序代码结构性提高
二、函数初始化
现在是不是觉得函数特🐮,来一起看看如何定义一个函数吧:
1 2 3 4
| In [1]: def func(): ...: print('A') ...: print('B') ...: print('C')
|
看完上面定义方法,其实可以发现其定义语法规则:
- 函数初始化以关键字 def 开头,之后接函数名和一对圆括号,再加一个冒号
- 函数的内容使用缩进
但是,这并不是一个标准的函数写法,标准的函数应当还有一个非常重要的部分,那就是函数的说明,放在函数名的下面,用三引号引起来,例如:
1 2 3 4 5
| In [2]: def func(): ...: ''' ...: function's description # 三引号将函数的说明引起来 ...: ''' ...: pass
|
三、函数返回与调用
函数的返回:
函数是可以将结果返回的,如果想要获取函数的执行结果,可以使用关键字 return 将其返回,例如:
1 2 3 4 5 6 7 8
| In [3]: def add(): ...: ''' ...: calculate a plus b ...: ''' ...: a = 5 ...: b = 6 ...: c = a + b ...: return c
|
函数在执行过程中一旦执行到return语句,就会结束函数,并返回
函数中如果没有return语句,则默认执行完毕后返回 None
函数的调用:
一个函数定义完成后,必须要又相应的调用语句才能够执行,否则就和没写一样━━( ̄ー ̄|||━━,调用方法如下:
1 2 3 4 5 6 7 8
| In [4]: def tell_name(): ...: ''' ...: print your name ...: ''' ...: print('I am MinuteSheep')
In [5]: tell_name() I am MinuteSheep
|
函数的递归:在函数内部返回调用自己
大家一定做过这种类型的题目:
解题步骤:f(20)=f(10)=f(5)= 5 + 3 = 8,可以看到,当x>=10时,就是自己调用自己,这就是递归,Python代码可以这样写:
1 2 3 4 5 6 7 8 9 10 11 12
| In [20]: def func(x): ...: if x < 10: ...: ans = x + 3 ...: print(ans) ...: return ans ...: else: ...: return func(x/2) ...:
In [21]: func(20) 8.0 Out[21]: 8.0
|
函数递归的规则:
- 必须有一个明确的结束条件
- 每次进入递归后,运算规模比上一次小(否则无限大,问题无法求解,内存会被占满)
四、函数参数
在初始化函数时,可以令函数使用参数,如下:
1 2 3 4 5 6 7 8
| In [6]: def add(x,y): ...: ''' ...: calculate x plus y ...: ''' ...: return x + y
In [7]: add(1,5) Out[7]: 6
|
形参:只是一个名字,不占用真实内存空间
1 2 3 4 5
| In [6]: def add(x,y): ...: ''' ...: calculate x plus y ...: ''' ...: return x + y
|
实参:本质是一个变量,占用真实内存空间
1 2
| In [7]: add(1,5) Out[7]: 6
|
按位置参数调用:按形参的位置调用
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15
| In [8]: def print_num(a,b,c): ...: ''' ...: print num of nums ...: ''' ...: print(a) ...: print(b) ...: print(c)
In [9]: print_num(1,3,5)
1 3 5
|
按关键参数调用:调用函数时,直接参数赋值,而不按位置赋值
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15
| In [8]: def print_num(a,b,c): ...: ''' ...: print num of nums ...: ''' ...: print(a) ...: print(b) ...: print(c) ...:
In [11]: print_num(b=7,a=1,c=10) 1 7 10
|
注意:在调用时,位置参数可以和关键参数混合使用,但是位置参数一定在关键参数之前才行:
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19
| In [8]: def print_num(a,b,c): ...: ''' ...: print num of nums ...: ''' ...: print(a) ...: print(b) ...: print(c) ...:
In [12]: print_num(5,c=1,b=10) 5 10 1
In [13]: print_num(a=4,6,c=10) File "<ipython-input-13-99c9e145d0a7>", line 1 print_num(a=4,6,c=10) ^ SyntaxError: positional argument follows keyword argument
|
默认参数:在初始化函数时,可以给形参指定默认值,例如:
1 2 3 4
| In [14]: def print_num(a,b,c=5): ...: print(a) ...: print(b) ...: print(c)
|
在调用时,如果不需要改变c的值就可以省略不写,如果需要改变其值,调用时重新赋值即可:
1 2 3 4 5 6 7 8 9 10 11 12 13 14
| In [15]: print_num(1,2) 1 2 5
In [17]: print_num(1,2,3) 1 2 3
In [19]: print_num(1,2,c=3) 1 2 3
|
非固定参数:有时函数在初始化时不确定要传入参数的数量,这时就要使用非固定参数了
1 2 3 4 5 6 7 8 9 10 11
| In [22]: def print_num(a,b,c): ...: print(a,b,c) ...:
In [23]: print_num(1,2,3,4) --------------------------------------------------------------------------- TypeError Traceback (most recent call last) <ipython-input-23-a8e0af192084> in <module> ----> 1 print_num(1,2,3,4)
TypeError: print_num() takes 3 positional arguments but 4 were given
|
*args:将传入的多余参数构造成一个元组
1 2 3 4 5 6 7 8
| In [26]: def print_num(a,b,c,*args): ...: print(a,b,c) ...: print(args) ...:
In [27]: print_num(1,2,3,4,5,6,7,8,9,0) 1 2 3 (4, 5, 6, 7, 8, 9, 0)
|
**kwargs:将传入的多余参数构造成一个字典
1 2 3 4 5 6 7 8
| In [28]: def print_num(a,b,c,**kwargs): ...: print(a,b,c) ...: print(kwargs) ...:
In [29]: print_num(1,2,3,f=3,g=45,y=123) 1 2 3 {'g': 45, 'y': 123, 'f': 3}
|
最完美的办法:*args和**args一起使用
1 2 3 4 5 6 7 8 9 10
| In [30]: def print_num(a,b,c,*args,**kwargs): ...: print(a,b,c) ...: print(args) ...: print(kwargs) ...:
In [32]: print_num(1,2,3,4,4,5,f=45,g=23,u=345) 1 2 3 (4, 4, 5) {'g': 23, 'u': 345, 'f': 45}
|
五、函数变量
在Python中,有两种变量:全局变量和局部变量
全局变量:在整个程序中都可以访问的变量,分配在全局数据段,在程序开始运行的时候被加载,生存周期从程序开始到程序结束,也就是说,全局变量被定义在主程序中
局部变量:存在于某个函数或类中,只有在函数或类中才能够被访问,只分配在程序的堆栈中,生存周期从函数开始到函数结束,也就是说,局部变量被定义在子模块中
1 2 3 4 5 6 7 8 9 10 11 12
| quan_jv_bian_liang = 555
def func(): jv_bu_bian_liang = 666 ans = quan_jv_bian_liang + jv_bu_bian_liang print(ans)
print(quan_jv_bian_liang)
print(jv_bu_bian_liang)
|
上面程序的运行结果:
1 2 3 4 5 6
| E:\PythonProjects\跟着MS学Python>python -u "e:\PythonProjects\跟着MS学Python\#12\1.py" 555 Traceback (most recent call last): File "e:\PythonProjects\跟着MS学Python\#12\1.py", line 12, in <module> print(jv_bu_bian_liang) NameError: name 'jv_bu_bian_liang' is not defined
|
继续修改程序,验证全局变量是否可以在函数中被访问:
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16
| quan_jv_bian_liang = 555
def func(): jv_bu_bian_liang = 666 ans = quan_jv_bian_liang + jv_bu_bian_liang print(ans)
func()
E:\PythonProjects\跟着MS学Python>python -u "e:\PythonProjects\跟着MS学Python\#12\1.py" 1221
|
继续修改程序,验证全局变量是否可以在函数内被修改:
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17
| quan_jv_bian_liang = 555
def func(): jv_bu_bian_liang = 666 quan_jv_bian_liang = 777 ans = quan_jv_bian_liang + jv_bu_bian_liang print(ans)
func() print(quan_jv_bian_liang)
E:\PythonProjects\跟着MS学Python>python -u "e:\PythonProjects\跟着MS学Python\#12\1.py" 1443 555
|
可以看到在函数内并不能真正的修改全局变量,如果非要在函数内修改全局变量,也不是不可以:
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19
| quan_jv_bian_liang = 555
def func(): jv_bu_bian_liang = 666 global quan_jv_bian_liang quan_jv_bian_liang = 777 ans = quan_jv_bian_liang + jv_bu_bian_liang print(ans)
func() print(quan_jv_bian_liang)
E:\PythonProjects\跟着MS学Python>python -u "e:\PythonProjects\跟着MS学Python\#12\1.py" 1443 777
|
六、高阶函数
当函数的参数是函数时,这个函数被称为高阶函数=====( ̄▽ ̄*)b
1 2 3 4 5 6 7 8 9
| In [33]: def dunc(x): ...: return x*x ...:
In [36]: def add(a,b,f): ...: print(f(a) + f(b))
In [37]: add(1,2,dunc) 5
|
七、匿名函数
针对简单功能的函数,可以不显式的指定函数框架,使用匿名函数即简洁,又可以实现其功能:
1 2 3 4 5 6 7 8 9
| In [38]: def func(x): ...: return x*x
In [39]: func = lambda x:x*x
In [40]: func(10) Out[40]: 100
|
可以看到匿名函数的定义语法:函数名 = lambda 变量 : 函数体
八、内置函数
在Python中,有许多内置函数可以直接供我们使用,之前的讲解其实已经接触很多了,现在来看看全部的内置函数: