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
闭包的三个条件
形成闭包需要满足三个条件:
- 存在嵌套函数(内部函数)
- 内部函数引用了外部函数的变量
- 外部函数返回内部函数
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时,不同类型的参数分别缓存(如3和3.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
cache 是 lru_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") # 哈希值
小结
本章我们学习了:
- 函数定义:基本语法和函数的本质
- 函数参数:位置参数、默认参数、关键字参数、不定长参数、限定参数
- 函数返回值:return 语句、多返回值、提前返回
- 变量作用域:局部变量、全局变量、LEGB 规则
- 匿名函数:lambda 语法和应用场景
- 闭包:概念、条件、应用场景
- 装饰器:原理、带参数装饰器、类装饰器、常见应用
- 生成器:yield 语法、生成器表达式、生成器方法
- functools 模块:wraps、lru_cache、partial、reduce 等
- 递归函数:基本概念、优化、深度限制
- 函数注解:类型提示语法
练习
- 编写一个装饰器,统计函数的调用次数和平均执行时间
- 实现一个生成器,按行读取大文件并过滤空行
- 使用闭包实现一个简单的计数器工厂
- 使用
lru_cache优化递归计算组合数 C(n, k) - 编写一个
@retry装饰器,支持指定重试次数和异常类型