1. 函数基础
什么是函数
函数:一个功能独立的代码块
示例:
# 定义一个函数,能够完成打印信息的功能
def print_info():
print('------------------------------------')
print(' 人生苦短,我用Python ')
print('------------------------------------')
作用
函数在编程中是非常重要的一个知识点,大体功能如下
- 能够将部分代码划分为一个整体的功能
- 只需定义函数1次就可以使用N次,可以大大减少了代码的冗余
- 代码看上去更加整洁美观
示例一:未使用函数的情况
# 输出第1遍 佛祖镇楼
print(" _ooOoo_ ")
print(" o8888888o ")
print(" 88 . 88 ")
print(" (| -_- |) ")
print(" O\\ = /O ")
print(" ____/`---'\\____ ")
print(" . ' \\| |// `. ")
print(" / \\||| : |||// \\ ")
print(" / _||||| -:- |||||- \\ ")
print(" | | \\\\\\ - /// | | ")
print(" | \\_| ''\\---/'' | | ")
print(" \\ .-\\__ `-` ___/-. / ")
print(" ___`. .' /--.--\\ `. . __ ")
print(" ."" '< `.___\\_<|>_/___.' >'"". ")
print(" | | : `- \\`.;`\\ _ /`;.`/ - ` : | | ")
print(" \\ \\ `-. \\_ __\\ /__ _/ .-` / / ")
print(" ======`-.____`-.___\\_____/___.-`____.-'====== ")
print(" `=---=' ")
print(" ")
print(" ............................................. ")
print(" 佛祖镇楼 BUG辟易 ")
print(" 佛曰: ")
print(" 写字楼里写字间,写字间里程序员; ")
print(" 程序人员写程序,又拿程序换酒钱。 ")
print(" 酒醒只在网上坐,酒醉还来网下眠; ")
print(" 酒醉酒醒日复日,网上网下年复年。 ")
print(" 但愿老死电脑间,不愿鞠躬老板前; ")
print(" 奔驰宝马贵者趣,公交自行程序员。 ")
print(" 别人笑我忒疯癫,我笑自己命太贱; ")
print(" 不见满街漂亮妹,哪个归得程序员?")
# 输出第2遍 佛祖镇楼
print(" _ooOoo_ ")
print(" o8888888o ")
print(" 88 . 88 ")
print(" (| -_- |) ")
print(" O\\ = /O ")
print(" ____/`---'\\____ ")
print(" . ' \\| |// `. ")
print(" / \\||| : |||// \\ ")
print(" / _||||| -:- |||||- \\ ")
print(" | | \\\\\\ - /// | | ")
print(" | \\_| ''\\---/'' | | ")
print(" \\ .-\\__ `-` ___/-. / ")
print(" ___`. .' /--.--\\ `. . __ ")
print(" ."" '< `.___\\_<|>_/___.' >'"". ")
print(" | | : `- \\`.;`\\ _ /`;.`/ - ` : | | ")
print(" \\ \\ `-. \\_ __\\ /__ _/ .-` / / ")
print(" ======`-.____`-.___\\_____/___.-`____.-'====== ")
print(" `=---=' ")
print(" ")
print(" ............................................. ")
print(" 佛祖镇楼 BUG辟易 ")
print(" 佛曰: ")
print(" 写字楼里写字间,写字间里程序员; ")
print(" 程序人员写程序,又拿程序换酒钱。 ")
print(" 酒醒只在网上坐,酒醉还来网下眠; ")
print(" 酒醉酒醒日复日,网上网下年复年。 ")
print(" 但愿老死电脑间,不愿鞠躬老板前; ")
print(" 奔驰宝马贵者趣,公交自行程序员。 ")
print(" 别人笑我忒疯癫,我笑自己命太贱; ")
print(" 不见满街漂亮妹,哪个归得程序员?")
示例二:使用函数的情况
# 定义一个函数,完成了独立输出“佛祖镇楼”的功能
def print_fozu():
print(" _ooOoo_ ")
print(" o8888888o ")
print(" 88 . 88 ")
print(" (| -_- |) ")
print(" O\\ = /O ")
print(" ____/`---'\\____ ")
print(" . ' \\| |// `. ")
print(" / \\||| : |||// \\ ")
print(" / _||||| -:- |||||- \\ ")
print(" | | \\\\\\ - /// | | ")
print(" | \\_| ''\\---/'' | | ")
print(" \\ .-\\__ `-` ___/-. / ")
print(" ___`. .' /--.--\\ `. . __ ")
print(" ."" '< `.___\\_<|>_/___.' >'"". ")
print(" | | : `- \\`.;`\\ _ /`;.`/ - ` : | | ")
print(" \\ \\ `-. \\_ __\\ /__ _/ .-` / / ")
print(" ======`-.____`-.___\\_____/___.-`____.-'====== ")
print(" `=---=' ")
print(" ")
print(" ............................................. ")
print(" 佛祖镇楼 BUG辟易 ")
print(" 佛曰: ")
print(" 写字楼里写字间,写字间里程序员; ")
print(" 程序人员写程序,又拿程序换酒钱。 ")
print(" 酒醒只在网上坐,酒醉还来网下眠; ")
print(" 酒醉酒醒日复日,网上网下年复年。 ")
print(" 但愿老死电脑间,不愿鞠躬老板前; ")
print(" 奔驰宝马贵者趣,公交自行程序员。 ")
print(" 别人笑我忒疯癫,我笑自己命太贱; ")
print(" 不见满街漂亮妹,哪个归得程序员?")
# 调用2次函数,每次输出1个佛祖镇楼
print_fozu()
print_fozu()
函数分类
函数根据是否是自己编写可以分为2类
- 别人已经写好拿来直接用的函数,成为包括Python内置函数或者第三方包中的函数,更省事
- 开发者自己编写的函数(通常称为自定义函数);根据需要自己编写,更灵活
在整个函数相关知识学习中,我们主要以研究“自定义函数”为主,当“自定义函数”都弄明白了弄懂了,那么用别人的写好的函数就信手拈来了
自定义函数
上面的知识我们知道,函数就是一个独立功能的代码块,想要用函数一定要掌握2块知识
- 定义函数(即怎样制作函数)
- 调用函数(即怎样使用函数)
下面我们就来研究函数的定义以及调用
定义函数
定义函数的格式如下:
def 函数名(形参):
函数体
说明:
- 函数名:一定有,按照命名规则起个名字,注意Python中建议使用“小写加下划线”的方式,例如
print_info
而不用PrintInfo
- 函数体:一定有,就是整个函数真正要执行的代码,注意函数体前面缩进4个空格
- 形参:可有可无,专门用过来存储调用时传递给函数的数据用的
示例代码
下面定义一个函数,完成打印菜单的功能
def print_menu():
print("-----------------------")
print(" 欢迎光临 ")
print("数字1: 黄瓜炒肉")
print("数字2: 冬瓜炒肉")
print("数字3: 西瓜炒肉")
print("数字4: 南瓜炒肉")
print("数字5: 北瓜炒肉")
print("-----------------------")
说明:
- 函数名:
print_menu
- 函数体:第2~9行的代码
- 形参:无
注意:
- 定义了函数,这个函数不会被执行。就好比你发明了一个功能,这个功能不能自己用自己,必须等待别人使用才行,如果想让函数执行,就需要调用函数
调用函数
通俗的讲:定义函数相当于打造了一个工具,调用函数相当于使用这个工具完成想要做的事情
调用函数的格式如下
函数名(实参)
说明:
- 函数名:一定有,想要调用的函数的名字
- 实参:可有可无,调用函数时给它传递的数据
注意:
- 调用的函数的名字必须是先定义好的才能够调用,否则会失败
示例代码
# 定义了一个函数,实现打印菜单功能
def print_menu():
print("-----------------------")
print(" 欢迎光临 ")
print("数字1: 黄瓜炒肉")
print("数字2: 冬瓜炒肉")
print("数字3: 西瓜炒肉")
print("数字4: 南瓜炒肉")
print("数字5: 北瓜炒肉")
print("-----------------------")
# 调用函数
print_menu()
运行结果:
-----------------------
欢迎光临
数字1: 黄瓜炒肉
数字2: 冬瓜炒肉
数字3: 西瓜炒肉
数字4: 南瓜炒肉
数字5: 北瓜炒肉
-----------------------
说明:
- 函数定义一定要在函数的调用之前,其原因:Python解释器在解析
.py
文件时会从文件的开头开始解析,会先遇到函数的定义,然后当遇到函数的调用时就知道它在哪,所以函数要先定义才能调用
pass 关键字
如果一个函数,暂时不确定函数体的代码,可以用pass
先占用位置,这样即没有语法错误,也能够让开发人员知道 这个函数功能没有实现,可以在后面进行编写
def 函数名():
pass
示例:要定义4个函数,实现加减乘除的功能,但还不清楚具体功能怎样写,此时就可以用pass
# 定义了4个函数
def add_2_nums():
pass
def min_2_nums():
pass
def mult_2_nums():
pass
def div_2_nums():
pass
# 分别调用函数
add_2_nums()
min_2_nums()
mult_2_nums()
div_2_nums()
如果想清楚了具体的函数应该怎样实现,那么把pass
删除,补上需要的代码即可
# 定义了4个函数
def add_2_nums():
print("接下来要进行加法操作...")
num1 = input("请输入第1个数:")
num2 = input("请输入第2个数:")
print("%s+%s=%d" % (num1, num2, int(num1) + int(num2)))
def min_2_nums():
print("接下来要进行减法操作...")
num1 = input("请输入第1个数:")
num2 = input("请输入第2个数:")
print("%s-%s=%d" % (num1, num2, int(num1) - int(num2)))
def mult_2_nums():
print("接下来要进行乘法操作...")
num1 = input("请输入第1个数:")
num2 = input("请输入第2个数:")
print("%s*%s=%d" % (num1, num2, int(num1) * int(num2)))
def div_2_nums():
print("接下来要进行除法操作...")
num1 = input("请输入第1个数:")
num2 = input("请输入第2个数:")
print("%s/%s=%d" % (num1, num2, int(num1) / int(num2)))
# 分别调用函数
add_2_nums()
min_2_nums()
mult_2_nums()
div_2_nums()
注意:
- Python代码规范中提到,定了多个函数,那么函数与函数之间空2行
定义一次执行N次
一个函数可以进行多次调用,每次调用函数时,函数都会从头开始执行,当这个函数中的代码执行完毕后,意味着调用结束了
def test():
num = 100
num += 1
print("在test函数中num=%d" % num)
test()
test()
运行结果:
在test函数中num=101
在test函数中num=101
说明:
- 第7行调用了第一次test函数,当test函数结束后num为101
- 第8行调用了第二次test函数,依然会从test函数的开头开始执行,所以结果依然是101
内置函数
内置函数:Python自带的函数,尽情的用,对我们的开发有极大的帮助
Python之所以越来越受到开发者的喜爱,原因之一就是:它丰富的内置函数,基本上需要的功能Python都有了
常用的内置函数如下示例
cmp
:
>>> cmp([1, 2], [3, 4])
-1
>>> cmp([1, 2], [1, 1])
1
>>> cmp([1, 2], [1, 2, 3])
-1
>>> cmp({"a":1}, {"b":1})
-1
>>> cmp({"a":2}, {"a":1})
1
>>> cmp({"a":2}, {"a":2, "b":1})
-1
注意:cmp在比较字典数据时,先比较键,再比较值。
len
:
>>> len("hello abcdef")
12
>>> len([1, 2, 3, 4])
4
>>> len((3,4))
2
>>> len({"a":1, "b":2})
2
注意:len在操作字典数据时,返回的是键值对个数。
max
:
>>> max("hello abcdef")
'o'
>>> max([1,4,522,3,4])
522
>>> max({"a":1, "b":2})
'b'
>>> max({"a":10, "b":2})
'b'
>>> max({"c":10, "b":2})
'c'
del
:
del有两种用法,一种是del加空格
,另一种是del()
>>> a = 1
>>> a
1
>>> del a
>>> a
Traceback (most recent call last):
File "<stdin>", line 1, in <module>
NameError: name 'a' is not defined
>>> a = ['a', 'b']
>>> del a[0]
>>> a
['b']
>>> del(a)
>>> a
Traceback (most recent call last):
File "<stdin>", line 1, in <module>
NameError: name 'a' is not defined
time
:
开发中,经常需要打印一些调试的信息,此时就又必须要输出时间,这就需要一些时间函数
import time # 引入time模块
currentTime = time.time()
print("当前时间戳为:", currentTime)
运行结果:
当前时间戳为: 1598952056.126486
random
:
import random
a = random.uniform(1, 5)
print("a =", a)
b = random.randint(10, 50)
print("b =", b)
c = random.randrange(0, 51, 2)
print("c =", c)
运行结果(每次结果都可能不同):
a = 2.018423712655862
b = 14
c = 14
内置函数有很多,在后面的课程中我们根据实际情况加以使用
切记:用到哪些内置函数就查询哪些内置函数,不用背,但要会查询会应用即可
函数说明文档
函数说明文档,顾名思义就是一个对函数说明的信息文档
格式如下:
def 函数名():
"""
用3对双引号进行包裹,这里就是函数的说明文档,用来对函数的功能,使用方式等进行说明
以便开发人员能够快速的了解此函数
"""
pass
函数文档对于我们的意义:
Python内置了很多的函数,但这些函数具体的功能不是我们编写的,我们很难在较短的时间内读懂函数的实现代码
但是只要有了函数说明文档,我们就能快速的知道这个函数的作用,能够大大提高可读性
那怎样用函数的说明文档呢?
Help on built-in function print in module builtins:
print(...)
print(value, ..., sep=' ', end='\n', file=sys.stdout, flush=False)
Prints the values to a stream, or to sys.stdout by default.
Optional keyword arguments:
file: a file-like object (stream); defaults to the current sys.stdout.
sep: string inserted between values, default a space.
end: string appended after the last value, default a newline.
flush: whether to forcibly flush the stream.
给大家的建议:
编写的代码不仅仅是自己看,还有可能是同事看、同学看、领导看等
当我们的代码中有函数,虽然函数说明文档可写可不写,但是建议大家写上,这样会更加规范
函数参数
引入
阅读如下代码,思考问题:下面的代码实现了2个数的加减乘除,并且把结果打印出来,运行如下代码感觉到哪些地方不太好吗?
# 定义了4个函数
def add_2_nums():
print("接下来要进行加法操作...")
num1 = input("请输入第1个数:")
num2 = input("请输入第2个数:")
print("%s+%s=%d" % (num1, num2, int(num1) + int(num2)))
def min_2_nums():
print("接下来要进行减法操作...")
num1 = input("请输入第1个数:")
num2 = input("请输入第2个数:")
print("%s-%s=%d" % (num1, num2, int(num1) - int(num2)))
def mult_2_nums():
print("接下来要进行乘法操作...")
num1 = input("请输入第1个数:")
num2 = input("请输入第2个数:")
print("%s*%s=%d" % (num1, num2, int(num1) * int(num2)))
def div_2_nums():
print("接下来要进行除法操作...")
num1 = input("请输入第1个数:")
num2 = input("请输入第2个数:")
print("%s/%s=%d" % (num1, num2, int(num1) / int(num2)))
# 分别调用函数
add_2_nums()
min_2_nums()
mult_2_nums()
div_2_nums()
上述的代码,虽然能够实现2个数的加减乘除,但有个较大的问题:4个函数中每次都需要重新获取这2个数字,我们如果想要计算1和2的加减乘除的结果,就需要输入4遍数字1,4遍数字2,这太麻烦了
想要解决这个问题,大体的思路应该是,在调用加减乘除这4个函数之前先获取要操作的2个数字,然后将这2个数字传递给函数让它们直接用即可而不是每个函数都重新获取
Python中如果在调用函数时,需要将数据传递给函数,这就用到了一个新的"传参数"
使用流程
想要实现”传参数“的功能,只要注意2点即可:
- 定义函数时
()
中定义变量,变量的个数根据需要的个数来确定,这些变量的目的是用来存储传递过来的数据。我们把这些变量称之为形参
- 调用函数时
()
中写入要传递的数据,可以是常量、表达式、变量等,这些就是要真实传递的数据。我们把这些数据称之为实参
如下示例,展示了调用一个函数将数字100、200都传递给它
def test(num1, num2): # 形参:调用函数时用来存储数据的变量
print("传递过来的第1个数是:%d" % num1)
print("传递过来的第2个数是:%d" % num2)
print("它们俩的和是:%d" % (num1 + num2))
test(100, 200) # 实参:在调用函数时传入具体的值
运行结果:
传递过来的第1个数是:100
传递过来的第2个数是:200
它们俩的和是:300
练习
要求:定义一个函数,有3个形参,函数体中要实现的功能为第1个形参+第2个形参-第3个形参 输出结果到终端
参考代码:
def test(num1, num2, num3):
print(num1+num2-num3)
test(100, 200, 300)
函数返回值
引入
现实生活中的场景:
- 我给儿子10块钱,让他给我买包烟。这个例子中,10块钱是我给儿子的,就相当于调用函数时传递到参数,让儿子买烟这个事情最终的目标是,让他把烟给带回来然后给你。此时烟就是
返回值
开发中的场景:
- 定义了一个函数,它检测到室内的温度,想一想是不是应该把这个结果给调用者,只有调用者拥有了这个返回值,才能够根据当前的温度做适当的调整
综上所述:
- 所谓“返回值”,就是函数完成一件事情后,最后给调用者的结果
使用流程
想要用函数的返回结果,我们需要注意2点
- 定义函数时,需要使用
return
将结果返回 - 调用函数时,需要存储这个返回值(当然了语法上来讲可以不存,但一般情况下都会存储,否则还要返回值干什么)
定义函数时,使用return
示例:
def add_2_nums(a, b):
c = a + b
return c # 此时就相当于把变量c的结果返回
定义函数时保存返回值
在本小节刚开始的时候,说过的“买烟”的例子中,最后儿子给你烟时,你一定是从儿子手中接过来 对么,程序也是如此,如果一个函数返回了一个数据,那么想要用这个数据,那么就需要保存
保存函数的返回值示例如下:
# 定义函数
def add_2_nums(a, b):
c = a + b
return c
# 调用函数,顺便保存函数的返回值
result = add_2_nums(200, 98)
# 因为result已经保存了add_2_nums的返回值,所以接下来就可以使用了
print(result)
结果:
298
注意点
return
实际上有两个作用:
- 返回数据给调用方
- 结束函数的执行
猜猜看如下程序执行完毕后,会输出什么?
def test(num1, num2, num3):
print("test-----1----")
return 100
print("test-----2----")
print(num1+num2-num3)
ret = test(100, 200, 300)
print(ret)
运行结果:
test-----1----
100
可见return
不仅有返回一个数据给调用方的功能,还有结束整个函数的作用 换言之:只要在函数体中return后要执行的代码都不会被执行。
return 返回多个值
想一想,如果一个函数中需要返回多个数据,我们改怎么实现呢?
示例一:错误示例
下面的代码,为了能够返回多个数据,用了多个return
,想想看行吗?
def create_nums():
print("---1---")
return 1 # 函数中下面的代码不会被执行,因为return除了能够将数据返回之外,还有一个隐藏的功能:结束函数
print("---2---")
return 2
print("---3---")
ret = create_nums()
print(ret)
说明:
- 一个函数中可以有多个
return
,但是只要有一个return
被执行到,那么函数就会结束了,因此后面的return
没有什么用处
示例二:
def create_nums():
print("---1---")
return [1, 2, 3]
ret = create_nums() # 此时ret存储了列表[1, 2, 3]
print(ret)
说明:
- 一个函数中只能有1个return被执行,可用通过return返回列表、元组、集合、字典等从而实现一次性返回多个数据
注意点:
一个函数写多个return
也不是没有任何用处,我们可以添加if
等代码从而实现不同场景执行不同的return
def create_nums(num):
print("---1---")
if num == 100:
print("---2---")
return num + 1 # 函数中下面的代码不会被执行,因为return除了能够将数据返回之外,还有一个隐藏的功能:结束函数
else:
print("---3---")
return num + 2
print("---4---")
result1 = create_nums(100)
print(result1) # 打印101
result2 = create_nums(200)
print(result2) # 打印202
四种函数类型
函数根据有没有参数,有没有返回值,可以相互组合,一共有4种
- 无参数,无返回值
- 无参数,有返回值
- 有参数,无返回值
- 有参数,有返回值
无参数无返回值函数
此类函数,不能接收参数,也没有返回值,一般情况下用来打印提示等类似的功能
def print_menu():
print('--------------------------')
print('点菜系统')
print('1.羊肉涮涮锅')
print('2.牛肉涮涮锅')
print('3.猪肉涮涮锅')
print('--------------------------')
无参数有返回值函数
此类函数,不能接收参数,但是可以返回某个数据,一般情况下像采集数据等功能会用到
# 获取温度
def get_temperature():
# 这里是获取温度的一些处理过程 处理计算温度的代码省略
# 为了简单起见,先模拟返回一个数据
return 24
temperature = get_temperature()
print('当前的温度为:%d' % temperature)
有参数无返回值函数
此类函数,能接收参数,但不可以返回数据,一般情况下对某些变量设置数据而不需结果时用此类函数
def set_age(new_age):
age = new_age
set_age(18)
有参数有返回值函数
此类函数,不仅能接收参数,还可以返回某个数据,一般情况下这类函数用的很多,毕竟我们调用一个函数还想获取它的结果的情况比较多
# 计算1~num的累积和
def add_nums(num):
sum_result = 0
for x in range(1, num + 1):
sum_result += x
return sum_result
result = add_nums(100)
print('1~100的累积和为:%d' % result)
总结
- 函数根据有没有参数,有没有返回值可以相互组合
- 定义函数时,是根据实际的功能需求来设计的,所以不同开发人员编写的函数类型各不相同
函数之间互相隔离
引入
我们知道函数是一个具有独立功能的代码块,在前面的学习中知道一般情况下把一个个功能都单独做成一个个函数,像之前实现加减乘除就定义了4个函数
在开发时,把独立功能做成一个函数其实就是封装
的思想,通过这种方式能够让代码更加整洁
打个比方,当我们离开家乡去远方上学时,往往会大包小包的拎着,每个包中肯定是相类似的物品,这样不仅携带方便在打开包裹取东西时也非常方便,这其实就是”封装“
Python中,根据封装的级别不同,我们会陆陆续续学习到函数、类、对象、模块、包等
"封装"最大的特点就是高内聚低耦合
,大白话讲:相关的功能全部封装到函数中(这是高内聚),尽量减少函数与函数之间的依赖(低耦合),也就是说一个函数的改的对其他的函数来说没有影响
因此以后我们在编写代码的时候,谨记“高内聚低耦合”,尽量做到与函数与函数之间没有关系
大家要注意哦“低耦合"可不是"零耦合",也就是函数之间多多少少还是有千丝万缕的关系,大白话讲:函数之间还是有些关系的
函数之间的关系
根据代码的不同,存在3种关系
- 可能共用同一个变量,会导致一函数添加了数据,另外一个函数删除了数据
- 可能一个函数的返回值,被当做另外一个函数的参数
- 可能一个函数体中调用了另外一个函数
共用一个变量
使用全局变量来实现。后面会详细学习
g_num = 0
def test_1():
global g_num
# 将处理结果存储到全局变量g_num中
g_num = 100
def test_2():
print(g_num)
# 先调用test_1得到数据并且存到全局变量中
test_1()
# 再调用test_2打印test_1()处理过后的数据
test_2()
函数的返回值被当做实参
def test_1():
return 50
def test_2(num):
print(num)
# 先调用test_1获取该函数的返回值
result = test_1()
# 调用test_2时将test_1的返回值传入进去
test_2(result)
函数中调用另外一个函数
使用函数嵌套调用来实现。后面详细学习
def test_1():
# 通过return将一个数据结果返回
return 20
def test_2():
# 在test_2函数体中调用test_1并获取test_1的返回值
result = test_1()
# 在当前函数体中处理数据
print(result)
# 调用test_2完成数据处理
test_2()
总结
函数的功能应该遵循“高内聚,低耦合”
函数之间有关系是肯定的,只要一个函数的改动尽量对另外函数没有影响这就是高品质的函数
至于函数之间的3种关系以及实现方式,需要多练习,当写的代码多了,总结的也多了,相信你的感觉也就到位了
函数嵌套调用
什么是函数嵌套调用?
一种函数中又调用了另外一个函数的方式
def test_1():
print('--- test_1 start ---')
print('这里是test_1函数执行的代码...')
print('--- test_1 end ---')
def test_2():
print('--- test_2 start ---')
test_1()
print('--- test_2 end ---')
test_2()
函数嵌套的作用
能够让函数之间互相使用,增加代码复用性
打个比方,你已经写好了一个功能把它封装为了函数,我再实现另外一个功能的时候发现也需要你写的那个功能函数,此时我不用再写一遍,而是直接将你写的代码拿来用即可,此时我的函数与你的函数都在一个.py
文件中,想要用你的函数,我就必须在我写的函数中调用
练习
案例一:
- 写一个函数:打印一条横线
- 写另外一个函数:打印自定义行数的横线
# 打印一条横线
def print_1_line():
print("-" * 30)
# 打印多条横线
def print_num_line(num):
i = 0
# 因为print_1_line函数已经完成了打印横线的功能,
# 只需要多次调用此函数即可
while i < num:
print_1_line()
i += 1
# 调用函数
print_num_line(3)
案例二:
- 写一个函数:计算三个数的和
- 写一个函数:计算三个数的平均值
# 求3个数的和
def sum_3_number(a, b, c):
return a + b + c # return 的后面可以是数值,也可是一个表达式
# 完成对3个数求平均值
def average_3_umber(a, b, c):
# 因为sum_3_number函数已经完成了3个数的就和,所以只需调用即可
# 即把接收到的3个数,当做实参传递即可
sum_result = sum_3_number(a, b, c)
avg_result = sum_result / 3.0
return avg_result
# 调用函数,完成对3个数求平均值
result = average_3_umber(11, 2, 55)
print("average is %d" % result)
局部变量与全局变量
局部变量
什么是局部变量?
在函数中定义的变量,包括形参变量也是局部变量,只能在定义它的函数中用
def test_1():
a = 300
print(f'test_1在修改前的a的值为:{a}') # f表达式:使用大括号进行占位,要打印的数据直接使用保存数值的变量名称
a = 200
print(f'test_1在修改后的a的值为:{a}')
def test_2():
a = 400
print('函数test_2中的局部变量为: %d' % a)
test_1()
test_2()
局部变量的作用
我们知道函数要尽量遵循“高内聚,低耦合”也就是一个函数的功能尽量独立,为了实现这些功能就免不了进行各种数学运算,想要运算就需要变量,此时在函数中定义变量的需求也就自然而然的来了
所以说,局部变量一般就是为了实现这个函数中的功能而定义的变量,只能在当前函数中使用,其他函数不能使用
局部变量的特点
- 局部变量,就是在函数内部定义的变量
- 形参也属于局部变量
- 其作用范围是这个函数内部,即只能在这个函数中使用,在函数的外部是不能使用的
- 每次调用函数时,局部变量都会重新使用,而不是用上一次调用函数时留下的数据
- 因为其作用范围只是在自己的函数内部,所以不同的函数可以定义相同名字的局部变量(打个比方,把你、我是当做成函数,把局部变量理解为每个人手里的手机,你可有个iPhone12,我当然也可以有个iPhone12, 互不相关)
全局变量
什么是全局变量?
一种在函数外部定义的变量,可以在所有函数中共用
# 定义全局变量
a = 100
def test_1():
a = 300 # 局部变量
print(f'test_1修改前a的值为: {a}')
a = 200
print(f'test_1修改后a的值为: {a}')
def test_2():
print(f'test_2获取全局变量的值为: {a}')
test_1()
test_2()
全局变量的作用
局部变量它的作用范围就只能在定义它的函数中用,而有时为了能够在多个函数之间共用某个数据,此时就有了全局变量
打个比方:有2个兄弟各自有手机,各自有自己的小秘密在手机里,不让另外一方使用(可以理解为局部变量);但是家里的电话是2个兄弟都可以随便使用的(可以理解为全局变量)
全局变量的使用方式
定义全局变量:
# 定义全局变量
a = 100
获取全局变量:
# 定义全局变量
a = 100
def test1():
print(a) # 虽然没有定义变量a但是依然可以获取其数据
# 调用函数
test1()
修改全局变量:
如果一个函数中需要修改全局的值,此时我们需要用global
进行声明
# 定义全局变量
a = 100
def test1():
print(a) # 虽然没有定义变量a但是依然可以获取其数据
def test2():
global a # 如果一个函数中需要修改全局的值,此时我们需要用globa进行声明
a += 1 # 修改全局变量
print(a)
# 调用函数
test1()
总结
- 在函数外边定义的变量叫做
全局变量
- 全局变量能够在所有的函数中进行访问
- 当函数内出现局部变量和全局变量相同名字时,函数内部中的
变量名 = 数据
此时理解为定义了一个局部变量,而不是修改全局变量的值 - 如果在函数中出现
global 全局变量的名字
那么这个函数中即使出现和全局变量名相同的变量名 = 数据
也理解为对全局变量进行修改,而不是定义局部变量 - 如果在一个函数中需要对多个全局变量进行修改,那么可以使用
函数案例:学生管理系统
# -*- coding:utf-8 -*-
# @FileName: test.py
# @Time : 2022/9/26 2:05 下午
# @Author : 顾安
import time
import os
# 定一个列表,用来存储所有的学生信息(每个学生是一个字典)
info_list = []
def print_menu():
print("---------------------------")
print(" 学生管理系统 V1.0")
print(" 1:添加学生")
print(" 2:删除学生")
print(" 3:修改学生")
print(" 4:查询学生")
print(" 5:显示所有学生")
print(" 6:退出系统")
print("---------------------------")
def add_new_info():
"""添加学生信息"""
new_name = input("请输入姓名:")
new_tel = input("请输入手机号:")
new_qq = input("请输入QQ:")
for temp_info in info_list:
if temp_info['name'] == new_name:
print("此用户名已经被占用,请重新输入")
return # 如果一个函数只有return就相当于让函数结束,没有返回值
# 定义一个字典,用来存储用户的学生信息(这是一个字典)
info = dict()
# 向字典中添加数据
info["name"] = new_name
info["tel"] = new_tel
info["qq"] = new_qq
# 向列表中添加这个字典
info_list.append(info)
print(info_list)
def del_info():
"""删除学生信息"""
del_num = int(input("请输入要删除的序号:"))
if 0 <= del_num < len(info_list):
del_flag = input("你确定要删除么?yes or no: ")
if del_flag == "yes":
del info_list[del_num]
else:
print("输入序号有误,请重新输入")
def modify_info():
"""修改学生信息"""
modify_num = int(input("请输入要修改的序号:"))
if 0 <= modify_num < len(info_list):
print("你要修改的信息是:")
print("姓名:%s, 手机:%s, QQ:%s" % (info_list[modify_num]['name'],
info_list[modify_num]['tel'], info_list[modify_num]['qq']))
info_list[modify_num]['name'] = input("请输入新的姓名:")
info_list[modify_num]['tel'] = input("请输入新的手机号:")
info_list[modify_num]['qq'] = input("请输入新QQ:")
else:
print("输入序号有误,请重新输入")
def search_info():
"""查询学生信息"""
search_name = input("请输入要查询的学生姓名:")
for temp_info in info_list:
if temp_info['name'] == search_name:
print("查询到的信息如下:")
print("姓名:%s, 手机:%s, QQ:%s" % (temp_info['name'], temp_info['tel'], temp_info['qq']))
break
else:
print("没有您要找的信息....")
def print_all_info():
"""遍历学生信息"""
print("序号\t\t姓名\t\t手机号\t\tQQ")
i = 0
for temp in info_list:
# temp是一个字典
print("%d\t\t%s\t\t%s\t%s" % (i, temp['name'], temp['tel'], temp['qq']))
i += 1
def main():
"""用来控制整个流程"""
while True:
# 1. 打印功能
print_menu()
# 2. 获取用户的选择
num = input("请输入要进行的操作(数字): ")
# 3. 根据用户选择,做相应的事情
if num == "1":
# 添加学生
add_new_info()
elif num == "2":
# 删除学生
del_info()
elif num == "3":
# 修改学生
modify_info()
elif num == "4":
# 查询学生
search_info()
elif num == "5":
# 遍历所有的信息
print_all_info()
elif num == "6":
# 退出系统
exit_flag = input("亲,你确定要退出么?~~~~(>_<)~~~~(yes or no): ")
if exit_flag == "yes":
break
else:
print("输入有误,请重新输入......")
input("\n\n\n按回车键继续....")
os.system("clear") # 调用Linux命令clear完成清屏
# 程序的开始
main()
2. 函数进阶
函数参数的高级用法
缺省参数
引入
缺省参数也叫做默认参数,是指定义函数时形参变量有默认值,如果调用函数时没有传递参数,那么函数就用默认值,如果传递了参数就用传递的那个数据。
示例:
def print_info(name, age=35):
print(f'name: {name}')
print(f'age: {age}')
print_info('顾安')
print_info('顾安', 18)
缺省参数的作用
当调用函数时,有些参数不必传递,而是用默认值,这样的场景往往都用缺省参数
例如,一个学校现在开始检查每个学生的信息,学生说:报告老师我是xxx学校xxx系xxx年级xxx班学生,名字叫xxxx,大家想只要是这学校的学生那么“xxx学校”就可以省略不用说了,因为大家都知道。所以就可以认为默认的学校就是xxx,而当其他学校的学生介绍时yyy学校名字案例说就一定要说清楚,否则让人弄混了。
示例:
def print_info(name, class_name, grade, department_name, school_name="图灵学院"):
print("老师好:我是来自 %s(大学) %s系 %s年级 %s班级 的学生,我叫%s" % (
school_name,
department_name,
grade,
class_name,
name
))
print_info("顾安", "爬虫", "二", "软件工程")
print_info("顾安", "爬虫", "二", "软件工程", "图灵python")
注意点
- 缺省参数只能在形参的最后(即最后侧)
- 缺省参数全挨在一起(在右侧),不是缺省参数挨在一起(在左侧)
>>> def printinfo(name, age=35, sex):
... print name
...
File "<stdin>", line 1
SyntaxError: non-default argument follows default argument
命名参数
引入
命名参数是指:在调用函数时,传递的实参带有名字,这样的参数叫做命名参数
示例:
def test(a, b):
print('-----')
print(f'a={a}')
print(f'b={b}')
test(11, 22)
test(a=11, b=22)
test(a=22, b=11) # 根据名称将值传入到指定的变量中
命名参数的作用
命名参数能够在调用函数的时候,不受位置的影响,可以给需要的参数指定传递数据
注意点
- 命名参数的名字要与形参中的名字相同,不能出现命名参数名字叫做
num
,而形参中没有变量num
- 如果形参左侧有普通的形参,调用函数时传递的参数一定要先满足这些形参,然后再根据需要编写命名参数
def test(a, b, c=100, d=200):
print("a=%d, b=%d, c=%d, d=%d" % (a, b, c, d))
# 下面的方式都成功
test(11, 22)
test(11, 22, 33)
test(11, 22, 33, 44)
test(11, 22, d=33, c=44)
# 下面的方式都失败
test(c=1, d=2) # 缺少a、b的值
test(c=1, d=2, 11, 22) # 11, 22应该在左侧
不定长参数
引入
不定长参数:定义函数的时候形参可以不确定到底多少个,这样的参数就叫做不定长参数
不定长参数有2种方式表示
*args
:表示调用函数时多余的未命名参数都会以元组的方式存储到args
中**kwargs
:表示调用函数时多余的命名参数都会以键值对的方式存储到kwargs
中
注意:
*
和**
是必须要写的,否则就变成了普通的形参了- 当我们说不定长参数的时候,就是指
*args
和**kwargs
示例:
def test(a, b, *args, **kwargs):
print(a, type(a))
print(b, type(b))
print(args, type(args))
print(kwargs, type(kwargs))
test(11, 22, 33, 44, 55, 66, name='顾安', address='长沙')
不定长参数的作用
通过不定长参数,能够实现调用函数时传递实参个数可以随意变换的需求
注意点
- 加了星号
*
的变量args
会存放所有未命名的变量参数,args
为元组 - 而加
**
的变量kwargs
会存放命名参数,即形如key=value
的参数,kwargs
为字典 - 一般情况下
*args
、**kwargs
会在形参的最右侧 args
与kwargs
的名字可以变,例如叫*aa
,**bb
都是可以,但一般为了能够让其他的开发者快速读懂我们的代码最好还是不改
特殊情况
缺省参数在*args
的后面
def sum_nums_3(a, *args, b=22, c=33, **kwargs):
print(a)
print(b)
print(c)
print(args)
print(kwargs)
sum_nums_3(100, 200, 300, 400, 500, 600, 700, b=1, c=2, mm=800, nn=900)
说明:
*args
后可以有缺省参数,想要给这些缺省参数在调用时传递参数,需要用命名参数传递,否则多余的未命名参数都会给args
- 如果有
**kwargs
的话,**kwargs
必须是最后的
输出结果:
100
1
2
(200, 300, 400, 500, 600, 700)
{'mm': 800, 'nn': 900}
函数返回值拆包
什么是函数返回值拆包
函数返回值拆包:如果一个函数通过return
返回了一个元组、列表、集合,可以通过拆包的方式将返回值进行拆分到每个变量中,这就是返回值拆包。
示例:
def test():
return 11, 22, 33
a, b, c = test()
print(a, b, c)
返回值拆包的作用
通过函数返回值拆包,可以快速的将具体的数据用变量进行存储,这样对数据的处理会更加方便
示例:
def test():
return 11, 22, 33
# 通过返回值拆包,快速使用每个数据
a, b, c = test()
print(a + b + c)
# 没有通过返回值拆包,这样用数据时稍微复杂
ret = test()
print(ret[0] + ret[1] + ret[2])
拆包的使用
def get_my_info():
high = 178
weight = 100
age = 18
return high, weight, age
# result = get_my_info()
# print(result)
# 通过返回值拆包,能够更加方便的对每个数据使用
my_high, my_weight, my_age = get_my_info()
print(my_high)
print(my_weight)
print(my_age)
使用拆包时的注意点
- 拆包时要注意,需要拆的数据的个数要与变量的个数相同,否则程序会异常
通过星号拆包
通过普通方式拆包
假如有以下函数:
def test(a, b, c):
print(a + b + c)
现在自己拥有的数据:
nums = [11, 22, 33]
怎样才能在调用test
函数的时候,将nums
给传递过去呢?
def test(a, b, c):
print(a + b + c)
nums = [11, 22, 33]
test(nums[0], nums[1], nums[2])
上述代码用的方式虽然能行,但不是很简洁
为了能够用更加简洁的方式实现上述场景需求,Python可以通过*
、**
将数据拆包后传递
使用*
拆包
有时在调用函数时,这个函数需要的是多个参数,而自己拥有的是一个列表或者集合这样的数据,此时就用可以用*
拆包
使用方式:
*列表
*元组
*集合
用*
拆包的方式实现上述功能:
def test(a, b, c):
print(a + b + c)
nums = [11, 22, 33]
test(*nums) # 此时的*的作用就是拆包,此时*nums相当于11, 22, 33 即test(11, 22, 33)
如果为数据元组时使用方式与上述代码一致:
def test(a, b, c):
print(a + b + c)
nums = (11, 22, 33)
test(*nums)
集合类型同上:
def test(a, b, c):
print(a + b + c)
nums = {11, 22, 33}
test(*nums)
注意:
*
对列表、元组、集合可以拆包,但一般都是在调用函数时用
使用**
拆包
使用**
可以对字典进行拆包,拆包的结果是命名参数
示例:
def test(name, age, address):
print(name)
print(age)
print(address)
info = {
"name": "顾安",
"age": 18,
"address": "长沙"
}
test(**info)
'''
当前**info相当于以下代码:
name='顾安'
age=18
address='长沙'
** 主要对字典进行拆包
'''
难点
学习不定长参数时,掌握了*args
、**kwargs
现在学习拆包时,也用到了*
、**
那它们之间有什么关系呢?
答:没有任何关系,只是长得像罢了
示例一:
def test1(*args, **kwargs):
print("----在test1函数中----")
print("args:", args)
print("kwargs", kwargs)
def test2(*args, **kwargs):
print("----在test2函数中----")
print("args:", args)
print("kwargs", kwargs)
test1(args, kwargs) # 在函数test1传递参数时没有进行拆包
test2(11, 22, 33, name="顾安", age=18)
运行结果:
----在test2函数中----
args: (11, 22, 33)
kwargs {'name': '顾安', 'age': 18}
----在test1函数中----
args: ((11, 22, 33), {'name': '顾安', 'age': 18})
kwargs {}
示例二:
def test1(*args, **kwargs):
print("----在test1函数中----")
print("args:", args)
print("kwargs", kwargs)
def test2(*args, **kwargs):
print("----在test2函数中----")
print("args:", args)
print("kwargs", kwargs)
test1(*args, **kwargs) # 对参数进行了拆包
test2(11, 22, 33, name="顾安", age=18)
运行结果:
----在test2函数中----
args: (11, 22, 33)
kwargs {'name': '顾安', 'age': 18}
----在test1函数中----
args: (11, 22, 33)
kwargs {'name': '顾安', 'age': 18}
Python
语言中的引用
引入
如下代码中,最后b的值为多少?
>>> a = 1
>>> b = a
>>> b
1
>>> a = 2
>>> a
2
如下代码中,最后b的值为多少?
>>> a = [1, 2]
>>> b = a
>>> b
[1, 2]
>>> a.append(3)
>>> a
[1, 2, 3]
什么是引用
引用:就是地址
那地址是什么呢?可以理解为存放数据的空间在内存中的编号
例如:
a = 100
怎样知道它的地址呢?
id(a)
可以直接将上述的结果打印:
print(id(a))
运行结果(在不同机器上输出的地址可能不相同):
4347271232
当我们知道了原来引用就是地址之后,再来看如下代码:
a = [1, 2]
我们可以用id(a)
取它的地址:
a = [1, 2]
print(id(a)) # 获取变量存储的引用(地址)是多少
接下来定义变量b并且赋值:
a = [1, 2]
print(id(a)) # 获取变量存储的引用(地址)是多少
b = a
此时输出变量b的引用:
a = [1, 2]
print(id(a))
b = a
print(id(b))
运行结果(不同机器上的内存地址可能不相同):
4558971360
4558971360
这说明,此时变量a、b存储的引用都是相同的
由此我们可以得出一个结论:Python中的变量并不是真正存储数据,而是存储的数据所在内存中的地址,我们一般称之为引用
既然变量a、b都指向同一个列表,那么接下来
a.append(3)
此时变量a、b指向的同一个列表中多了一个数据,即此时列表为[1, 2, 3]
所以a、b此时用print
输出相同的结果
补充内容
大家自己试试看a=257, b=257时它们的id还是否会相等。事实上Python 为了优化速度,使用了小整数对象池,避免为整数频繁申请和销毁内存空间。而Python 对小整数的定义是 [-5, 257),只有数字在-5到256之间它们的id才会相等,超过了这个范围就不行了,同样的道理,字符串对象也有一个类似的缓冲池,超过区间范围内自然不会相等了。
总的来说,只有数值型和字符串型,并且在通用对象池中的情况下,a is b才为True,否则当a和b是int,str,tuple,list,dict或set型时,a is b均为False。
赋值运算符=
赋值运算符=
,之前为了更好的理解变量,把a=100
理解为变量a中存放了100
,事实上变量a
存储是100
的引用
也就是说:在Python中只要用=
那么就表示=
左边的变量存储了一个新的引用
大白话讲:就是=
左边的变量指向了右边的数据
想想下面的代码运行的结果是什么?
a = [1, 2]
b = a
b.append(3)
b = [100, 200, 300]
print(b)
运行结果:
[100, 200, 300]
而不是:
[1, 2, 3]
引用当做实参
Python中调用函数时,传递实参实际上都是是引用,即传递的都是地址
只要是传递的引用,那么也就是说在函数中是可以直接对指向的数据进行修改
def test(p):
# 此时变量p也指向nums指向的列表
p.append(44)
print("在函数test中,p=", p)
nums = [11, 22, 33]
print("调用test函数之前,nums=", nums)
test(nums) # 此时将列表的引用当做了实参进行传递
print("调用test函数之后,nums=", nums)
运行结果:
调用test函数之前,nums= [11, 22, 33]
在函数test中,p= [11, 22, 33, 44]
调用test函数之后,nums= [11, 22, 33, 44]
函数名也是引用
引入
阅读如下代码,思考会输出什么结果
def test1():
print("我是test1函数哦。。。。")
def test2():
print("我是test2函数哦。。。。")
test1()
test1 = test2
test1()
运行结果如下:
我是test1函数哦。。。。
我是test2函数哦。。。。
你可能会惊讶,为什么第9行调用test1
函数输出的是我是test1函数哦。。。。
,反而到了第12行再次调用test1
函数时变成了我是test2函数哦。。。。
上述问题的原因核心点是:在Python中即使是函数名也是一个变量名,只不过这个变量没有指向普通的数据,而是指向了一段代码;也就是说如果定义了一个函数名字叫做test1
就好比是一个变量名test1
指向了那个代码块而已,所以当上述代码第11行test1 = test2
时,就相当于让test1
变量不在指向原本的代码块,而是指向新的代码块即test2
指向的代码块,所以当第12行执行test1
函数时,会输出我是test2函数哦。。。。
引用的作用
看完上述的引入知识后,相信你会对什么是函数的引入有一个大体的认知了
在此简单总结:所谓函数名当做引用,其实是指在Python中所有的函数名实际上是一个变量名,只不过这个变量名指向的不是常见的数据,而是一段代码,当我们用函数名()
是实际上就是让指向的这块代码开始执行,当我们只用函数名
时其实就是这个函数的引用
记住:既然函数名也是变量名,那么就可以给它赋值获取它的引用给别的变量
总结:
- 使用
def
定义的函数名,实际就是个变量名它存储了函数的引用 - 如果将另外一个变量,例如
b
保存了函数的引用,即也指向了同一个函数,那么b()
就是调用函数
匿名函数
什么是匿名函数
没有名字的函数,在Python中用lambda
定义
示例:
lambda x, y: x + y # 定义了一个匿名函数 1.没有名字 2.完成2个数的加法操作
匿名函数的作用
- 可以用一行代码完成简单的函数定义
- 可以当做实参快速传递到函数中去
使用方式
用lambda
关键词能创建匿名函数。这种函数得名于省略了用def
声明函数的标准步骤
lambda
函数的语法只包含一个语句,如下:
lambda 形参1, 形参2, 形参3: 表达式
注意点:lambda
函数能接收任何数量的参数但只能返回一个表达式的值,其默认就是返回的,不用写return
既然我们已经知道def
定义函数时的变量存储的是函数的引用,所以只要有了这个函数的引用,也就可以通过变量名()
的方式调用函数
而函数分为def
定义的普通函数,和用lambda
定义的匿名函数,所以无论一个变量例如b
保存的是普通函数的引用,还是匿名函数的引用,都可以用b()
方式调用b指向的函数
一般情况下对匿名函数的使用有2种方式
- 通过
lambda
定义匿名函数,然后用一个变量指向这个匿名函数,然后通过变量名()
调用这个匿名函数 - 直接在调用其它函数实参的位置通过
lambda
定义匿名函数,会将这个匿名函数的引用当做实参进行传递
方式一:
# 定义了一个匿名函数,然后让变量add_2_nums指向它
add_2_nums = lambda x, y: x + y
# 调用add_2_nums指向的匿名函数
print("10+20=" % add_2_nums(10, 20))
输出结果:
10+20=30
方式二:
def fun(a, b, opt):
print("a = %d" % a)
print("b = %d" % b)
print("result = %d" % opt(a, b)) # 此时opt指向了第7行定义的匿名函数,所以opt(a, b)就相当于调用匿名函数
fun(1, 2, lambda x, y: x + y) # 定义一个匿名函数,且将它的引用当做实参进行传递
代码案例
想一想,下面的数据如何指定按age
或name
排序?
stus = [
{"name": "顾安", "age": 18},
{"name": "夏洛", "age": 19},
{"name": "木木", "age": 17}
]
按照name
排序:
stus = [
{"name": "顾安", "age": 18},
{"name": "夏洛", "age": 19},
{"name": "木木", "age": 17}
]
print("排序前,stus=", stus)
stus.sort(key=lambda x: x['name'])
print("排序后,stus=", stus)
按照age
排序:
stus = [
{"name": "顾安", "age": 18},
{"name": "夏洛", "age": 19},
{"name": "木木", "age": 17}
]
print("排序前,stus=", stus)
stus.sort(key=lambda x: x['age'])
print("排序后,stus=", stus)
结语
以上就是关于python专题中的函数全部内容了,欢迎同学们在评论区讨论交流,有任何python开发、数据采集相关需求也可以后台或V加regentwan与我联系哟~