前言
矩形的面积 S = ab,只要知道任一矩形的的长和宽,就可以带入上式求得面积。这样有什么好处呢?一个公式,适用于全部矩形,一个公式,重复利用,减少了大脑的记忆负担。像这类用变量代替不变量的思想在数学中称为函数,Python中,也有类似的思想!
一、什么是函数
在数学中,函数是指输入集合与输出集合存在一种特定的映射关系,这种映射关系可以被广泛使用。在Python中,函数是指将一定量的代码块使用一个指定的函数名通过特有的语法封装起来的代码块。
函数优点:
-
大大减少重复代码
-
可以重复使用
-
程序具有扩展性
程序代码结构性提高
二、函数初始化
现在是不是觉得函数特?,来一起看看如何定义一个函数吧:
In [1]: def func(): # func是函数名,可随意更改
...: print('A') # 函数的内容
...: print('B')
...: print('C')
看完上面定义方法,其实可以发现其定义语法规则:
-
函数初始化以关键字 def 开头,之后接函数名和一对圆括号,再加一个冒号
-
函数的内容使用缩进
但是,这并不是一个标准的函数写法,标准的函数应当还有一个非常重要的部分,那就是函数的说明,放在函数名的下面,用三引号引起来,例如:
In [2]: def func():
...: '''
...: function's description # 三引号将函数的说明引起来
...: '''
...: pass
三、函数返回与调用
函数的返回:
函数是可以将结果返回的,如果想要获取函数的执行结果,可以使用关键字 return 将其返回,例如:
In [3]: def add():
...: '''
...: calculate a plus b
...: '''
...: a = 5
...: b = 6
...: c = a + b
...: return c # 将结果c返回
函数在执行过程中一旦执行到return语句,就会结束函数,并返回
函数中如果没有return语句,则默认执行完毕后返回 None
函数的调用:
一个函数定义完成后,必须要又相应的调用语句才能够执行,否则就和没写一样━━( ̄ー ̄*|||━━,调用方法如下:
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代码可以这样写:
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
函数递归的规则:
-
必须有一个明确的结束条件
-
每次进入递归后,运算规模比上一次小(否则无限大,问题无法求解,内存会被占满)
四、函数参数
在初始化函数时,可以令函数使用参数,如下:
In [6]: def add(x,y): # 函数拥有参数时,将其写在括号里即可
...: '''
...: calculate x plus y
...: '''
...: return x + y # return语句可以返回计算式
In [7]: add(1,5) # 带参数的调用方法
Out[7]: 6
形参:只是一个名字,不占用真实内存空间
In [6]: def add(x,y): # x,y就是形参,只是一个名称而已,并不占用内存空间
...: '''
...: calculate x plus y
...: '''
...: return x + y
实参:本质是一个变量,占用真实内存空间
In [7]: add(1,5) # 1,5是实参,本质为 在调用函数时,将 x=1,y=5传给add函数,相当于两变量,占用内存资源
Out[7]: 6
按位置参数调用:按形参的位置调用
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和a对应,3和b对应,5和c对应,这就是按位置参数调用
按关键参数调用:调用函数时,直接参数赋值,而不按位置赋值
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
# 直接给变量赋值,必须指定是哪个参数
注意:在调用时,位置参数可以和关键参数混合使用,但是位置参数一定在关键参数之前才行:
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
默认参数:在初始化函数时,可以给形参指定默认值,例如:
In [14]: def print_num(a,b,c=5): # 指定c的默认值为5
...: print(a)
...: print(b)
...: print(c)
在调用时,如果不需要改变c的值就可以省略不写,如果需要改变其值,调用时重新赋值即可:
In [15]: print_num(1,2) # 使用默认参数
1
2
5
In [17]: print_num(1,2,3) # 按位置参数,令c=3
1
2
3
In [19]: print_num(1,2,c=3) # 按关键参数,令c=3
1
2
3
非固定参数:有时函数在初始化时不确定要传入参数的数量,这时就要使用非固定参数了
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:将传入的多余参数构造成一个元组
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:将传入的多余参数构造成一个字典
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一起使用
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 quan_jv_bian_liang = 555
2
3
4 def func():
5 jv_bu_bian_liang = 666
6 ans = quan_jv_bian_liang + jv_bu_bian_liang
7 print(ans)
8
9
10 print(quan_jv_bian_liang) # 访问全局变量
11
12 print(jv_bu_bian_liang) # 访问局部变量
上面程序的运行结果:
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
继续修改程序,验证全局变量是否可以在函数中被访问:
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
# 可以看到全局变量在函数中可以被正确访问
继续修改程序,验证全局变量是否可以在函数内被修改:
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 # 在主程序中,全局变量并没有被修改
可以看到在函数内并不能真正的修改全局变量,如果非要在函数内修改全局变量,也不是不可以:
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
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
七、匿名函数
针对简单功能的函数,可以不显式的指定函数框架,使用匿名函数即简洁,又可以实现其功能:
In [38]: def func(x):
...: return x*x
# 上面的函数使用下面的匿名函数代替:
In [39]: func = lambda x:x*x
In [40]: func(10)
Out[40]: 100
可以看到匿名函数的定义语法:函数名 = lambda 变量 : 函数体
八、内置函数
在Python中,有许多内置函数可以直接供我们使用,之前的讲解其实已经接触很多了,现在来看看全部的内置函数:
具体的使用方法见官方文档:https://docs.python.org/3.5/library/functions.html