Python 函数

函数是组织好的、可重复使用的代码段,用来实现单一或相关联的功能。

一、定义函数

使用 def 关键字定义函数:

def 函数名(参数列表):
    """文档字符串(可选)"""
    函数体
    return 表达式
def hello():
    print("Hello World!")

hello()  # 调用函数 → Hello World!
def max_num(a, b):
    if a > b:
        return a
    else:
        return b

print(max_num(4, 5))  # 5

return 语句用于退出函数并返回一个值给调用方。不带 returnreturn 后没有表达式时,返回 None

二、参数类型

2.1 必需参数

必须按正确的顺序和数量传入:

def printme(str):
    print(str)

printme("Hello")  # ✅
# printme()       # ❌ TypeError: missing 1 required positional argument

2.2 关键字参数

调用时通过参数名指定值,顺序可以和定义时不一致:

def printinfo(name, age):
    print("名字:", name)
    print("年龄:", age)

printinfo(age=50, name="runoob")  # ✅ 顺序不同也能调用

2.3 默认参数

调用时如果不传某个参数,就使用默认值。默认参数必须放在必需参数后面

def printinfo(name, age=35):
    print("名字:", name)
    print("年龄:", age)

printinfo("runoob")       # 名字: runoob, 年龄: 35
printinfo("runoob", 50)   # 名字: runoob, 年龄: 50
# ❌ 错误写法 — 默认参数不能在前面
def printinfo(age=35, name):  # SyntaxError: non-default argument follows default argument
    pass

2.4 不定长参数 *args

加了 * 的参数会以元组的形式接收所有未命名的变量参数:

def printinfo(arg1, *vartuple):
    print("输出:")
    print(arg1)
    for var in vartuple:
        print(var)

printinfo(10)          # 输出: 10
printinfo(70, 60, 50)  # 输出: 70 \n 60 \n 50

2.5 不定长参数 **kwargs

加了 ** 的参数会以字典的形式接收所有关键字参数:

def printinfo(arg1, **vardict):
    print("输出:")
    print(arg1)
    print(vardict)

printinfo(1, a=2, b=3)
# 输出:
# 1
# {'a': 2, 'b': 3}

2.6 强制位置参数 / 和强制关键字参数 *

Python 3.8 新增的语法,用 /* 来分隔参数:

def f(a, b, /, c, d, *, e, f):
    print(a, b, c, d, e, f)

f(10, 20, 30, d=40, e=50, f=60)   # ✅
# f(10, b=20, c=30, d=40, e=50, f=60)  # ❌ b 必须用位置参数
# f(10, 20, 30, 40, 50, f=60)          # ❌ e 必须用关键字参数
  • / 之前的参数:只能用位置参数,不能用关键字
  • * 之后的参数:只能用关键字,不能用位置
  • 中间的参数:两种都可以

三、参数传递:可变 vs 不可变

Python 中参数传递严格来说既不是值传递也不是引用传递,而是传对象引用。

3.1 传不可变对象(字符串、数字、元组)

函数内部修改参数时,实际上是创建了一个新对象,不影响外部变量

def change(a):
    print(id(a))   # 4379369136 — 和外部的 a 指向同一个对象
    a = 10
    print(id(a))   # 4379369424 — 新对象,和外部的 a 没关系了

a = 1
print(id(a))       # 4379369136
change(a)
print(a)           # 1 — 外部 a 没变

3.2 传可变对象(列表、字典)

函数内部修改参数的内容(如 append),会影响外部变量

def changeme(mylist):
    mylist.append([1, 2, 3, 4])
    print("函数内取值:", mylist)

mylist = [10, 20, 30]
changeme(mylist)
print("函数外取值:", mylist)
# 函数内取值: [10, 20, 30, [1, 2, 3, 4]]
# 函数外取值: [10, 20, 30, [1, 2, 3, 4]]  — 外部也变了!

但如果在函数内部对参数重新赋值,外部不受影响:

def changeme(mylist):
    mylist = [1, 2, 3, 4]   # 重新赋值,创建了新对象
    print("函数内取值:", mylist)

mylist = [10, 20, 30]
changeme(mylist)
print("函数外取值:", mylist)
# 函数内取值: [1, 2, 3, 4]
# 函数外取值: [10, 20, 30]  — 外部没变

一句话区分: 修改对象的内容(append/修改元素)会影响外部,重新赋值(=)不会。

四、返回值

Python 函数可以返回多个值,以元组形式返回:

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

res = return_sum(4, 5)
print(res)  # 9
# 返回多个值 — 本质上返回一个元组
def fun(a, b):
    return a, b, a + b

print(fun(1, 2))  # (1, 2, 3)

# 解包接收
x, y, z = fun(1, 2)
print(x, y, z)    # 1 2 3
# 不写 return 或 return 后没有值,返回 None
def empty_return(x, y):
    c = x + y
    return

res = empty_return(4, 5)
print(res)  # None

五、变量作用域

变量的作用范围遵循 LEGB 规则,从内到外依次查找:

L(Local)→ E(Enclosing)→ G(Global)→ B(Built-in)

x = "全局变量"

def outer():
    x = "外层函数变量"

    def inner():
        x = "内层函数变量"
        print(x)    # L — 找到 inner 的 x → "内层函数变量"

    inner()

outer()
x = "全局变量"

def outer():
    x = "外层函数变量"

    def inner():
        # inner 没有定义 x,向上找
        print(x)    # E — 找到 outer 的 x → "外层函数变量"

    inner()

outer()
x = "全局变量"

def outer():
    # outer 没有定义 x,继续向上找
    def inner():
        # inner 和 outer 都没有 x
        print(x)    # G — 找到全局的 x → "全局变量"

    inner()

outer()

5.1 global 关键字

在函数内部修改全局变量,需要先用 global 声明:

a = 10

def sum(n):
    global a       # 声明 a 是全局变量
    n += a
    a = 11
    print("a =", a, ", n =", n)

sum(3)             # a = 11, n = 13
print("外 a =", a) # a = 11 — 全局变量被修改了

如果函数内部不使用 global,直接对同名变量赋值会创建一个局部变量,不会影响全局:

a = 10

def test():
    a = 20       # 这是局部变量,不是修改全局的 a
    print("a =", a)

test()           # a = 20
print("外 a =", a)  # a = 10 — 全局的没变

5.2 nonlocal 关键字

在嵌套函数中,修改外层函数(不是全局)的变量:

def outer():
    count = 0

    def inner():
        nonlocal count   # 声明 count 是外层函数的变量
        count += 1
        print(count)

    inner()   # 1
    inner()   # 2
    inner()   # 3

outer()

nonlocal 只能修改外层函数的局部变量,不能修改全局变量。如果要修改全局变量,用 global

六、Lambda 匿名函数

Lambda 是一种只包含一个表达式的小型匿名函数,不需要用 def 定义。

lambda [arg1, arg2, ...]: expression
# 普通函数
def add(a, b):
    return a + b

# 等价的 lambda
add = lambda a, b: a + b

print(add(10, 20))  # 30

Lambda 没有函数名,只能通过赋值给变量或作为参数传递:

x = lambda a: a + 10
print(x(5))   # 15

y = lambda a, b: a * b
print(y(5, 6))  # 30

z = lambda: "Hello, world!"
print(z())   # Hello, world!

Lambda 也可以使用关键字参数和默认值:

g = lambda x, y=0: x**2 + y**2

print(g(2, 3))  # 13
print(g(2))     # 4
print(g(y=3))   # 9

6.1 Lambda 与 map()

map() 对序列中的每个元素应用一个函数:

numbers = [1, 2, 3, 4, 5]
squared = list(map(lambda x: x**2, numbers))
print(squared)  # [1, 4, 9, 16, 25]

等价于列表推导式:

squared = [x**2 for x in numbers]

6.2 Lambda 与 filter()

filter() 筛选满足条件的元素:

numbers = [1, 2, 3, 4, 5, 6, 7, 8]
even = list(filter(lambda x: x % 2 == 0, numbers))
print(even)  # [2, 4, 6, 8]

等价于:

even = [x for x in numbers if x % 2 == 0]

6.3 Lambda 与 reduce()

reduce() 对序列进行累积计算:

from functools import reduce

numbers = [1, 2, 3, 4, 5]
product = reduce(lambda x, y: x * y, numbers)
print(product)  # 120 — 即 1*2*3*4*5

6.4 Lambda 作为参数传递

Lambda 最常见的用途就是作为其他函数的参数:

students = [("Alice", 85), ("Bob", 92), ("Charlie", 78)]

# 按成绩排序
students.sort(key=lambda s: s[1])
print(students)  # [('Charlie', 78), ('Alice', 85), ('Bob', 92)]

# 按姓名排序
students.sort(key=lambda s: s[0])
print(students)  # [('Alice', 85), ('Bob', 92), ('Charlie', 78)]

6.5 Lambda 在闭包中

def myfunc(n):
    return lambda a: a * n

mydoubler = myfunc(2)   # n=2,返回一个乘以 2 的 lambda
mytripler = myfunc(3)   # n=3,返回一个乘以 3 的 lambda

print(mydoubler(11))   # 22
print(mytripler(11))   # 33

这里 myfunc 返回了一个 lambda,lambda 捕获了外层的 n 值,这就是闭包

七、函数作为参数传递

Python 中函数也是对象,可以作为参数传递给其他函数:

def execute(f):
    """执行一个没有参数的函数"""
    f()

def hello():
    print("Hello, world!")

execute(hello)  # Hello, world!
def apply(func, value):
    return func(value)

print(apply(lambda x: x * 2, 5))   # 10
print(apply(lambda x: x ** 2, 5))  # 25

八、文档字符串(DocString)

函数的文档说明写在 def 下面,用三引号包裹。可以通过 函数名.__doc__ 查看:

def add(a, b):
    """这是 add 函数文档,计算两个数的和"""
    return a + b

print(add.__doc__)  # 这是 add 函数文档,计算两个数的和

九、类型注解(Type Hints)

Python 3.5+ 支持在函数参数和返回值上标注类型,不会影响运行,但对 IDE 智能补全很有帮助:

def add(a: int, b: int) -> int:
    return a + b

def greet(name: str) -> None:
    print(f"Hello, {name}")
# 复杂类型标注
from typing import List, Dict

def process(data: List[int]) -> Dict[str, int]:
    return {"sum": sum(data), "len": len(data)}