函数是组织好、可重复使用、用来实现单一或相关联功能的代码块。可以说函数是模块化的一种体现。定义好函数后,放在函数库中,等到需要用的时候直接调用,不仅方便重复使用,提高编码效率,还可以提高代码的简洁性和逻辑性。
7.1 如何定义一个函数
类似于C语言和Java的函数或方法的定义,定义Python函数有以下几个步骤:
- 函数代码块以def关键字开头,后接函数标识符名称和小括号();
- 任何传入的参数和自变量必须放在小括号内,小括号之间可以用于定义参数,且参数可以是任意类型;
- 函数的第一行语句可以选择性地使用文档字符串——用于存放函数说明,增加代码可读性;
- 函数内容以冒号起始,并且需要缩进;
- return [表达式]结束函数,选择性地返回一个值给调用方,不带表达式的return相当于返回none
# 1.创建函数格式:def function_name([参数1],[参数2]... ):
def function_1():
print("Hello") # 函数创建后需要调用才会被执行
# 2.调用函数
function_1() # 结果为:Hello
# 创建有参数的函数
def function_2(a):
print(a)
# 调用有参数的函数
function_2("Hello") # 结果为:Hello
7.2 函数的参数
Python中的函数参数具有灵活性,可以接受各种形式的参数。函数中的pass关键字与第6章节的作用是一致的,都是为了保证程序代码的完整性。
- 位置参数:在对函数进行调用的时候,有几个位置参数(坑)就需要传递几个参数(萝卜),否则会触发异常,并且传入的参数与函数参数列表是一一对应的。
def plus(a,b):
print(a + b)
plus(20,60) # 结果为30,如果传递三个参数,就会报错。参数需要一一对应
- 默认参数:默认参数即带有默认值的参数,在对该函数进行调用时,可以不不显示传递参数给函数。当不传递参数时,函数将使用默认值。默认值只能会执行一次,这条规则在默认值为可变对象时非常重要,所以建议默认参数使用不可变对象,因为可变对象会存储在后续调用中传递给它的参数。
def plus_default(a=1,b=2):
print(a + b)
plus_default() # 结果为:3
plus_default(600) # 结果为:602默认传递给a(按顺序赋值)
plus_default(600, 400) # 结果为:1000
plus_default(b=400) # 结果为:401,指定默认参数传值
# 第二个参数为可变对象时
def plus_default_1(a, b=[]):
b.append(a)
print(b)
plus_default_1(1) # 结果为:[1]
# 第二次调用时候,第一次存储的数据一直在列表中,不会消失
plus_default_1(2) # 期待为:[2],但结果为:[1, 2]
plus_default_1(3) # 期待为:[3],但结果为:[1, 2, 3]
def plus_default_2(a, b=None):
if b is None:
b = []
b.append(a)
print(b)
plus_default_2(1) # 结果为:[1]
plus_default_2(2) # 结果为:[2]
def function_3(a, b=4, c): # 默认参数后面必须也是默认参数
print(a)
print(b)
print(c)
function_3(1, 2, 3) # 会报错
def function_3(a, b=4, c=3):
print(a)
print(b)
print(c)
function_3(1, 2, 3) # 结果为:1 2 3(空格代表回车),并且可以换b,c的位置:function_3(1, c=4, b=3)
- 关键字参数:函数调用时,指定参数名称,成为关键字参数。在函数调用时,关键字参数必须在普通参数后面。
def function_key(a=1, b=2): # 这是默认参数,在函数定义时
print(a + b)
function_key(a=3, b=4) # 这是关键字参数,在函数调用时
function_key(2, b=6) # 关键字参数必须在普通参数后面,不然会报错,结果为:8
- 限定关键字形参(命名关键字参数):必须使用关键字方式传递参数。限定关键字形参为了限制后面几个参数只能按关键字传递,这往往是因为后面几个形参名具有十分明显的含义,显式写出有利于可读性,或者后面几个形参随着版本更迭可能会发生变化,强制关键字形式有利于保证跨版本兼容性。
def function_4(a, *, c, d): # 星号是占位
print(a, c, d)
function_4(1, c=3, d=4) # c,d必须使用关键字传参,结果为:1 3 4
- 可变参数:①*参数:最常见的变量名是args,看到该变量名,就知道变量args指向一个tuple对象。自动收集所有未匹配的位置参数到一个tuple对象中,变量名args指向了此tuple对象。②**参数:最常见的变量名是kwargs,看到该变量名,就知道变量kwargs指向一个dict对象。它会自动收集所有未匹配的关键字参数到一个dict对象中,变量名kwargs指向此dict对象。
# ------------一个*号------------
def function_5(a, b):
print(a, b)
#function_4(1) 会报错,如何解决,我向传递一个参数,但是不会报错->利用星号
def function_6(a, *b):
print(a, b)
function_6(1) # 结果为:1 (),b为元组
function_6(1, 2) # 结果为:1 (2,)
function_6(1, 2, 3, 4, 5) # 结果为:1 (2, 3, 4, 5)
# 一般星号的b参数后面换成args参数,默认的,形成习惯了,看到args就知道作用是什么!
# ------------两个**号------------
def function_7(a, **b):
print(a, b) # b指向空字典{}
function_7(1) # 结果为:1 {}
function_7(1, 2) # 会报错
function_7(1, x=2, y=3) # 结果为:1 {'x': 2, 'y': 3}
# 一般星号星号的b参数后面换成kwargs参数,默认的,形成习惯了,看到args就知道作用是什么!
函数的参数总结:
序号 | 参数的类型 | 函数的定义(形式参数) | 函数的调用(实际参数i) | 备注 |
1 | 位置实参 | √ | ||
将列表中的每个元素都转换为位置实参 | √ | 使用* | ||
2 | 关键字实参 | √ | ||
将字典中的每个键值对都转换为关键字实参 | √ | 使用** | ||
3 | 默认值形参 | √ | ||
4 | 关键字形参 | √ | 使用* | |
5 | 个数可变的位置形参 | √ | 使用* | |
6 | 个数可变的关键字形参 | √ | 使用** |
7.3 参数排列的位置
参数排列位置的注意事项:
- 可变参数,必须定义在普通参数以及默认值参数后面
- 函数定义时,二者同时存在,需要将*args放在**kwargs之前
def fucntion_name(普通参数,默认参数s="",*参数,**参数):
pass
例子:
def function_eg(a, b="learn", *args, **kwargs):
print(a, b, args, kwargs) # 结果为:1 Emily (1, 2, 3, 4) {'x': 1, 'y': 2, 'z': 3}
for i in args:
print(i, end=" ") # 结果为:1 2 3 4
for key, value in kwargs.items():
print(key, end=" ")
print(value, end=" ") # 结果为:x 1 y 2 z 3
function_eg(1, "Emily", 1, 2, 3, 4,x=1,y=2,z=3)
7.4 参数解包(拆包)
解包意义:将传递给函数的一个列表,元组,字典,拆分成独立的多个元素然后赋值给函数中的形参变量。(变量数量 = 元素数量,会进行逐一赋值,比如a, b, c = [1, 2, 3],结果为a=1 b=2 c=3)
解包有两种解法:一种用*解的只有key,一种用**解的有key、value。但是这个方法**只能在函数定义中使用。传递实参时,可以在序列类型的参数前添加*(星号),这样他会自动将序列中的元素依次作为参数传递。参数数据类型为:字符串/列表/元组/集合/字典的时候可以解包。
s = "123"
list_1 = [1, 2, 3]
tuple_1 = (4, 5, 6)
set_1 = {7, 8, 9}
dict_1 = {"a": "Emily",
"b": 18,
"c": "Python"}
def function_8(a ,b ,c):
print(a, b, c)
function_8(*s) # 结果为:1 2 3,如果s="123456"会报错,长度得于参数长度一致,多于少于都不行
function_8(*list_1) # 结果为:1 2 3,如果list_1=[1,2,3,4]会报错,长度得于参数长度一致,多于少于都不行
function_8(*tuple_1) # 结果为:4 5 6
function_8(*set_1) #结果为:8 9 7
function_8(*dict_1) # 结果为:a b c
function_8(**dict_1) # 结果为:Emily 18 Python
# 在使用两个星号得到值时,拆包的形参和实参必须对应,也就是说字典的键名和函数的参数一致!!!
"""
function_8(**dict_1)也可以这样写:
function_8(dict_1['a'], dict_1['b'], dict_1['c']) # 因为这样写很复杂,所以使用**
这里就可以理解为什么拆包的形参和实参必须对应(字典的键名和函数的参数一致)
"""
Python的参数解包和可变参数一起使用,**参数只能收集未匹配的关键字参数(字典)
def function_9(a, *args):
print(a, args)
function_9(1) # 结果为:100 ()
function_9(1, (1, 2, 3)) # 结果为:1 ((1, 2, 3),)
function_9(1, *(1, 2, 3)) # 结果为:1 (1, 2, 3),解包后:(1, 2, 3)成为一个元素,不是元组内的元素了
dict_2 = {"name": "Emily",
"age": 18,
"skill": "Python"}
def function_10(a, **kwargs):
print(a, kwargs)
function_10(1) # 结果为:1 {}
function_10(1, b=2, c=3) # 结果为:1 {'b': 2, 'c': 3}
function_10(1, **dict_2) # 结果为:1 {'name': 'Emily', 'age': 18, 'skill': 'Python'}
function_10(1, **dict_2) # 结果为:1 {'name': 'Emily', 'age': 18, 'skill': 'Python'}
7.5 变量的作用域
变量的作用域:程序代码能访问该变量的区域,根据变量的有效范围可分为:
- 局部变量:在函数内定义并使用的变量,只在函数内部有效,局部变量使用global声明,这个变量就转变成全局变量;
- 全局变量:函数体外定义的变量,可作用于函数内外;
def function_vary(a, b):
c = a + b # c是在函数体内定义的变量,为局部变量。而a b是函数的形参,作用范围也是函数内部,相当于局部变量
print(c)
name = "Emily" # name的作用范围为函数内部外部都可以使用->>>全局变量
print(name)
def function_vary2():
print(name)
function_vary2() # 结果为:Emily
def function_vary3():
global age # 函数内部定义的变量使用global声明时,这个变量实际上就变成了全局变量
age = 20
print(age)
function_vary3()
print(age) # 结果为:20
7.6 函数的返回值
在Python中,函数可以使用return返回数据,也可以不用return返回,默认返回“None”。在函数执行时,return关键字用来帮助返回处理好的数据。
def function_11():
print("Hello")
function_11() # 函数调用的返回值,可以用变量接收
r = function_11()
print(r) # 返回值为None
def function_12(a):
return a + 1
res = function_12(1)
print(res) # 返回值为2
def function_13(a, b, c):
return a + 100, b + 100, c + 100
res1 = function_13(100, 100, 100)
print(res1) # 结果为:(200, 200, 200)
print(type(res1)) # 结果为:<class 'tuple'>,返回值是一个元组数据类型
x, y, z = res1 # 依次解包元组
print(x, y, z) # 结果为:200 200 200
7.7 函数返回函数
函数里面嵌套函数且函数的返回值是函数
def function_14():
def function_15():
return [1, 2, 3]
return function_15
res2 = function_14()
print(res2) # 接收的是xyz这个函数,而不是函数的返回值,所以结果为:<function function_14.<locals>.function_15 at 0x000001CB923525E0>
res3 = res2()
print(res3) # 结果为:[1, 2, 3]
# 其实还可以这样写
def function_16():
def function_17():
return [1, 2, 3]
return function_17()
res4 = function_16()
print(res4) # 结果为:[1, 2, 3]
7.8 递归函数
递归函数:函数直接或间接调用函数本身,则该函数称为递归函数,通俗的来说就是自己调用自己。Python中的递归调用和Java、C语言的递归调用没有很大差别!
递归函数的组成部分:
- 递归调用
- 递归出口
def factorial(n):
if n == 1: # 递归出口
return 1
else:
return n * factorial(n-1) # 递归调用
print(factorial(10)) # 结果为:3628800
# 调用的过程为:
factorial(6) # 第 1 次调用 使用 6
6 * factorial(5) # 第 2 次调用 使用 5
6 * (5 * factorial(4)) # 第 3 次调用 使用 4
6 * (5 * (4 * factorial(3))) # 第 4 次调用 使用 3
6 * (5 * (4 * (3 * factorial(2)))) # 第 5 次调用 使用 2
6 * (5 * (4 * (3 * (2 * factorial(1))))) # 第 6 次调用 使用 1
6 * (5 * (4 * (3 * (2 * 1)))) # 第 6 次调用 返回
6 * (5 * (4 * (3 *2))) # 第 5 次调用 返回
6 * (5 * (4 * 6)) # 第 4 次调用 返回
6 * (5 * 24) # 第 3 次调用 返回
6 * 120 # 第 2 次调用 返回
720 # 第 1 次调用 返回