第 5 章 Python 函数详解

在 Python 项目开发中,函数是实现代码复用、提升开发效率的核心工具。本章将带你从基础的函数创建,到灵活多样的参数使用,一步步掌握 Python 函数的核心用法。


5.1 函数的创建

5.1.1 函数的介绍

Python 提供了丰富的内置函数(如print()len()),但在实际开发中,这些内置函数往往无法满足项目的特定需求。因此,Python 允许我们自定义函数,通过封装一段代码来实现特定功能,实现一次定义、多次调用,大幅减少重复代码。

注意区分两个概念:

  • 定义函数:创建一个新的函数(如def fun1():
  • 调用函数:使用已经定义好的函数(如fun1()

5.1.2 函数的定义(def 语句)

创建新函数的过程称为定义函数,使用def关键字,格式如下:

python

def 函数名(参数):
    代码块
    return 返回值

使用def语句定义函数时,需要注意以下 5 点:

  1. def是 Python 的保留字,用于声明函数,语句末尾必须有冒号:
  2. 函数名的命名规则与变量相同,由字母、数字、下划线组成,不能以数字开头。
  3. ()中的参数表示函数接收的数据,可根据功能需求决定是否设置参数。
  4. 函数内的代码块必须缩进一个制表符(4 个空格),表示属于函数的执行逻辑。
  5. return语句用于将函数的执行结果返回给调用处,若不需要返回数据,可省略。
示例:创建一个简单的无参函数

python

# 定义函数
def fun1():
    # 函数代码块:输出0~9
    for i in range(10):
        print(i, end='')

这个函数没有参数,功能是输出数字 0~9。定义时,Python 不会执行函数内的代码,只有调用时才会执行。

5.1.3 函数的调用

定义好函数后,需要通过调用函数来执行它的功能。调用格式如下:

python

函数名(参数)
示例:调用无参函数

python

# 定义函数
def fun1():
    for i in range(10):
        print(i, end='')

# 调用函数
fun1()

plaintext

0123456789

⚠️ 重要注意:函数必须先定义,后调用!如果把调用写在定义前面,会出现NameError错误:

python

# 错误写法:先调用后定义
fun1()  # 报错:name 'fun1' is not defined

def fun1():
    for i in range(10):
        print(i, end='')

这是因为代码是从上到下执行的,执行到调用语句时,系统还没有标记到函数的定义位置。

5.1.4 函数的复用

函数的最大优势就是复用性:一次定义,多次调用,避免重复编写相同的代码块。

示例 1:阶乘函数的复用

python

# 定义阶乘函数:计算n的阶乘
def fun3(n):
    b = 1
    for i in range(1, n + 1):
        b = int(i) * b
    return b

# 复用函数:计算7! + 8! + 9! 的和
b = fun3(7) + fun3(8) + fun3(9)
print(b)

plaintext

408240

可以看到,我们只编写了一次阶乘的逻辑,就可以通过调用函数,计算多个数的阶乘,代码非常简洁。

示例 2:学生成绩统计函数的复用

python

# 定义函数:接收姓名、数学和计算机成绩,输出平均分
def fun5(name, math, computer):
    average = (math + computer) / 2
    print(name, ':\t数学成绩:', math, '\t计算机成绩:', computer, '\t平均成绩:', average)

# 调用函数,传入不同学生的数据
fun5('张三', 90, 89)
fun5('李四', 98, 84)
fun5('王五', 91, 80)

plaintext

张三 :   数学成绩: 90       计算机成绩: 89      平均成绩: 89.5
李四 :   数学成绩: 98       计算机成绩: 84      平均成绩: 91.0
王五 :   数学成绩: 91       计算机成绩: 80      平均成绩: 85.5

只需要 1 行调用代码,就能输出一名学生的成绩信息,大幅提升了代码编写效率,同时降低了出错率。

5.1.5 return 返回值

return语句的核心功能是将函数执行后的结果返回给调用函数的地方,共有 4 种使用方式:

1. 带 return 语句,返回一个值

python

def fun2():
    ls = ['张三', '李四']
    a = len(ls)
    # return将a的值返回给调用处
    return a

print(fun2())

plaintext

2
2. 不带 return 语句

如果函数中没有return语句,函数执行完后会默认返回None

python

def fun1():
    for i in range(10):
        print(i, end='')

# 打印函数的返回值
print(fun1())

plaintext

0123456789None
3. 带 return 语句,返回多个值

多个值会以元组的形式返回给调用处。

python

def fun2():
    a = 2
    b = '李四'
    # 同时返回a和b两个值
    return a, b

print(fun2())

plaintext

(2, '李四')
4. return 语句位于非末尾,提前结束函数

函数中可以存在多个return语句,执行到return时,函数会立即结束,后续代码不会执行。

python

def fun2(flag):
    a = 2
    b = '李四'
    if flag:
        # 条件满足时,直接返回b的值,函数结束
        return b
    # 条件不满足时,返回a和b
    return a, b

print(fun2(True))

plaintext

李四

5.2 函数的参数

前面的示例中,我们用到了无参和带参函数。在实际开发中,函数的参数形式非常灵活,下面将详细讲解 Python 函数的参数用法。

5.2.1 形参和实参

  • 形参(形式参数):定义函数时,()中的参数变量,用于接收调用时传递的数据。
  • 实参(实际参数):调用函数时,()中传入的真实数据,会按顺序复制给对应的形参。

格式:

python

# 定义函数:形参
def 函数名(形参1, 形参2, ...):
    代码块
    return 返回值

# 调用函数:实参
函数名(实参1, 实参2, ...)

使用形参和实参时,需要注意以下 5 点:

  1. 形参是变量,用于接收实参传递的数据。
  2. 多个形参之间用逗号,隔开。
  3. 实参的个数必须和形参的个数相同。
  4. 调用时,实参会按顺序依次复制给形参。
示例:输入数字求阶乘

python

# 定义函数:形参n接收要计算阶乘的数字
def fun3(n):
    b = 1
    for i in range(1, n + 1):
        b = int(i) * b
    return b

# 输入一个数字,作为实参传递给函数
a = input("请输入一个数字:")
print(a, '的阶乘:', fun3(int(a)))

运行结果(输入数字 5):

plaintext

请输入一个数字:5
5 的阶乘: 120
  • fun3(n)中的n是形参,用于接收实参数据。
  • fun3(int(a))中的int(a)是实参,将用户输入的数字传递给形参n

5.2.2 默认参数

当定义函数时,如果某些参数在大多数场景下都使用固定值,可以设置为默认参数,提前给形参设置一个默认数据。

格式:

python

def 函数名(必填参数, 默认参数=默认值):
    代码块
    return 返回值

使用默认参数的注意事项:

  1. 形参中,形参2=数据2表示将数据 2 赋值给形参 2,也叫默认参数。
  2. 调用函数时,如果不传入实参 2,形参 2 会自动使用默认值;如果传入了实参 2,则会覆盖默认值。
  3. 必填参数必须写在默认参数前面,否则会引发语法错误。
示例:计算圆的面积

python

# 定义函数:r为必填参数,pi为默认参数,默认值3.14
def circle_s(r, pi=3.14):
    return r * r * pi

# 调用函数:不传入pi,使用默认值
a = circle_s(3)
# 调用函数:不传入pi,使用默认值
b = circle_s(5)
print(a + b)

plaintext

106.76

如果需要提高计算精度,可以传入实参覆盖默认值:

python

# 传入实参3.1415926,覆盖pi的默认值
print(circle_s(6, 3.1415926))

也可以通过指定参数名的方式传递数据,此时参数的顺序可以与定义时不同:

python

# 指定参数名传递,顺序不受限制
a = circle_s(pi=3.14159, r=3)
print(a)
错误示例:必填参数写在默认参数后面

python

# 错误写法:必填参数r在默认参数pi后面,语法错误
def circle_s(pi=3.14, r):
    return r * r * pi

5.2.3 可变参数

当不确定函数需要接收的参数个数时,可以使用可变参数,在形参前加一个*,即可将其转换为可变参数,以元组的形式接收所有剩余数据。

格式:

python

def 函数名(必填参数, *可变参数):
    代码块

使用可变参数的注意事项:

  1. 可变参数的形参必须以*开头。
  2. 可变参数必须位于必填参数的右边,保证必填参数先接收数据。
  3. 可变参数以元组的形式接收数据。
示例:可变参数的使用

python

def fun3(a, *b):
    print(b)
    c = 0
    # 遍历可变参数b中的每个值,与a相乘后累加
    for i in b:
        c += a * i
    return c

print(fun3(3, 1, 2, 3))

plaintext

(1, 2, 3)
18
  • 实参 3 复制给必填参数a
  • 剩余的 1、2、3 全部传递给可变参数b,以元组形式存储。

5.2.4 关键字参数

关键字参数以**开头,与可变参数类似,但它以字典的形式接收数据,适用于接收带键值对的参数。

格式:

python

def 函数名(必填参数, **关键字参数):
    代码块

使用关键字参数的注意事项:

  1. 关键字参数的形参必须以**开头。
  2. 关键字参数必须位于必填参数的右边。
  3. 关键字参数以字典的形式接收数据,传递的数据需要包含键和值,用等号连接。
示例:关键字参数的使用

python

def fun4(a, **b):
    print(a, '课程考试成绩:')
    print(b)

# 调用函数,关键字参数以键值对形式传递
fun4('Python', zhangsan=90, lisi=95)

plaintext

Python 课程考试成绩:
{'zhangsan': 90, 'lisi': 95}

5.3 函数的变量作用范围

根据变量的作用范围,Python 变量分为全局变量局部变量,两者的区别和使用场景是本节的核心。

5.3.1 局部变量与全局变量

  • 局部变量:在函数内部定义的变量,只能在函数内部使用,函数外部无法访问。
  • 全局变量:在函数外部定义的变量,在整个代码文件中都可以使用,函数内部也能直接访问。
示例 1:局部变量的作用域

python

def fun5(a, b, c=10):
    d = a*a + b*b + c*c
    return d

fun5(1, 2, 3)
print(a)  # 报错

plaintext

NameError: name 'a' is not defined

abc是函数fun5的形参,属于局部变量;d是函数内部定义的变量,也属于局部变量。在函数外部无法访问这些变量,因此print(a)会报错。

示例 2:函数内访问全局变量

python

def fun5(a, b, c=10):
    d = a*a + b*b + c*c + e
    return d

e = 100  # 全局变量
print(fun5(1, 2, 3))

plaintext

114

函数内部可以直接访问全局变量e,因此代码可以正常运行。

5.3.2 函数中修改全局变量的问题

当函数内对全局变量直接赋值时,Python 会默认创建一个同名的局部变量,导致逻辑错误。

错误示例:修改全局变量时的常见错误

python

def fun7(b):
    print('a+b的值:', a+b)
    a = 2  # 试图修改全局变量a,Python会创建局部变量a
    print('a+b的值:', a+b)

a = 3  # 全局变量
fun7(13)
print('a的值:', a)

plaintext

Traceback (most recent call last):
  ...
UnboundLocalError: local variable 'a' referenced before assignment

报错原因:Python 在函数内看到对a的赋值语句,会默认创建局部变量a,但在赋值前先执行了print('a+b的值:', a+b),此时局部变量a还未定义,因此报错。

同名变量互不影响

python

def fun7(b):
    a = 2  # 局部变量a,和全局变量a无关
    print('a+b的值:', a+b)

a = 3  # 全局变量
fun7(13)
print('a的值:', a)

plaintext

a+b的值: 15
a的值: 3

函数内的a是局部变量,和全局变量a只是名字相同,实际是两个不同的变量,因此修改局部变量不会影响全局变量。

5.3.3 global 声明全局变量

如果需要在函数内修改全局变量,必须使用global关键字声明,告诉 Python 这是全局变量,而非局部变量。

示例:使用 global 声明全局变量

python

def fun7(b):
    global a  # 声明a是全局变量
    print('a的值:', a)
    a = 2  # 修改全局变量a
    print('a+b的值:', a+b)

a = 3  # 全局变量
fun7(13)
print('a的值:', a)

plaintext

a的值: 3
a+b的值: 15
a的值: 2

使用global声明后,函数内的a就是全局变量,修改后会影响函数外部的全局变量值。


5.4 导入其他模块中的函数

在项目开发中,我们会将不同功能的代码写在不同文件中,通过模块导入的方式在当前文件中使用其他文件的函数,实现模块化开发。

5.4.1 导入整个模块

使用import关键字导入整个文件模块,格式如下:

python

import 文件名

调用时需要加上文件名,格式:文件名.函数名(参数)

示例:导入 a.py 文件中的 fun6 函数

a.py 文件代码:

python

def fun6(n):
    b = 1
    for i in range(1, n+1):
        b = int(i) * b
    return b

b.py 文件代码:

python

import a  # 导入a.py模块
print(a.fun6(6))  # 调用a模块中的fun6函数

运行 b.py 的结果:

plaintext

720

导入多个模块时,可以用逗号分隔:

python

import a, b, c

5.4.2 导入文件中的指定函数

如果只需要使用文件中的某个函数,可以使用from...import语句导入指定函数,格式:

python

from 文件名 import 函数名

调用时无需添加文件名,直接使用函数名即可。

示例:导入 a1.py 中的 fun6 函数

a1.py 文件代码:

python

def fun6(n):
    b = 1
    for i in range(1, n+1):
        b = int(i) * b
    return b

def fun7():
    print("这是fun7函数")

def fun8():
    print("这是fun8函数")

b1.py 文件代码:

python

from a1 import fun6  # 仅导入fun6函数
print(fun6(6))  # 直接调用fun6

plaintext

720

导入多个指定函数时,用逗号分隔:

python

from a1 import fun6, fun8

5.4.3 导入文件中的全部函数

如果需要使用文件中的所有函数,可以使用from...import *语句,格式:

python

from 文件名 import *

调用时无需添加文件名,直接使用所有函数名。

示例:导入 a1.py 中的所有函数

python

from a1 import *
print(fun6(6))
fun7()
fun8()

plaintext

720
这是fun7函数
这是fun8函数

5.4.4 给导入的函数设置别名

当模块或函数名较长时,可以使用as关键字设置别名,简化调用。

  • 给模块设置别名:import 文件名 as 新模块名
  • 给函数设置别名:from 文件名 import 函数名 as 新函数名
示例:设置别名

python

# 给模块设置别名
import a as t
print(t.fun6(6))

# 给函数设置别名
from a import fun6 as f6
print(f6(6))

5.4.5 __name__属性与模块测试

__name__属性用于获取当前文件的名称:

  • 当文件是主文件(直接运行的文件)时,__name__的值为__main__
  • 当文件是次文件(被导入的文件)时,__name__的值为文件名。

我们通常利用if __name__ == '__main__':语句,让测试代码仅在直接运行文件时执行,被导入时不执行。

示例:模块测试代码

a6.py 文件代码:

python

def fun6(n):
    print(__name__)  # 输出当前文件的__name__属性
    b = 1
    for i in range(1, n+1):
        b = int(i) * b
    return b

if __name__ == '__main__':
    print(fun6(5))  # 仅当直接运行a6.py时执行
  • 直接运行 a6.py,输出:

    plaintext

    __main__
    120
    
  • 从 b6.py 导入 a6.py:

    python

    import a6
    print(a6.fun6(5))
    
     

    plaintext

    a6
    120
    
    此时if __name__ == '__main__':中的代码不会执行,仅执行了fun6函数内的代码。

5.5 排序函数 sorted () 详解

sorted()函数可以对所有可迭代的数据对象(如列表、字符串、元组)进行排序,比列表的sort()方法更灵活。

5.5.1 sorted () 函数的基本用法

sorted()函数的通用格式:

python

sorted(iterable, key=None, reverse=False)
  • iterable:可迭代对象,如列表、字符串、元组。
  • key:表示比较的函数,常和 lambda 配合使用。
  • reverse:排序顺序,False为升序(默认),True为降序。
示例 1:对列表进行升序 / 降序排序

python

a = [3, 7, 9, 4, 0, 2]
b = sorted(a)  # 默认升序
c = sorted(a, reverse=True)  # 降序
print(b)
print(c)

plaintext

[0, 2, 3, 4, 7, 9]
[9, 7, 4, 3, 2, 0]
示例 2:对字符串按 Unicode 顺序排序

python

d = sorted('nfweubvy')
print(d)

plaintext

['b', 'e', 'f', 'n', 'u', 'v', 'w', 'y']

字符串按每个字符的 Unicode 值从小到大排序。

5.5.2 结合 lambda 实现自定义排序

通过key参数传入 lambda 函数,可以实现自定义规则排序。

示例:按元素减去 5 后的绝对值排序

python

a = [3, 7, 9, 4, 0, 2]
b = sorted(a, key=lambda x: abs(x-5))
print(b)

plaintext

[4, 3, 7, 2, 9, 0]

排序规则:先计算每个元素x减去 5 的绝对值,再按绝对值从小到大排序,最终结果为[4, 3, 7, 2, 9, 0]


5.6 匿名函数 lambda

lambda 是 Python 的保留字,用于创建简单的匿名函数,格式如下:

python

函数名 = lambda 参数: 函数表达式

lambda 函数只能包含一个表达式,执行后会自动返回结果,无需使用return语句。

示例 1:定义一个加法 lambda 函数

python

# 定义lambda函数,实现x+y
fun1 = lambda x, y: x + y

# 调用函数
print(fun1(3, 4))

plaintext

7

等价于以下普通函数:

python

def fun1(x, y):
    return x + y

示例 2:将lambda作为函数的参数

函数语法

python

sorted(iterable, key=None, reverse=False)
参数 说明
iterable 可迭代对象,如列表、字符串、元组
key 比较函数,用于自定义排序规则,常与 lambda 配合使用
reverse 排序顺序:False 为升序(默认),True 为降序

基础排序示例

1. 列表升序 / 降序排序

python

# 原始列表
a = [3, 7, 9, 4, 0, 2]

# 1. 默认升序排序
c = sorted(a)
print("升序排序结果:", c)

# 2. 设置 reverse=True 实现降序排序
d = sorted(a, reverse=True)
print("降序排序结果:", d)

plaintext

升序排序结果: [0, 2, 3, 4, 7, 9]
降序排序结果: [9, 7, 4, 3, 2, 0]

2. 字符串排序(按 Unicode 码值)

sorted() 对字符串排序时,会按照每个字符的 Unicode 码值从小到大排列。

python

# 原始字符串
b = 'nfweubvy'

# 对字符串排序
e = sorted(b)
print("字符串排序结果:", e)

plaintext

字符串排序结果: ['b', 'e', 'f', 'n', 'u', 'v', 'w', 'y']

例如字母 'b' 的 Unicode 值小于 'c',因此排在前面。


结合 lambda 实现自定义排序

通过 key 参数传入 lambda 匿名函数,可以实现自定义规则排序,这是 sorted() 函数最强大的用法之一。

示例:按「元素与 5 的距离」排序

需求:将列表 [3, 7, 9, 4, 0, 2] 按「每个元素减去 5 后的绝对值」从小到大排序。

python

# 原始列表
a = [3, 7, 9, 4, 0, 2]

# 使用 lambda 定义排序规则:按 abs(x-5) 的值排序
b = sorted(a, key=lambda x: abs(x-5))
print("按与5的距离排序结果:", b)

plaintext

按与5的距离排序结果: [4, 3, 7, 2, 9, 0]

更多推荐