目录
🙄什么是Python函数(了解函数的概念)
🤔为什么需要它?(背景和痛点)
😮函数的分类(函数有哪些?)
内置函数
标准库函数
第三方库函数
定义函数
🧐核心原理拆解(搞清楚他们是如何工作的?)
函数的定义
函数的调用
函数的参数
形参和实参的概念
位置参数
默认参数
关键字参数
不定长参数
函数的返回值
🤑案例实操
案例一:基础函数(无参+有参)
案例二:默认参数+可变参数
案例三:return 返回值
🤓关联知识(通过函数,了解变量的作用域)
什么是作用域?
全局变量和局部变量
案例——全局和局部变量
🤫 避坑总结
🥳三条学习心法
心法 1:参数 + return > global
心法 2:函数要"专一"
心法 3:默认参数用不可变对象
🙄什么是Python函数(了解函数的概念)
- Python函数:是一个被命名的、独立的、完成特定功能的代码段,有时候给调用它的程序一个返回值。
- 💡核心导读:Python函数就是一个个功能明确的小工具,当用到某个工具时直接调用。它不仅能帮我们把复杂的任务分解成简单的步骤,还能提高代码的复用性和可读性。
🤔为什么需要它?(背景和痛点)
- 没有函数之前,我们可能会在代码中多次重复地编写相同的功能代码,这不仅浪费时间,还会使代码变得冗长且难以维护。
- 函数带来的核心优势:
- 提高代码复用性,减少重复代码;
- 使代码逻辑更加清晰,便于理解和调试;
- 模块化编程,方便团队协作开发,每个人负责不同的函数模块。
😮函数的分类(函数有哪些?)
内置函数
在编写代码过程中,可以拿来直接使用的时内置函数(如:print() / input() / min() / max() 等)
| 函数 | 作用 |
|---|---|
print() | 输出内容 |
len() | 获取长度 |
type() | 查看类型 |
input() | 接收输入 |
sum() | 求和 |
标准库函数
通过import语句导入库,然后使用该库中定义的函数。
| 模块 | 函数 | 作用 |
|---|---|---|
math | sqrt() | 开平方 |
random | randint() | 随机整数 |
time | sleep() | 延时 |
第三方库函数
Python社区提供了很多高质量的库,通过下载安装这些库后,也可以使用import语句导入,然后使用这些第三方库的函数。
| 库 | 函数 |
|---|---|
numpy | array() |
pandas | read_excel() |
requests | get() |
定义函数
用户根据自己的需求自定义的函数,这也是我们需要学习的,根据需求构建合适的函数来实现某个功能。
🧐核心原理拆解(搞清楚他们是如何工作的?)
函数的定义
- 以def 关键字开始,告诉Python我要定义一个函数。
- 函数名是函数的标识,所以要遵循命名规则,通常全小写且单词之间使用下划线连接。
- 参数是函数运行时需要的输入数据,可以是0个或者多个。
- 函数体是函数的核心部分,包含了具体的处理逻辑,需要注意的是这部分代码需要缩进,表明下面这部分代码属于该函数。
- return 语句用于返回函数的处理结果,如果没有 return 语句,函数默认返回 None。
def function_name([参数1,参数2,……]): ''' 文档字符串(可选,用于解释函数功能) ''' 函数体(注意必须缩进) …… return 返回值 # 可选,用于结束函数并返回结果,不写这条语句默认返回None函数的调用
当我们需要使用函数的功能时,通过函数名加上圆括号来调用它,如果函数有参数,需要在圆括号内传入相应的实参。
函数名([参数1,参数2,……])函数的参数
主要参数类型:默认参数、关键字参数、位置参数、不定长参数
在了解上面那三种参数类型之前,首先需要认识什么是:形参和实参?
形参和实参的概念
形参(形式参数):是函数定义时小括号中的参数,用来接收参数的,并在函数内部作为变量使用。
实参(实际参数):是函数调用时小括号中的参数,用来把数据传递到函数内部。
# 形参 def two_numbers_sum(a, b): # 函数的定义,其中的a,b是形参 return a + b # 实参 result = two_numbers_sum(3, 4) # 函数的调用,其中的3, 4是实参位置参数
按位置顺序传递,最为常见
示例:
def two_numbers_sum(a, b): # 当调用函数时,按照形参定义的位置顺序,一次传递参数 return a + b result = two_numbers_sum(3, 4) # 位置参数:实参“3”对应形参“a”,实参“4”对应形参“b”默认参数
给参数设置默认值,调用时可以不传参数,如果传参,则会变成和传入参数一样的值。一般默认参数放到形参的最后面。
示例:
def user_info(user_name, age, gender='男'): # 格式化打印用户信息 print("用户名:{0}, 年龄:{1}, 性别:{2}".format(user_name, age, gender)) user_info("小王", 18) user_info("小刘", 20, "女") # 运行结果 # 用户名:小王, 年龄:18, 性别:男 # 用户名:小刘, 年龄:20, 性别:女通过上面的示例可知:函数"user_info()"中有默认参数" gender ",其默认值是“男”。当没有传递该参数时,函数中形参“gender”的值就是“男”;如果传递了参数“女”,则函数中的“gender”的值就是“女”。
关键字参数
调用时指定参数名,此时就不需要按照位置参数的顺序进行传参了。
def user_info(user_name, age, gender='男'): # 格式化打印用户信息 print("用户名:{0}, 年龄:{1}, 性别:{2}".format(user_name, age, gender)) # 调用函数时,使用关键字参数指定传递的参数值,此时的参数就可以不按照定义函数时的顺序进行传递了 user_info(age=22, user_name="小张") # 运行结果 # 用户名:小张, 年龄:22, 性别:男不定长参数
不定长参数:接收任意多个参数,包括” *args “ 和" **kwargs "两种。
| 参数类型 | 说明 | 示例 |
|---|---|---|
| *args | 接收任意多个位置参数,变成元组 | def total(*nums) |
| **kwargs | 接收任意多个关键字参数,变成字典 | def info(**data) |
不定长参数也是放到形参的最后面,如果同时又默认参数和不定长参数,最好的方式就是把不定长参数放到默认参数之后。
✅推荐正确顺序:
普通位置参数 -> 默认参数 -> 不定长参数(*args) -> 不定长参数(**kwargs)
# 按照标准顺序定义函数 def my_func(a, b=10, *args, **kwargs): print(f"普通参数 a: {a}") print(f"默认参数 b: {b}") print(f"不定长参数 args (元组): {args}") print(f"不定长关键字参数 kwargs (字典): {kwargs}") # 调用函数 my_func(1, 2, 3, 4, 5, name="小明", age=18) # 输出结果 # 普通参数 a: 1 # 默认参数 b: 2 # 不定长参数 args (元组): (3, 4, 5) # 不定长关键字参数 kwargs (字典): {'name': '小明', 'age': 18}❓可能有人问:
为什么调用函数的时候使用了“关键字函数(name="小明", age=18)”为什么会变成字典呢?
因为这不是“关键字函数”,关键字函数确实是在函数调用的括号里定义的,定义方式:形参名=数据。可以看到上面在函数定义的形参列表中没有"name,age"这两个形参,但是有不定长参数“ **kwargs ”,所以这两个参数会被打包进“ **kwargs ”变成字典的形式。【至于他为什么会变成字典涉及到“拆包”的思想,这个在本章节不详细讲解,还请谅解】
函数的返回值
return后面可以跟一个值、多个值、或不写
跟多个值时,Python会自动打包成元组
函数遇到 return 会立即结束,后面的代码不在执行
🤑案例实操
案例一:基础函数(无参+有参)
# 无参函数:打招呼 def say_hello(): print("hello world") say_hello() # 调用无参函数 # 输出结果:hello world # 带参函数:求两数之和 def two_numbers(a, b): result = a + b return result print(two_numbers(3, 5)) # 调用带参函数并打印结果 # 输出结果:8案例二:默认参数+可变参数
# 默认参数 def user_info(name, age, gender="男"): print(f"姓名:{name},年龄:{age},性别:{gender}") user_info("小明", 18) # 输出:姓名:小明,年龄:18,性别:男 # 可变参数 *args:计算任意多个数的总和 # 计算小明的所有考试成绩 def total_sum(name, age, gender="男", *numbers): result = 0 for num in numbers: result += num print(f"姓名:{name},年龄:{age},性别:{gender},总分:{result}") total_sum("小明", 18, 80, 90, 88) # 输出:姓名:小明,年龄:18,性别:80,总分:178上述不定长参数“ *args ”接收的参数是,先按照位置参数的顺序将前面的实参接收,最后剩下的内容就是“ *args ”打包的内容。所以上述出现了“性别:80”的情况,所以当参数较多,使用“ *args ”接收参数时,实参尽量和形参的数量和顺序对应,更好的方式是使用关键字参数。
# 使用不定长参数**kwargs定义一个函数,接受任意数量的关键字参数,并打印出这些参数的键和值。 def user_info(name,age,gender='男',**info): print('姓名:%s'%name) print('年龄:%d'%age) print('性别:%s'%gender) print('其他信息:') for key,value in info.items(): print('%s:%s'%(key,value)) user_info('李四',age=25,job='程序员',city='上海',hobby='篮球') print('-'*20) user_info('张三',gender='女',job='学生',city='北京',age=20,hobby='唱歌') # 输出结果: # 姓名:李四 # 年龄:25 # 性别:男 # 其他信息: # job:程序员 # city:上海 # hobby:篮球 # -------------------- # 姓名:张三 # 年龄:20 # 性别:女 # 其他信息: # job:学生 # city:北京 # hobby:唱歌由此可见,不定长参数“ **kwargs ”会接收任意数量的关键字参数。而当同时使用关键字参数和不定长参数时,不按照形参的位置顺序传参也没关系,函数接收参数是按照形参的顺序依次接受的,当到了“ gender ”参数的时候,会现在函数调用的实参中寻找“ gender ”参数,找到就接收,而其他“ key = value ”形式的参数就会被“ **kwargs ”接收。
案例三:return 返回值
无 return 语句
# 不加return 语句 def user_info(name, age): print(f"Name: {name}, Age: {age}") # 使用一个变量接收函数的返回值 ret = user_info("Tom", 18) print(ret) # 输出结果: # Name: Tom, Age: 18 # 此结果是函数中的print()打印的结果 # None # 此结果是打印函数的返回值可见,不写 return 语句默认返回值为“ None ”。
使用 return 返回单个值
# 使用 return 语句返回单个值 def add(a, b): return a + b # 使用一个变量接收函数的返回值 ret = add(1, 2) print(ret) # 3使用 return 语句返回多个值
# 使用 return 语句返回多个值 def calculate(a, b): sum_result = a + b difference = a - b return sum_result, difference # 调用函数并接收返回的多个值 result_sum, result_diff = calculate(10, 5) print("两数求和:", result_sum) # 输出: 两数求和: 15 print("两数作差:", result_diff) # 输出: 两数作差: 5 # 打印函数的返回值 print(calculate(10, 5)) # 输出: (15, 5)可见,return 返回多个结果时,是以元组的形式返回的。而接收函数的返回值使用的是“拆包”的思想。
🤓关联知识(通过函数,了解变量的作用域)
变量根据作用域的不同分为:
- 全局变量
- 局部变量
什么是作用域?
作用域:实际上就是变量的“活动范围”。
Python 中变量的作用域遵循LEGB 规则(由内到外查找):
L → E → G → B 局部 → 嵌套 → 全局 → 内置L(Local):函数内部定义的变量
E(Enclosing):嵌套函数的外层函数变量
G(Global):模块级别定义的变量
B(Built-in):Python 内置的变量(如
print、len)
简单说:Python 找变量时,先在本函数里找,找不到再去外层找,一层一层往外翻。
全局变量和局部变量
从名字上看,一个在全局(大范围)使用,一个是在局部(小范围)使用。
全局变量:在函数和类定义之外声明的变量。作用域为定义的模块,从定义位置开始到模块结束。
局部变量:在函数内部声明的变量(包括形参),只能在该函数中使用,外部无法使用。
案例——全局和局部变量
函数内部访问全局变量
# 定义一个全局变量 name = 'Tom' # 定义一个方法,打印全局变量 def show(): # 函数内部可以直接访问全局变量 print(name) show() # 输出: Tom函数外部访问局部变量
# 定义一个函数 def show(): # 定义一个局部变量 name = 'Tom' print(name) print(name) # 错误:NameError: name 'name' is not defined既然函数内部可以访问全局变量,那么我们现在在函数内部修改全局变量【需要使用 global 关键字】
# 定义一个全局变量 name = 'Tom' # 定义一个方法,打印全局变量 def show(): # 函数内部修改全局变量 name = 'Jerry' print(name) show() # 输出:Jerry print(name) # 输出: Tom # 我们发现,在函数外面定义的全局变量并没有因为在函数内部发生改变而改变,仍然是原来的“Tom” print('-'*30) # 正确的方式是: name1 = 'Tom1' def show1(): # 函数内部修改全局变量 global name1 name1 = 'Jerry1' print(name1) show1() # 输出:Jerry1 print(name1) # 输出: Jerry1🤫 避坑总结
1.缩进问题:Python中是依靠缩进区分代码块的,函数体内部的代码必须缩进,且缩进量保持一致,否则导致语法错误。
2.调用顺序和局限:不是只要定义了函数就可以随时随地使用。必须先定义函数,然后才能调用它,并且如果想使用别的文件中的方法,直接在当前文件中调用是不可行的。它是先将含有该方法的文件进行封装(封装成一个类或模块),通过导入模块和实例化类,来调用该方法。
3.return 语句:函数没有 return 语句是不是没有返回值?错,没有 return 语句,也有返回值,默认返回 None。其实所有的函数都有返回值,只是有的函数不需要返回指定的数据,所以可以不需要写 return 语句,让他默认返回None即可。
4.print 与 return 区别:print 只是将结果输出到控制台,方便人查看;而 return 是将结果返回给调用者,供程序后续使用,一个函数可以没有 print ,但是如果需要返回值处理结果,就必须有 return 。
| 坑 | 说明 |
|---|---|
| 函数定义了不调用 | 写 def 只是“造工具”,必须用 函数名() 才会执行 |
| return 后还写代码 | return 是重点,后面的代码永远不会执行 |
| 默认参数使用 [] 或 {} | 会导致多次调用共享同一个对象,应该使用 None 代替 |
| 函数内直接改全局变量 | 需要先使用 global 声明,否则Python认为是局部变量 |
| 参数顺序写错 | 正确顺序:位置参数 -> 默认参数 -> *args -> **kwargs |
🥳三条学习心法
心法 1:参数 + return > global
能用参数传进去、用 return 传出来,就绝不用 global。
全局变量是"隐形炸弹"——程序小没问题,项目大了谁改了值根本找不到。参数和 return 让数据流动看得见、管得住。
心法 2:函数要"专一"
一个函数只做一件事,名字就要让人一眼看懂。
❌
def process_data_and_save_and_log()
✅def save_data()+def log_event()函数越"专一",复用性越强,调试越轻松。
心法 3:默认参数用不可变对象
def f(lst=None)永远比def f(lst=[])安全。Python 的默认参数在函数定义时只创建一次,用可变对象(列表、字典)会导致多次调用共享同一个对象,产生难以察觉的 Bug。