函数
函数是组织好的,可重复使用的,用来实现单一,或相关联功能的代码段。
函数能提高应用的模块性,和代码的重复利用率。你已经知道Python提供了许多内建函数,比如print()。但你也可以自己创建函数,这被叫做用户自定义函数。
1、定义一个函数
你可以定义一个由自己想要功能的函数,以下是简单的规则:
- 函数代码块以 def 关键词开头,后接函数标识符名称和圆括号 ()。
- 任何传入参数和自变量必须放在圆括号中间,圆括号之间可以用于定义参数。
- 函数的第一行语句可以选择性地使用文档字符串—用于存放函数说明。
- 函数内容以冒号起始,并且缩进。
- return [表达式] 结束函数,选择性地返回一个值给调用方。不带表达式的return相当于返回 None。
def test(): """ for test """ print("in the test funcation") return 0 # def 定义函数的关键字 # test 函数名(函数标识名称符) # () 任何传入参数和自变量必须放在圆括号中间,圆括号之间可以用于定义参数 # """for test""" 函数说明,说明函数作用,方便他人阅读代码 # print("in the test funcation") 函数体(代码块),注意缩进 # return 结束函数,选择性地返回一个值给调用方。不带表达式的return相当于返回 None
2、函数特征:
- 代码重复利用
- 可扩展性
- 保持一致性
优化前:

1 # 打印字符串 2 print("ABCD") 3 print("EFGH") 4 5 6 # 现在下面有2个函数,每个函数处理完了,都需要使用上面的逻辑,那么唯一的方法就是拷贝2次这样的逻辑 7 8 def test_1(): 9 "test" 10 print("in the test1") 11 print("ABCD") 12 print("EFGH") 13 14 15 def test_2(): 16 "test" 17 print("in the test2") 18 print("ABCD") 19 print("EFGH")
优化后:

1 # 新定义一个函数 2 def test(): 3 """test""" 4 print("ABCD") 5 print("EFGH") 6 7 8 # 在想执行打印字符串功能的函数中,调用test()函数就可以了 9 def test_1(): 10 """test""" 11 print("in the test1") 12 test() # 调用test()函数 13 14 def test_2(): 15 """test""" 16 print("in the test2") 17 test() # 调用test()函数
3、定义函数的三种形式
1、无参:应用场景仅仅只是执行一些操作,比如与用户交互,打印
2、有参:需要根据外部传进来的参数,才能执行相应的逻辑,比如统计长度,求最大值最小值
3、空函数:设计代码结构

1 #定义阶段 2 def tell_tag(tag,n): #有参数 3 print(tag*n) 4 5 def tell_msg(): #无参数 6 print('hello world') 7 8 #调用阶段 9 tell_tag('*',12) 10 tell_msg() 11 tell_tag('*',12) 12 13 ''' 14 ************ 15 hello world 16 ************ 17 ''' 18 19 #结论: 20 #1、定义时无参,意味着调用时也无需传入参数 21 #2、定义时有参,意味着调用时则必须传入参数

1 def auth(user,password): 2 ''' 3 auth function 4 :param user: 用户名 5 :param password: 密码 6 :return: 认证结果 7 ''' 8 pass 9 10 def get(filename): 11 ''' 12 :param filename: 13 :return: 14 ''' 15 pass 16 17 def put(filename): 18 ''' 19 :param filename: 20 :return: 21 ''' 22 def ls(dirname): 23 ''' 24 :param dirname: 25 :return: 26 ''' 27 pass 28 29 #程序的体系结构立见
4、函数的参数:
- 形参:指的是形式参数,是虚拟的,不占用内存空间,形参单元只有被调用的时才分配内存单元
- 实参:指的是实际参数,是一个变量,占用内存空间,数据传递单向,实参传给形参,形参不能传给实参
def test(x,y): #x,y是形参 print(x) print(y) test(1,2) #1和2是实参 #输出 1 2
4.1、位置参数
从上面的例子可以看出,实际参数和形式参数是一一对应的,如果调换位置,x和y被调用的时,位置也会互换,代码如下
def test(x,y): print(x) print(y) print("--------互换前-----") test(1,2) print("--------互换后-----") test(2,1) #输出 --------互换前----- 1 2 --------互换后----- 2 1
因为定义x,y两个形参,所以传递实参的时候,也只能传递两个实参,多一个或少一个都是有问题的:

1 #多传递一个参数 2 def test(x, y): 3 print(x) 4 print(y) 5 6 7 print("--------多一个参数----") 8 test(1,2,3) 9 10 #输出结果 11 Traceback (most recent call last): 12 --------多一个参数---- 13 test(1,2,3) 14 TypeError: test() takes 2 positional arguments but 3 were given #test()函数需要传两个实参,你传了三个实参 15 16 17 18 #少传递一个实参 19 def test(x, y): 20 print(x) 21 print(y) 22 23 24 print("--------少一个参数----") 25 test(1) 26 27 #输出结果 28 --------少一个参数---- 29 Traceback (most recent call last): 30 test(1) 31 TypeError: test() missing 1 required positional argument: 'y' #没有给y参数传实参
4.2关键字参数
上面的位置参数,看起来有点死,必须形参和实参的位置一一对应,不然就会传错参数,为了避免这种问题,就有了关键字参数的玩法:关键字传参不需要一一对应,只需要你指定你的哪个形参调用哪一个实参即可;
def test(x,y): print(x) print(y) print("--------互换前------") test(x=1,y=2) print("--------互换后------") test(y=2,x=1) #输出 --------互换前------ 1 2 --------互换后------ 1 2
位置参数和关键字参数结合使用:
1.位置参数在前,关键字参数在后
def test(x,y): print(x) print(y) test(1,y=2) #输出 1 2
def test(x,y,z): print(x) print(y) print(z) test(1,z=2,y=3) #输出 1 3 2
2.上面的列子是关键字传参传给y,现在传给x,代码如下:
def test(x,y): print(x) print(y) test(1,x=2) #输出结果 Traceback (most recent call last): test(1,x=2) TypeError: test() got multiple values for argument 'x' #给x形参传的值过多,之前位置参数 就传实参给x一次,后面关键字又传实参给x,造成报错
3.关键字参数在前,位置参数在后:
def test(x,y): print(x) print(y) test(y=2,1) #输出结果 test(y=2,1) ^ SyntaxError: positional argument follows keyword argument # 关键字参数在位置参数的前面
4.关键字参数在中间:
def test(x,y,z): print(x) print(y) test(1,y=2,3) #输出结果 test(1,y=2,1) ^ SyntaxError: positional argument follows keyword argument
结论:关键字参数是不能写在位置参数前面的。
5、全局变量和局部变量
5.1、局部变量
局部变量:顾名思义,指在局部生效,定义在函数体内的变量只能在函数里面生效,出个这个函数体,就不能找到它,这个函数就是这个变量的作用域,如下代码:
name = "apple" def test(name): print("before change:",name) name = "bananan" #局部变量name,只能在这个函数内生效,这个函数就是这个变量的作用域 print("after change:",name) test(name) print(name) #输出结果 before change: apple after change: bananan #局部变量生效 bananan# 外部的变量还是apple,仅函数内的name变成了bananan
5.2、全局变量
全局变量:指的是在整个程序中都生效的变量,在整个代码的顶层声明:
name = "apple" def test(name): print("before change:", name) name = "bananan" # 局部变量name,只能在这个函数内生效,这个函数就是这个变量的作用域 print("after change:", name) def test1(name): print(name) test(name) test1(name) print(name) #输出结果 before change: apple after change: bananan #局部变量生效 apple# 全局变量 apple# 全局变量
注:全局变量的优先级是低于局部变量的,当函数体内没有局部变量,才会去找全局变量
5.3、局部变量改成全局变量
- 改前用global先声明一下全局变量
- 将全局变量重新赋值
name1 = "apple" def test(name): global name1 # 使用global 声明全局变量 print("before change:", name1) name1 = "bananan" # 全局变量重新赋值 print("after change:", name1) def test1(name1): print(name1) test(name1) test1(name1) print(name1) #输出结果 before change: apple after change: bananan bananan bannann
注:最好不要用global这个关键字,因为你用了,其他人调你的函数时,就乱套了,而且还不好调试。
5.4、全局变量定义成列表
names = ['AAAA',"BBBB"] #定义一个列表 def test(): names[0] = "CCCC" print(names) print("--------test-----") test() print("------打印names--") print(names) #输出结果 --------test----- ['CCCC', 'BBBB'] #函数内的names输出 ------打印names-- ['CCCC', 'BBBB'] #函数外的names输出
注:1、只有字符串和整数是不可以被修改的,如果修改,需要在函数里面声明global。2、但是复杂的数据类型,像列表(list)、字典(dict)、集合(set)、类(class)都是可以修改的。
5.5、小实验:
局部作用域和全局作用域的访问顺序
#局部作用域和全局作用域的访问顺序 x=0 def grandpa(): x=1 def dad(): x=2 def son(): x=3 print(x) son() dad() #调用grandpa grandpa() #输出结果 3
注:作用域,只能是从里往外找,一层一层的的找。
全局变量和局部变量小结:
- 在子程序(函数)中定义的变量称为局部变量,在程序一开始定义的变量称为全局变量。
- 全局变量的作用域是整个程序,局部变量的作用域是定义该变量的子程序(函数)。
- 当全局变量和局部变量同名时:在定义局部变量的子程序内,局部变量起作用;在其他地方,全局变量起作用。