跳到主要内容

Python 函数

函数是组织代码的基本单元,可以提高代码的复用性和可读性。Python 中的函数是一等公民,可以作为参数传递、作为返回值返回,这为函数式编程提供了基础。

函数定义

基本语法

def function_name(parameters):
"""函数文档字符串(docstring)"""
# 函数体
return result # 返回值

示例

def greet():
"""问候函数"""
print("你好,欢迎学习 Python!")

# 调用函数
greet()

函数的本质

在 Python 中,函数本质是一个对象。函数名只是指向这个对象的引用:

def greet():
print("你好!")

# 函数可以被赋值给变量
say_hello = greet
say_hello() # 你好!

# 函数可以存储在数据结构中
funcs = [greet, print, len]
funcs[0]() # 你好!

# 查看函数的类型
print(type(greet)) # <class 'function'>

这种特性使得函数可以作为参数传递、作为返回值返回,是 Python 支持函数式编程的基础。

函数参数

位置参数

位置参数是最基本的参数类型,调用时按照位置顺序传递:

def add(a, b):
"""两个数相加"""
return a + b

result = add(3, 5) # a=3, b=5,result = 8

默认参数

默认参数允许调用时省略某些参数:

def greet(name, greeting="你好"):
"""问候函数,默认使用"你好"作为问候语"""
print(f"{greeting}{name}!")

greet("张三") # 你好,张三!
greet("李四", "欢迎") # 欢迎,李四!

重要提示:默认参数的值在函数定义时计算一次,不要使用可变对象作为默认值:

# 错误示例:使用可变对象作为默认值
def add_item(item, items=[]): # 危险!
items.append(item)
return items

print(add_item("a")) # ['a']
print(add_item("b")) # ['a', 'b'] - 不是预期的结果!

# 正确做法:使用 None 作为默认值
def add_item(item, items=None):
if items is None:
items = []
items.append(item)
return items

print(add_item("a")) # ['a']
print(add_item("b")) # ['b'] - 正确

关键字参数

关键字参数允许按名称传递参数,顺序可以任意:

def person(name, age, city):
print(f"姓名:{name}")
print(f"年龄:{age}")
print(f"城市:{city}")

# 使用关键字参数
person(name="张三", age=20, city="北京")
person(city="上海", name="李四", age=25) # 顺序可以打乱

不定长参数

*args - 接收任意数量的位置参数

*args 将所有位置参数收集为一个元组:

def sum_all(*args):
"""计算所有参数的和"""
print(f"args 的类型:{type(args)}") # <class 'tuple'>
total = 0
for num in args:
total += num
return total

print(sum_all(1, 2, 3)) # 6
print(sum_all(1, 2, 3, 4, 5)) # 15
print(sum_all()) # 0

**kwargs - 接收任意数量的关键字参数

**kwargs 将所有关键字参数收集为一个字典:

def print_info(**kwargs):
"""打印所有关键字参数"""
print(f"kwargs 的类型:{type(kwargs)}") # <class 'dict'>
for key, value in kwargs.items():
print(f"{key}: {value}")

print_info(name="张三", age=20, city="北京")
# 输出:
# name: 张三
# age: 20
# city: 北京

解包参数

*** 也可以用于解包:

def add(a, b, c):
return a + b + c

# 解包列表/元组
numbers = [1, 2, 3]
result = add(*numbers) # 等同于 add(1, 2, 3)

# 解包字典
params = {"a": 1, "b": 2, "c": 3}
result = add(**params) # 等同于 add(a=1, b=2, c=3)

参数组合使用

参数定义的顺序必须是:位置参数 → 默认参数 → *args → 关键字限定参数 → **kwargs

def func(a, b, c=0, *args, **kwargs):
print(f"a={a}, b={b}, c={c}")
print(f"args={args}")
print(f"kwargs={kwargs}")

func(1, 2, 3, 4, 5, name="张三", age=20)
# a=1, b=2, c=3
# args=(4, 5)
# kwargs={'name': '张三', 'age': 20}

位置限定参数(Positional-Only Parameters)

Python 3.8 引入了位置限定参数语法(/),用于指定某些参数只能通过位置传递:

def power(base, exp, /):
"""位置限定参数:base 和 exp 只能通过位置传递"""
return base ** exp

power(2, 3) # 8 - 正确
power(2, exp=3) # 错误!TypeError

位置限定参数的作用:

  • 防止参数名被外部依赖,方便重构 API
  • 参数名称改变时不影响调用代码
  • 许多内置函数使用此特性(如 pow(), len() 等)
# 混合使用位置限定参数和普通参数
def greet(name, /, greeting="你好"):
"""name 只能位置传递,greeting 可以关键字传递"""
return f"{greeting}{name}!"

greet("张三") # 你好,张三!
greet("张三", greeting="欢迎") # 欢迎,张三!
greet(name="张三") # 错误!

关键字限定参数(Keyword-Only Parameters)

使用 * 后面的参数必须使用关键字传递:

def create_user(name, *, age, city="未知"):
"""age 必须使用关键字传递"""
return {"name": name, "age": age, "city": city}

create_user("张三", age=20) # 正确
create_user("张三", age=20, city="北京") # 正确
create_user("张三", 20) # 错误!

关键字限定参数的作用:

  • 强制调用者明确指定参数名,提高代码可读性
  • 避免参数顺序错误
  • 便于未来添加新参数

函数返回值

return 语句

return 语句结束函数执行并返回结果:

def add(a, b):
return a + b

result = add(3, 5) # result = 8

返回多个值

Python 函数可以返回多个值,实际上是返回一个元组:

def get_person():
name = "张三"
age = 20
city = "北京"
return name, age, city # 返回一个元组

# 使用元组接收
person = get_person()
print(person) # ('张三', 20, '北京')

# 使用多个变量接收(元组解包)
name, age, city = get_person()
print(name) # 张三

无返回值

没有 return 语句或 return 后面没有值的函数返回 None

def print_hello():
print("Hello")

result = print_hello()
print(result) # None

def do_nothing():
return # 等同于 return None

result = do_nothing()
print(result) # None

提前返回

return 可以用于提前退出函数:

def divide(a, b):
"""安全的除法运算"""
if b == 0:
return None # 提前返回
return a / b

print(divide(10, 2)) # 5.0
print(divide(10, 0)) # None

变量作用域

局部变量

在函数内部定义的变量是局部变量,只能在函数内部访问:

def test():
x = 10 # 局部变量
print(x)

test() # 10
# print(x) # 错误:NameError: name 'x' is not defined

全局变量

在函数外部定义的变量是全局变量,可以在整个模块中访问:

x = 10  # 全局变量

def test():
print(x) # 可以读取全局变量

test() # 10

使用 global 关键字

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

x = 10

def test():
global x # 声明使用全局变量 x
x = 20 # 修改全局变量

test()
print(x) # 20

最佳实践:尽量避免使用全局变量,使用参数和返回值传递数据更清晰。

nonlocal 关键字

在嵌套函数中修改外层函数的局部变量需要使用 nonlocal 声明:

def outer():
x = 10

def inner():
nonlocal x # 声明使用外层变量
x = 20 # 修改外层变量

inner()
print(x) # 20

outer()

作用域查找顺序(LEGB 规则)

Python 查找变量的顺序:Local → Enclosing → Global → Built-in

x = "global"

def outer():
x = "enclosing"

def inner():
x = "local"
print(x) # local

inner()
print(x) # enclosing

outer()
print(x) # global

匿名函数(lambda)

lambda 函数是使用 lambda 关键字创建的匿名函数,只能包含一个表达式。

基本语法

lambda parameters: expression

示例

# 单参数
square = lambda x: x ** 2
print(square(5)) # 25

# 多参数
add = lambda a, b: a + b
print(add(3, 5)) # 8

# 带默认值
greet = lambda name, greeting="你好": f"{greeting}{name}!"
print(greet("张三")) # 你好,张三!

lambda 与内置函数结合

lambda 函数常用于需要简单函数作为参数的场景:

# 与 map() 配合:对每个元素应用函数
numbers = [1, 2, 3, 4, 5]
squared = list(map(lambda x: x ** 2, numbers))
print(squared) # [1, 4, 9, 16, 25]

# 与 filter() 配合:筛选符合条件的元素
numbers = [1, 2, 3, 4, 5, 6]
even = list(filter(lambda x: x % 2 == 0, numbers))
print(even) # [2, 4, 6]

# 与 sorted() 配合:自定义排序规则
students = [
{"name": "张三", "age": 20},
{"name": "李四", "age": 18},
{"name": "王五", "age": 22}
]
sorted_students = sorted(students, key=lambda s: s["age"])
print(sorted_students)
# [{'name': '李四', 'age': 18}, {'name': '张三', 'age': 20}, {'name': '王五', 'age': 22}]

# 与 reduce() 配合:累积计算
from functools import reduce
numbers = [1, 2, 3, 4, 5]
total = reduce(lambda x, y: x + y, numbers)
print(total) # 15

lambda 的限制

lambda 函数只能包含一个表达式,不能包含语句:

# 错误:lambda 不能包含 print 语句
# f = lambda x: print(x)

# 正确:使用 def 定义
def f(x):
print(x)
return x

最佳实践:如果逻辑复杂,使用 def 定义普通函数,代码更清晰。

闭包(Closure)

闭包是指引用了外部作用域变量的内部函数,即使外部函数已经执行完毕,内部函数仍然可以访问这些变量。

闭包的基本概念

def outer(x):
"""外部函数"""
def inner(y):
"""内部函数,引用了外部变量 x"""
return x + y
return inner # 返回内部函数

# 创建闭包
add_five = outer(5) # x = 5 被"记住"了
print(add_five(10)) # 15
print(add_five(20)) # 25

add_ten = outer(10) # x = 10 被记住
print(add_ten(5)) # 15

在这个例子中:

  • outer 是外部函数,inner 是内部函数
  • inner 引用了外部变量 x,形成了闭包
  • 即使 outer 执行完毕,inner 仍然可以访问 x

闭包的三个条件

形成闭包需要满足三个条件:

  1. 存在嵌套函数(内部函数)
  2. 内部函数引用了外部函数的变量
  3. 外部函数返回内部函数
def make_multiplier(factor):
"""创建乘法器"""
def multiply(number):
return number * factor # 引用外部变量 factor
return multiply # 返回内部函数

double = make_multiplier(2)
triple = make_multiplier(3)

print(double(5)) # 10
print(triple(5)) # 15

闭包的应用场景

1. 数据封装

def make_counter():
"""创建计数器"""
count = 0

def counter():
nonlocal count
count += 1
return count

return counter

counter1 = make_counter()
print(counter1()) # 1
print(counter1()) # 2
print(counter1()) # 3

counter2 = make_counter() # 独立的计数器
print(counter2()) # 1

2. 配置函数

def make_url_formatter(base_url):
"""创建 URL 格式化器"""
def format_url(path):
return f"{base_url}/{path}"
return format_url

github_api = make_url_formatter("https://api.github.com")
print(github_api("users")) # https://api.github.com/users
print(github_api("repos")) # https://api.github.com/repos

3. 延迟计算

def lazy_sum(*args):
"""延迟求和"""
def calculate():
return sum(args)
return calculate

sum_func = lazy_sum(1, 2, 3, 4, 5)
# 此时还没有计算
print(sum_func()) # 15 - 真正计算

查看闭包变量

def outer(x):
def inner():
return x
return inner

closure = outer(10)

# 查看闭包变量
print(closure.__closure__) # (<cell at ...: int object at ...>,)
print(closure.__closure__[0].cell_contents) # 10

装饰器(Decorator)

装饰器是一种设计模式,用于在不修改原函数代码的情况下,扩展函数的功能。装饰器本质上是一个高阶函数,接收一个函数作为参数,返回一个新的函数。

装饰器的基本概念

装饰器的工作原理很简单:用一个新函数"包裹"原函数,在调用原函数前后执行额外的逻辑。

# 装饰器的等价形式
@decorator
def func():
pass

# 等价于
def func():
pass
func = decorator(func)

理解装饰器的关键是:@decorator 只是语法糖,它等价于把函数作为参数传给装饰器函数,然后用返回值替换原函数。

简单装饰器

def my_decorator(func):
"""定义一个装饰器"""
def wrapper():
print("函数执行前")
func() # 调用原函数
print("函数执行后")
return wrapper

@my_decorator
def say_hello():
print("你好!")

# 调用被装饰的函数
say_hello()
# 输出:
# 函数执行前
# 你好!
# 函数执行后

带参数的装饰器

被装饰的函数可能有参数,装饰器需要处理这些参数:

def log_decorator(func):
"""日志装饰器"""
def wrapper(*args, **kwargs):
print(f"调用函数:{func.__name__}")
print(f"参数:args={args}, kwargs={kwargs}")
result = func(*args, **kwargs) # 调用原函数
print(f"返回值:{result}")
return result
return wrapper

@log_decorator
def add(a, b):
return a + b

result = add(3, 5)
# 输出:
# 调用函数:add
# 参数:args=(3, 5), kwargs={}
# 返回值:8
print(result) # 8

使用 functools.wraps 保留元信息

装饰器会改变原函数的元信息(如 __name__, __doc__),使用 functools.wraps 可以保留这些信息:

from functools import wraps

def my_decorator(func):
@wraps(func) # 保留原函数的元信息
def wrapper(*args, **kwargs):
"""wrapper 的文档字符串"""
return func(*args, **kwargs)
return wrapper

@my_decorator
def example():
"""原函数的文档字符串"""
pass

print(example.__name__) # example(没有 @wraps 会是 wrapper)
print(example.__doc__) # 原函数的文档字符串

带参数的装饰器(装饰器工厂)

装饰器本身也可以接受参数:

from functools import wraps

def repeat(times):
"""重复执行装饰器"""
def decorator(func):
@wraps(func)
def wrapper(*args, **kwargs):
for _ in range(times):
result = func(*args, **kwargs)
return result
return wrapper
return decorator

@repeat(3)
def say_hello():
print("你好!")

say_hello()
# 输出三次:
# 你好!
# 你好!
# 你好!

类装饰器

类也可以作为装饰器,通过实现 __call__ 方法:

class CountCalls:
"""统计函数调用次数的装饰器"""

def __init__(self, func):
self.func = func
self.count = 0

def __call__(self, *args, **kwargs):
self.count += 1
print(f"调用次数:{self.count}")
return self.func(*args, **kwargs)

@CountCalls
def say_hello():
print("你好!")

say_hello() # 调用次数:1
say_hello() # 调用次数:2
say_hello() # 调用次数:3

装饰器的常见应用

1. 计时装饰器

import time
from functools import wraps

def timer(func):
"""测量函数执行时间"""
@wraps(func)
def wrapper(*args, **kwargs):
start = time.time()
result = func(*args, **kwargs)
end = time.time()
print(f"{func.__name__} 执行时间:{end - start:.4f} 秒")
return result
return wrapper

@timer
def slow_function():
time.sleep(1)
return "完成"

slow_function() # slow_function 执行时间:1.0012 秒

2. 缓存装饰器

from functools import wraps

def memoize(func):
"""简单的缓存装饰器"""
cache = {}

@wraps(func)
def wrapper(*args):
if args in cache:
print(f"从缓存返回:{args}")
return cache[args]
result = func(*args)
cache[args] = result
return result

return wrapper

@memoize
def fibonacci(n):
if n <= 1:
return n
return fibonacci(n - 1) + fibonacci(n - 2)

print(fibonacci(10)) # 55

3. 权限检查装饰器

from functools import wraps

def require_auth(func):
"""权限检查装饰器"""
@wraps(func)
def wrapper(*args, **kwargs):
# 假设有一个检查权限的函数
if not check_permission():
raise PermissionError("没有权限访问")
return func(*args, **kwargs)
return wrapper

def check_permission():
# 实际应用中检查用户权限
return True

@require_auth
def admin_function():
return "管理员功能"

print(admin_function()) # 管理员功能

4. 重试装饰器

import time
from functools import wraps

def retry(times=3, delay=1):
"""重试装饰器"""
def decorator(func):
@wraps(func)
def wrapper(*args, **kwargs):
for attempt in range(times):
try:
return func(*args, **kwargs)
except Exception as e:
if attempt == times - 1:
raise
print(f"第 {attempt + 1} 次失败,{delay} 秒后重试...")
time.sleep(delay)
return wrapper
return decorator

@retry(times=3, delay=0.5)
def unstable_function():
import random
if random.random() < 0.7:
raise ValueError("随机失败")
return "成功"

内置装饰器

Python 提供了一些内置装饰器:

class MyClass:
@staticmethod
def static_method():
"""静态方法:不需要实例即可调用"""
print("静态方法")

@classmethod
def class_method(cls):
"""类方法:第一个参数是类本身"""
print(f"类方法,类名:{cls.__name__}")

@property
def prop(self):
"""属性装饰器:将方法变成属性"""
return self._value

@prop.setter
def prop(self, value):
self._value = value

# 使用
MyClass.static_method()
MyClass.class_method()
obj = MyClass()
obj.prop = 10
print(obj.prop) # 10

生成器(Generator)

生成器是一种特殊的迭代器,使用 yield 语句逐个产生值,而不是一次性返回所有结果。生成器可以节省内存,特别适合处理大量数据。

生成器函数

包含 yield 语句的函数是生成器函数:

def count_up_to(n):
"""生成器函数:生成 1 到 n 的整数"""
i = 1
while i <= n:
yield i # 产生一个值,暂停执行
i += 1

# 创建生成器
counter = count_up_to(5)
print(type(counter)) # <class 'generator'>

# 迭代生成器
for num in counter:
print(num)
# 输出:1 2 3 4 5

yield 的工作原理

当生成器函数被调用时,它返回一个生成器对象,但函数体并不立即执行。每次调用 next() 时,函数执行到下一个 yield 语句并暂停:

def simple_generator():
"""简单的生成器示例"""
print("第一次 yield")
yield 1
print("第二次 yield")
yield 2
print("第三次 yield")
yield 3
print("生成器结束")

gen = simple_generator()
print("--- 开始迭代 ---")
print(next(gen)) # 第一次 yield → 1
print(next(gen)) # 第二次 yield → 2
print(next(gen)) # 第三次 yield → 3
# print(next(gen)) # StopIteration 异常

生成器表达式

类似于列表推导式,但使用圆括号,生成器表达式返回生成器对象:

# 列表推导式:立即生成所有元素
squares_list = [x ** 2 for x in range(10)]
print(type(squares_list)) # <class 'list'>

# 生成器表达式:按需生成元素
squares_gen = (x ** 2 for x in range(10))
print(type(squares_gen)) # <class 'generator'>

# 迭代生成器
for square in squares_gen:
print(square, end=" ") # 0 1 4 9 16 25 36 49 64 81

生成器 vs 列表

生成器的主要优势是内存效率:

import sys

# 列表:所有元素存储在内存中
numbers_list = [x for x in range(1000000)]
print(f"列表大小:{sys.getsizeof(numbers_list)} 字节")

# 生成器:只存储生成逻辑
numbers_gen = (x for x in range(1000000))
print(f"生成器大小:{sys.getsizeof(numbers_gen)} 字节")

生成器方法

生成器提供了几个方法:

send() 方法

send() 方法可以向生成器发送值,yield 表达式的返回值就是发送的值:

def accumulator():
"""累加器生成器"""
total = 0
while True:
value = yield total # yield 返回当前 total,接收 send 的值
if value is not None:
total += value

acc = accumulator()
print(next(acc)) # 0 - 初始化
print(acc.send(10)) # 10
print(acc.send(5)) # 15
print(acc.send(3)) # 18

throw() 方法

throw() 方法可以在生成器内部抛出异常:

def error_handler():
try:
while True:
value = yield
print(f"接收:{value}")
except ValueError:
print("捕获到 ValueError")
finally:
print("生成器关闭")

gen = error_handler()
next(gen) # 启动生成器
gen.send(1) # 接收:1
gen.throw(ValueError) # 捕获到 ValueError,生成器关闭

close() 方法

close() 方法关闭生成器:

def infinite_counter():
n = 0
while True:
yield n
n += 1

counter = infinite_counter()
print(next(counter)) # 0
print(next(counter)) # 1
counter.close() # 关闭生成器
# next(counter) # StopIteration

yield from 语法

yield from 用于从一个生成器委托到另一个生成器:

def sub_gen():
yield 1
yield 2
yield 3

def main_gen():
yield "开始"
yield from sub_gen() # 委托给 sub_gen
yield "结束"

for value in main_gen():
print(value)
# 输出:开始 1 2 3 结束

生成器应用场景

1. 处理大文件

def read_large_file(file_path):
"""逐行读取大文件"""
with open(file_path, 'r', encoding='utf-8') as f:
for line in f:
yield line.strip()

# 使用
for line in read_large_file("large_file.txt"):
process(line) # 每次只处理一行,内存友好

2. 无限序列

def fibonacci():
"""无限斐波那契数列"""
a, b = 0, 1
while True:
yield a
a, b = b, a + b

# 获取前 10 个斐波那契数
fib = fibonacci()
first_10 = [next(fib) for _ in range(10)]
print(first_10) # [0, 1, 1, 2, 3, 5, 8, 13, 21, 34]

3. 数据管道

def read_lines(filename):
"""读取文件行"""
with open(filename) as f:
for line in f:
yield line

def filter_comments(lines):
"""过滤注释行"""
for line in lines:
if not line.strip().startswith('#'):
yield line

def strip_lines(lines):
"""去除空白"""
for line in lines:
yield line.strip()

# 使用管道
pipeline = strip_lines(filter_comments(read_lines("config.txt")))
for line in pipeline:
print(line)

functools 模块

functools 模块提供了许多有用的高阶函数,用于操作或返回其他函数。

@functools.wraps

wraps 装饰器用于保留被装饰函数的元信息:

from functools import wraps

def decorator(func):
@wraps(func) # 复制原函数的 __name__, __doc__ 等属性
def wrapper(*args, **kwargs):
return func(*args, **kwargs)
return wrapper

@decorator
def example():
"""这是示例函数"""
pass

print(example.__name__) # example(而非 wrapper)
print(example.__doc__) # 这是示例函数

@functools.lru_cache

lru_cache 装饰器实现了 LRU(最近最少使用)缓存,可以缓存函数结果:

from functools import lru_cache

@lru_cache(maxsize=128) # 最多缓存 128 个结果
def fibonacci(n):
"""计算斐波那契数(带缓存)"""
if n <= 1:
return n
return fibonacci(n - 1) + fibonacci(n - 2)

# 第一次计算
print(fibonacci(100)) # 354224848179261915075

# 查看缓存信息
print(fibonacci.cache_info())
# CacheInfo(hits=98, misses=101, maxsize=128, currsize=101)

# 清除缓存
fibonacci.cache_clear()

参数说明

  • maxsize:缓存的最大条目数,设为 None 则无限制
  • typed:设为 True 时,不同类型的参数分别缓存(如 33.0
@lru_cache(maxsize=None, typed=True)
def expensive_function(x):
print(f"计算 {x}")
return x * x

print(expensive_function(3)) # 计算 3 → 9
print(expensive_function(3.0)) # 计算 3.0 → 9.0(typed=True 时分开缓存)
print(expensive_function(3)) # 9(从缓存获取)

@functools.cache

cachelru_cache(maxsize=None) 的简化版本,无限制缓存:

from functools import cache

@cache
def factorial(n):
"""计算阶乘(带缓存)"""
return n * factorial(n - 1) if n else 1

print(factorial(10)) # 3628800
print(factorial(5)) # 120(从缓存获取)

functools.partial

partial 用于固定函数的部分参数,创建新函数:

from functools import partial

# 原始函数
def power(base, exp):
return base ** exp

# 创建新函数:固定 base=2
square = partial(power, exp=2)
print(square(5)) # 25

# 创建新函数:固定 base=10
power_of_10 = partial(power, 10)
print(power_of_10(2)) # 100
print(power_of_10(3)) # 1000

# 实际应用:绑定函数参数
from functools import partial
int_base_2 = partial(int, base=2)
print(int_base_2('1010')) # 10
print(int_base_2('1111')) # 15

functools.partialmethod

partialmethod 用于类方法的部分绑定:

from functools import partialmethod

class Cell:
def __init__(self):
self._alive = False

def set_state(self, state):
self._alive = state

# 使用 partialmethod 创建便捷方法
set_alive = partialmethod(set_state, True)
set_dead = partialmethod(set_state, False)

cell = Cell()
cell.set_alive()
print(cell._alive) # True
cell.set_dead()
print(cell._alive) # False

@functools.cached_property

cached_property 将方法转换为缓存属性,只计算一次:

from functools import cached_property

class DataSet:
def __init__(self, numbers):
self._data = numbers

@cached_property
def stdev(self):
"""计算标准差,只计算一次"""
print("计算标准差...")
n = len(self._data)
mean = sum(self._data) / n
variance = sum((x - mean) ** 2 for x in self._data) / n
return variance ** 0.5

data = DataSet([1, 2, 3, 4, 5])
print(data.stdev) # 计算标准差... → 1.414...
print(data.stdev) # 1.414...(从缓存获取,不再计算)

functools.reduce

reduce 对可迭代对象的元素进行累积计算:

from functools import reduce

# 计算列表元素的乘积
numbers = [1, 2, 3, 4, 5]
product = reduce(lambda x, y: x * y, numbers)
print(product) # 120

# 带初始值
total = reduce(lambda x, y: x + y, numbers, 100)
print(total) # 115(100 + 1 + 2 + 3 + 4 + 5)

# 找最大值
maximum = reduce(lambda x, y: x if x > y else y, numbers)
print(maximum) # 5

# 将列表转换为嵌套字典
pairs = [('a', 1), ('b', 2), ('c', 3)]
result = reduce(lambda d, p: {**d, p[0]: p[1]}, pairs, {})
print(result) # {'a': 1, 'b': 2, 'c': 3}

@functools.singledispatch

singledispatch 实现单分派泛型函数,根据第一个参数的类型选择实现:

from functools import singledispatch

@singledispatch
def process(value):
"""默认实现"""
return f"处理通用类型:{type(value).__name__}"

@process.register
def _(value: int):
"""整数处理"""
return f"处理整数:{value}"

@process.register
def _(value: str):
"""字符串处理"""
return f"处理字符串:{value!r}"

@process.register(list)
def _(value):
"""列表处理"""
return f"处理列表,长度:{len(value)}"

print(process(42)) # 处理整数:42
print(process("hello")) # 处理字符串:'hello'
print(process([1, 2, 3])) # 处理列表,长度:3
print(process(3.14)) # 处理通用类型:float

递归函数

递归函数是调用自身的函数。每个递归函数都需要一个终止条件(基准情况)来避免无限递归。

基本示例

# 阶乘
def factorial(n):
"""计算 n 的阶乘"""
if n <= 1: # 终止条件
return 1
return n * factorial(n - 1) # 递归调用

print(factorial(5)) # 120 = 5 * 4 * 3 * 2 * 1

# 斐波那契数列(效率较低,适合演示)
def fibonacci(n):
"""计算第 n 个斐波那契数"""
if n <= 1:
return n
return fibonacci(n - 1) + fibonacci(n - 2)

print(fibonacci(10)) # 55

递归的优化

使用 lru_cache 可以显著提高递归效率:

from functools import lru_cache

@lru_cache(maxsize=None)
def fibonacci(n):
"""带缓存的斐波那契数列"""
if n <= 1:
return n
return fibonacci(n - 1) + fibonacci(n - 2)

print(fibonacci(100)) # 354224848179261915075

递归深度限制

Python 有递归深度限制(默认约 1000),超过会抛出 RecursionError

import sys
print(sys.getrecursionlimit()) # 1000

# 可以修改限制(不推荐)
# sys.setrecursionlimit(2000)

尾递归优化

Python 不支持尾递归优化,但可以手动转换为循环:

# 尾递归形式(Python 不会优化)
def factorial_tail(n, acc=1):
if n <= 1:
return acc
return factorial_tail(n - 1, n * acc)

# 转换为循环
def factorial_iterative(n):
acc = 1
for i in range(1, n + 1):
acc *= i
return acc

函数注解(Type Hints)

函数注解用于添加类型提示,帮助 IDE 和类型检查器进行静态分析。

基本语法

def func(a: int, b: str) -> str:
"""带有类型注解的函数"""
return f"{a} - {b}"

print(func(1, "hello")) # 1 - hello

类型注解详解

from typing import Optional, List, Dict, Union, Callable, Any

# 可选参数
def greet(name: str, greeting: Optional[str] = None) -> str:
if greeting is None:
greeting = "你好"
return f"{greeting}{name}!"

# 列表和字典
def process_items(items: List[int]) -> Dict[str, int]:
return {"count": len(items), "sum": sum(items)}

# 联合类型
def process(value: Union[int, str]) -> str:
return str(value)

# 函数类型
def apply(func: Callable[[int], int], value: int) -> int:
return func(value)

# 任意类型
def log(value: Any) -> None:
print(value)

Python 3.10+ 新语法

# 使用 | 代替 Union
def process(value: int | str) -> str:
return str(value)

# 使用 | 代替 Optional
def greet(name: str, greeting: str | None = None) -> str:
if greeting is None:
greeting = "你好"
return f"{greeting}{name}!"

注解不会在运行时强制执行,仅作为类型提示使用。

内置函数

Python 提供了许多内置函数:

数值相关

abs(-5)           # 5 - 绝对值
max([1, 2, 3]) # 3 - 最大值
min([1, 2, 3]) # 1 - 最小值
pow(2, 3) # 8 - 幂运算
round(3.7) # 4 - 四舍五入
sum([1, 2, 3]) # 6 - 求和
divmod(10, 3) # (3, 1) - 商和余数

类型转换

int("10")         # 10
float("3.14") # 3.14
str(123) # "123"
bool(1) # True
list("abc") # ['a', 'b', 'c']
tuple("abc") # ('a', 'b', 'c')
set("abc") # {'a', 'b', 'c'}
dict(a=1, b=2) # {'a': 1, 'b': 2}

序列相关

len("hello")      # 5 - 长度
range(5) # range(0, 5)
enumerate(['a', 'b', 'c']) # 枚举
zip([1, 2], ['a', 'b']) # [(1, 'a'), (2, 'b')]
sorted([3, 1, 2]) # [1, 2, 3]
reversed([1, 2, 3]) # [3, 2, 1]

其他常用

type(123)         # <class 'int'> - 类型
isinstance(123, int) # True - 类型检查
callable(print) # True - 是否可调用
hasattr(str, 'upper') # True - 是否有属性
id(123) # 对象的唯一标识符
hash("hello") # 哈希值

小结

本章我们学习了:

  1. 函数定义:基本语法和函数的本质
  2. 函数参数:位置参数、默认参数、关键字参数、不定长参数、限定参数
  3. 函数返回值:return 语句、多返回值、提前返回
  4. 变量作用域:局部变量、全局变量、LEGB 规则
  5. 匿名函数:lambda 语法和应用场景
  6. 闭包:概念、条件、应用场景
  7. 装饰器:原理、带参数装饰器、类装饰器、常见应用
  8. 生成器:yield 语法、生成器表达式、生成器方法
  9. functools 模块:wraps、lru_cache、partial、reduce 等
  10. 递归函数:基本概念、优化、深度限制
  11. 函数注解:类型提示语法

练习

  1. 编写一个装饰器,统计函数的调用次数和平均执行时间
  2. 实现一个生成器,按行读取大文件并过滤空行
  3. 使用闭包实现一个简单的计数器工厂
  4. 使用 lru_cache 优化递归计算组合数 C(n, k)
  5. 编写一个 @retry 装饰器,支持指定重试次数和异常类型

延伸阅读