第6章 Python函数
6.1 函数的概念
函数(function)是 Python 中用于封装可重复执行代码块的基本单元,它能够接收输入参数、执行特定逻辑,并可选择性地返回结果。
简单的说:函数代表一个独立的可复用的功能。
通过使用函数,程序编写、阅读、测试和修复起来都更加容易。Python中的函数必须先定义后使用,Python提供了许多内建函数,比如print()。也可以自己创建函数,这被叫做用户自定义函数。
6.2 函数的定义
6.2.1 函数的定义格式
Python 定义函数使用 def 关键字,一般格式如下:
- 格式:
- 函数代码块以def关键词开头,后接函数标识符名称和圆括号 (),()后面以冒号结束。
- 函数体开始缩进。
- 函数名:
- 函数名是程序员给这个函数起的名称,需要遵循标识符的命名规则。函数名一般是一个动词,第一个单词小写,其他每个单词的首字母大写,即小驼峰命名法。
- 参数列表:
- 函数在完成某个功能时,可能需要一些数据,在定义函数时指定函数参数来接收这些数据。例如在屏幕上打印信息,需要把要打印的信息传递给print()函数。如果有多个参数,参数之间使用逗号分隔。函数也可以没有参数,但是小括弧不能省略。
- 函数体:
- 函数体一般包含3个部分:文档说明,功能代码,return [表达式]语句。
- 函数的第一行语句可以选择性地使用文档字符串—用于存放函数说明。用三个引号括起来,单引号和双引号都可以。
- return [表达式] 结束函数,返回一个值给调用方。不带表达式的return或没有return语句相当于返回 None。
6.2.2 函数的定义和调用
- 函数必须先定义再调用;
- 函数定义后不调用是不会执行的;
- 调用一次执行一次;
6.2.3 函数的好处
- 使程序变得更简短而清晰
- 可以提高程序开发的效率
- 提高了代码的重用性
- 便于程序分工协作开发
- 便于代码的集中管理
- 有利于程序维护
6.3 返回值
6.3.1 函数体中没有return语句
6.3.2 函数体有return语句
6.3.3 函数体有return 值语句
6.4 参数
6.4.1 形参和实参
-
在定义函数时,指定的参数称为形式参数,简称为形参(函数的提供者);在调用函数时,给函数传递的参数称为实际参数,简称为实参(函数的调用者)
-
在定义函数时,形参没有分配存储空间,也没有值,相当于一个占位符;在调用函数时, 会在栈区中给函数分配存储空间, 然后给形参/局部变量分配存储空间,传递的是实际的数据。当函数执行结束,函数所占的栈空间会被释放,函数的形参/局部变量也会被释放
6.4.2 参数传递
1、python 中变量没有类型
在 python 中,类型属于对象,变量是没有类型的
以上代码中,10是数字类型,"helloworld" 是 str 类型,而变量 a 是没有类型,它仅仅是一个对象的引用(一个指针),可以是指向数字类型对象,也可以是指向str 类型对象。
2、引用的概念
在 Python 中,变量和数据是分开存储的,数据保存在内存中的一个位置,变量中保存着数据在内存中的地址,变量中记录数据的地址,就叫做引用。使用id()函数可以查看变量中保存数据所在的内存地址。
注意:当给一个变量重新赋值的时候,本质上是修改了数据的引用,变量不再引用对之前的数据,变量改为对新赋值的数据引用,变量的名字类似于便签纸贴在数据上。

3、可变与不可变类型
在Python常见的类型中,数字类型、string、tuple是不可更改的对象,而list、set、dict等则是可以修改的对象。
(1)不可变(immutable)类型:所有的修改操作都会产生新对象,即修改引用。

(2)可变(mutable)类型:
-
有些操作基于原对象进行修改的,不产生新对象,例如:append、remove、+=、*=等
-
有些操作是会产生新对象的,例如:=、union、difference等

4、Python的参数传递机制
-
不可变类型:如整数、字符串、元组等类型。效果类似c++的值传递。如fun(a),在fun函数中对形参做任何修改,不会影响实参a 本身。因为对形参做任何修改本质上都是形参指向新对象,所以与原来的实参对象无关。
-
可变类型:如列表,字典等类型。效果类似c++的引用传递。如 fun(a),在fun函数中对形参进行修改,分为2种情况:
-
基于形参原对象进行修改,此时实参a也会受影响,因为形参和实参指向同一个对象。
-
修改产生新对象,即给形参重新赋值新对象,此时不会影响实参a。因为新对象与实参对象无关。
-
Python 中一切都是对象,严格意义我们不能说值传递还是引用传递,我们应该说传不可变对象和传可变对象。
不可变类型示例

可变类型示例

两种运算符区别
对于可变类型来说,还有一个小细节,la *= 2 和 la = la * 2 有差别:
- la *= 2 使用原地址
- la = la * 2 返回新地址
类似的还有:+=。
5、使用deepcopy防止可变类型参数被修改
- 根据Python的参数传递机制的原理,如果参数是可变类型(例如:列表、集合、字典等),那么在函数中对可变参数的内容做修改,会影响实参。如果此时希望对形参的修改不影响实参,那么可以通过
copy.deepcopy()来实现。本质上相当于是复制了一个实参对象给形参。

6.4.3 函数可使用的参数形式
1、位置参数
- 实参的顺序、个数必须与形参完全一致
2、默认值参数
- 定义默认值参数时要求:含默认值的形参必须在所有不含默认值的形参后面
- 含默认值的形参,可以不用指定实参
3、关键字参数
- 调用方法时,实参可以使用形参名作为关键字来指定给哪个形参赋值,此时实参的顺序就不用与形参完全一致了
4、强制使用位置参数或关键字参数
- 强制使用位置参数或关键字参数:/ 前的参数必须使用位置传参,* 后的参数必须用关键字传参。
5、解包传参
6、可变长度参数
*args: 接收任意数量的位置参数,存储为tuple类型**kwargs: 接收任意数量的关键字参数,存储为dict类型- 如果一个函数包含可变长度参数和普通参数,那么建议普通参数在前,可变长度参数在后
- 一个函数中最多只能有一个
*args和**kwargs的可变长度参数 **kwargs可变长度参数后面不允许再有任何参数。*args可变长度参数后面可以有参数。*args后面的参数必须通过关键字传参。如果包含*args的函数想要定义默认值参数,必须定义在*args后面
- 一个函数中最多只能有一个
6.5 函数的嵌套调用
- 在一个函数中调用另一个函数,当内层调用函数执行完之后才会继续执行外层函数的其他语句。
执行结果:
6.6 递归
6.5.1 递归的概念
递归是一种重要的编程思想,其核心理念是函数调用自身来解决问题。
1、基本概念
- 自我调用:函数在执行过程中调用自己
- 问题分解:将复杂问题分解为相同类型但规模更小的子问题
- 逐步简化:通过不断缩小问题规模,最终达到可直接解决的程度
2、递归的两个关键要素
(1)递归情况(Recursive Case)
- 函数调用自身的部分
- 将问题转化为规模更小的同类问题
- 逐步向基础情况靠近
(2)基础情况(Base Case)
- 递归的终止条件
- 防止无限递归的发生
- 最简单可以直接求解的情况
6.7.2 递归的案例演示
案例:求一个整数n的阶乘!
- 不使用递归
- 使用递归
6.7.3 递归的执行过程

6.7 函数是一种数据类型
在Python中,函数是一等公民,函数也有一种数据类型。
所以,函数也是一个对象,它可以:
- 函数可以被赋值给变量
- 函数可以作为参数传递给其他函数
- 函数可以作为函数的返回值
- 函数可以存储在数据结构中
这种特性使得Python支持高阶函数、闭包、装饰器等高级编程技术。
6.7.1 给变量赋值一个函数
注意:
- say_hello = greet # 赋值给say_hello变量的是一个函数
- say_hello = greet() # 赋值给say_hello变量的是函数的返回结果,这里是None,因为greet()函数没有返回值
6.7.2 函数作为参数传递
6.7.3 函数作为返回值
6.7.4 函数被存储到数据结构中
6.8 匿名函数
6.8.1 Lambda表达式
Python使用 lambda 来定义匿名函数,所谓匿名,指其不用 def 的标准形式定义函数。
- lambda 只是一个表达式,函数体比def简单很多。
- lambda的主体是一个表达式,而不是一个代码块,所以仅仅能在lambda表达式中封装有限的逻辑进去。
- lambda函数拥有自己的命名空间,且不能访问自己参数列表之外或全局命名空间里的参数。
6.8.2 Lambda表达式作为函数的参数
1、作为自定义函数的参数
2、作为内置函数的参数
3、作为标准库中函数的参数
6.8.3 Lambda表达式作为函数返回值
6.9 函数的说明文档(了解)
函数说明文档的好处
- 提高代码可读性 清晰描述函数的功能和用途 帮助其他开发者快速理解函数的作用
- 便于维护和调试 提供函数的详细说明,方便后续修改 减少因误解函数功能而产生的错误
- 自动生成文档 Python可以通过 help() 函数查看文档字符串 支持自动化工具生成API文档
- 提升开发效率 团队协作时,文档能减少沟通成本 IDE可以显示文档提示,提高编码效率
- 规范开发标准 统一团队的代码注释风格 有助于培养良好的编程习惯
PyCharm中将鼠标悬停在函数名上方也可以看到函数说明文档。

6.10 函数注释(了解)
Python 3.x 引入了函数注释,以增强函数的注释功能。
可以使用:对参数逐个进行注释,注释内容可以是任何形式,比如参数的类型、作用、取值范围等等,返回值使用->标注,所有的注释都会保存到函数的__annotations__属性中。另外,使用函数注释并不影响默认参数的使用。