跳转至

函数

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

1. 定义与调用

使用 def 关键字定义函数。

def say_hello():
    """这是一个简单的问候函数"""
    print("Hello, Python!")

# 调用函数
say_hello()

2. 参数传递

位置参数 (Positional Arguments)

按顺序传递参数。

def sub(a, b):
    return a - b

print(sub(10, 3))  # 7
print(sub(3, 10))  # -7

关键字参数 (Keyword Arguments)

通过参数名指定,顺序不重要。

print(sub(b=3, a=10))  # 7

默认参数 (Default Arguments)

为参数提供默认值。注意:默认参数必须放在非默认参数之后。

def greet(name, msg="Good morning"):
    print(f"{msg}, {name}")

greet("Alice")                  # 使用默认 msg
greet("Bob", "Good evening")    # 覆盖默认 msg

可变默认参数陷阱

千万不要使用可变对象(如列表、字典)作为默认参数值!

# ❌ 错误做法
def append_to(num, target=[]):
    target.append(num)
    return target

print(append_to(1)) # [1]
print(append_to(2)) # [1, 2] -> 竟然记住了上次的状态!

# ✅ 正确做法
def append_to(num, target=None):
    if target is None:
        target = []
    target.append(num)
    return target

3. 返回值 (return)

  • 使用 return 返回结果。
  • 如果没有 return,默认返回 None
  • 可以返回多个值(本质是返回一个元组)。
def get_user_info():
    name = "Alice"
    age = 25
    return name, age  # 返回元组 ("Alice", 25)

n, a = get_user_info() # 解包

4. 可变长参数

用于处理不确定数量的参数。

  • *args: 接收任意数量的位置参数(打包成元组)。
  • **kwargs: 接收任意数量的关键字参数(打包成字典)。
def func(required, *args, **kwargs):
    print(f"Required: {required}")
    print(f"Args (tuple): {args}")
    print(f"Kwargs (dict): {kwargs}")

func(1, 2, 3, 4, name="Alice", val=True)
# Output:
# Required: 1
# Args (tuple): (2, 3, 4)
# Kwargs (dict): {'name': 'Alice', 'val': True}

5. 作用域 (Scope)

  • Local (局部): 函数内部定义的变量。
  • Global (全局): 模块级别定义的变量。

在函数内部修改全局变量需要使用 global 关键字(不推荐)。

count = 0  # 全局变量

def increment():
    global count
    count += 1

increment()
print(count) # 1

Python 特别之处?

Python 中循环变量在循环外可访问的原因

在 Python 中,循环内定义的变量在循环结束后仍然可以访问,这是因为 Python 没有块级作用域(block scope)

核心原因

Python 的作用域只有以下几种:

  • 模块级作用域(全局)
  • 函数级作用域
  • 类级作用域

而像 forwhileif 等代码块 不会创建新的作用域,因此其中定义的变量仍然属于外层作用域。

示例说明
# 示例 1:for 循环
for i in range(3):
    x = i * 2
print(i)  # 输出: 2
print(x)  # 输出: 4

# 示例 2:if 语句
if True:
    y = 100
print(y)  # 输出: 100

# 示例 3:while 循环
count = 0
while count < 3:
    temp = count
    count += 1
print(temp)  # 输出: 2
与其他语言对比
# Python
for i in range(3):
    pass
print(i)  # ✅ 可以访问,输出 2

# Java / C++
for (int i = 0; i < 3; i++) {
    // ...
}
// System.out.println(i);  //  编译错误i 超出作用域
函数内部的情况

即使在函数中,循环变量也属于函数作用域:

def test():
    for j in range(3):
        value = j
    print(j)     # ✅ 输出: 2
    print(value) # ✅ 输出: 2

test()
# print(j)  # ❌ NameError,j 不在全局作用域
注意事项
⚠️ 变量泄露风险
# 可能意外覆盖外部变量
data = [1, 2, 3]
for data in   # ❌ 循环变量覆盖了原列表
    print(data)
print(data)  # 输出: 3,原列表已丢失
⚠️ 闭包中的陷阱
funcs = []
for i in range(3):
    funcs.append(lambda: i)

print([f() for f in funcs])  # 输出: [2, 2, 2],不是 [0, 1, 2]

解决方案:

funcs = []
for i in range(3):
    funcs.append(lambda x=i: x)  # 使用默认参数捕获当前值

print([f() for f in funcs])  # 输出: [0, 1, 2]
最佳实践建议
  1. 避免在循环中覆盖重要变量名
  2. 如需限制变量作用域,使用函数封装
  3. 在闭包中注意变量捕获问题
  4. 循环后不再使用的变量可显式删除del i
总结
特性 Python Java/C++
循环作用域 函数/模块级 块级作用域
循环后可访问 ✅ 是 ❌ 否
设计哲学 简洁、灵活 严格、安全

这是 Python 的设计选择,旨在简化语法,但开发者需注意潜在的作用域问题,编写更健壮的代码。

6. Lambda 函数 (匿名函数)

用于定义简单的、一行的函数。

# 语法: lambda arguments: expression
square = lambda x: x * x

print(square(5))  # 25

# 常见用途:作为参数传给 sort, map, filter
data = [(1, 'd'), (2, 'b'), (4, 'a'), (3, 'c')]
# 按第二个元素排序
data.sort(key=lambda x: x[1])
print(data) # Sort by letter

7. 类型提示 (Type Hints)

Python 3.5+ 引入,虽然不强制,但强烈建议写上。

def calculate_area(width: float, height: float) -> float:
    return width * height

本章小结

概念 说明
def 定义函数
return 返回值,可返回多个
参数 位置参数、关键字参数、默认参数
*args 可变位置参数 (Tuple)
**kwargs 可变关键字参数 (Dict)
lambda 匿名函数,短小精悍

练习题

  1. 基本函数:编写 max_of_three(a, b, c) 函数,返回三个数中的最大值。
  2. 默认参数:编写 power(x, n=2) 函数,计算 x 的 n 次方,默认计算平方。
  3. 可变参数:编写 sum_all(*args) 函数,计算所有传入数字的总和。

下一章:文件操作