Skip to content
返回

Python 函数式编程

AI 总结

本文全面介绍了Python函数式编程的核心概念和实用技巧,包括函数的定义、调用、参数传递、递归、生成器、迭代器、lambda表达式、高阶函数、装饰器等。通过丰富的代码示例,展示了如何利用这些特性实现高效、可读性强的函数式编程风格。

目录

函数介绍

函数是一段可以重复使用的代码,只需要定义一次,之后就可以用不同的参数进行调用。

# 求绝对值
print(abs(-10))    # 输出:10

# 求最大值
print(max(3, 7, 2, 5))  # 输出:7

# 类型转换
print(int("123"))    # 字符串转整数,输出:123
print(float("3.14")) # 字符串转浮点数,输出:3.14
print(str(456))      # 数字转字符串,输出:'456'
print(bool(0))       # 0转为布尔值,输出:False
print(bool("hi"))    # 非空字符串转为布尔值,输出:True

# abs函数只能传一个参数
# print(abs(1, 2))  # TypeError: abs() takes exactly one argument (2 given)

# abs函数不能传字符串
# print(abs("abc")) # TypeError: bad operand type for abs(): 'str'

# 给abs函数起个新名字
my_abs = abs
print(my_abs(-20))  # 输出:20

# 自定义函数
def area(r: float) -> float:
    """计算圆面积。r 为半径(米)。"""
    from math import pi
    return pi * r * r  # 用return返回结果

print(area.__doc__)   # -> 计算圆面积。r 为半径(米)。
help(area)            # 会显示这段说明

# 调用自定义函数
print(area(5))   # 输出:78.539815
print(area(10))  # 输出:314.15926

# 把整数转换为十六进制字符串
n1 = 255
n2 = 1000

print(hex(n1))  # 输出:0xff
print(hex(n2))  # 输出:0x3e8

定义函数

# 定义一个计算绝对值的函数
def my_abs(num):
    # 如果num大于等于0,直接返回num
    if num >= 0:
        return num
    # 否则返回-num
    else:
        return -num

# 调用函数,传入-8
print(my_abs(-8))  # 输出:8

# 多个返回值
def split_pair(s):
    return s[:len(s)//2], s[len(s)//2:]

left, right = split_pair("abcd")  # 左右解包

# 定义一个什么都不做的函数
def do_nothing():
    pass  # pass表示占位,什么也不做 这样代码不会报错
    
# 定义一个只接受数字参数的绝对值函数
def safe_abs(x):
    # 检查x是不是整数或浮点数
    if not isinstance(x, (int, float)):
        raise TypeError("参数类型必须是数字")
    if x >= 0:
        return x
    else:
        return -x

safe_abs("abc")  # 这一行会报TypeError: 参数类型必须是数字

参数

# 位置参数(positional-only)
def calculate_area(length, width):
    """
    计算矩形面积
    length: 长度
    width: 宽度
    """
    return length * width  # 返回长度乘以宽度的结果

# 调用函数
print(calculate_area(5, 3))  # 输出:15

# / 分隔符
def f(a, b, /, c, d):
    print(a, b, c, d)

f(1, 2, 3, 4)          # ✅ 都用位置传
f(1, 2, c=3, d=4)      # ✅ c、d 可关键字传
f(a=1, b=2, c=3, d=4)  # ❌ TypeError:a、b 是仅限位置

# 命名关键字参数(keyword-only parameters)

# 调用时必须用“参数名=值”的形式传入,不能当位置参数传。
def f(x, *, a, b=0):
    print(x, a, b)

f(10, a=1, b=2)   # ✅
f(10, 1, 2)       # ❌ TypeError:a/b 必须用关键字传

# 可以和 `*args` 一起用
def g(*args, a, b=0):
    print(args, a, b)

g(1, 2, a=3)      # args=(1,2), a=3, b=0

# 可变位置参数(var-positional) → `*args`

def sum_all(*nums):
    return sum(nums)

sum_all(1, 2, 3)            # 6
sum_all()                   # 0(空元组)

# 位置不确定 → 用 `*args`
# 关键字不确定 → 用 `**kwargs`
# 既有固定参数、又想允许“更多” → 组合使用
# 还想限制/命名某些关键字 → 配合命名关键字参数

# 综合使用示例
def api_call(endpoint, /, *path_params, method="GET", timeout=5, **query):
    """
    endpoint      位置仅限参数(/ 前)
    *path_params  不定数量的位置片段
    method/timeout  命名关键字参数(必须用名字传)
    **query       任意数量的关键字参数(作为查询串)
    """
    print(endpoint, path_params, method, timeout, query)

api_call("/v1/user", 25, method="POST", timeout=3, sort="desc", limit=10)
# 输出:
# /v1/user (25,) POST 3 {'sort': 'desc', 'limit': 10}

注意点

递归函数

递归函数就是“函数自己调用自己”,必须包含两个关键部分:

# 阶乘
def fact(n):
    # 终止条件 / 边界情形
    if n <= 1:
        return 1
    # 拆解规则 / 递归展开(规模收敛:n -> n-1)
    return n * fact(n - 1)

fact(5)

# 二分查找
def binary_search(arr, target, left, right):
    # 基本情况:如果左边界大于右边界,说明没找到
    if left > right:
        return -1
    # 计算中间位置
    # `//` 是整除(向下取整的除法,floor division)运算符
    mid = (left + right) // 2
    # 如果找到目标值,返回位置
    if arr[mid] == target:
        return mid
    # 如果中间值大于目标值,在左半部分查找
    if arr[mid] > target:
        return binary_search(arr, target, left, mid - 1)
    # 如果中间值小于目标值,在右半部分查找
    return binary_search(arr, target, mid + 1, right)

# 测试函数
numbers = [1, 3, 5, 7, 9, 11, 13, 15]
target = 7
result = binary_search(numbers, target, 0, len(numbers) - 1)
print(f"数字{target}在列表中的位置是:{result}")

生成器

一种可迭代、惰性产出数据的对象。它一次只产出一个值,不把所有结果一次性放进内存,特别适合大数据/流式处理

生成器对象实现了迭代器协议,这就是能被 for 遍历的原因。

优点有:

生成器表达式的语法与列表生成式非常相似

生成器函数(yield

def countdown(n):
    while n > 0:
        yield n        # 产出一个值并“暂停”
        n -= 1         # 下次迭代从这里继续

for x in countdown(3):  # 3, 2, 1
    print(x)

# 把迭代“委托”出去
def flatten(nested):
    for row in nested:
        yield from row   # 等价于:for x in row: yield x
        
# 把递归改为生成器
def inorder(root):
    if root:
        yield from inorder(root.left)
        yield root.val
        yield from inorder(root.right)

生成器表达式

gen = (x*x for x in range(5))  # 惰性
list(gen)  # [0, 1, 4, 9, 16]

# 典型用法:和 `sum`、`any`、`all` 等“消费一次就完”的函数搭配:
total = sum(x*x for x in range(10_000_000))

# 管道示例
def read_lines(fp):
    for line in fp:
        yield line.rstrip("\n")

def filter_nonempty(lines):
    for s in lines:
        if s.strip():
            yield s

def to_ints(lines):
    for s in lines:
        yield int(s)

with open("nums.txt") as f:
    for n in to_ints(filter_nonempty(read_lines(f))):
        print(n)

双向通信 & 控制

生成器不仅能“产出”(yield),还可以接收值或异常(协程原型):

日常开发里更常见的是“只读”的生成器;send/throw/close 多用于高级协程或框架内部。

# 双向通信 & 控制
def accumulator():
    total = 0
    while True:
        x = yield total    # 接收 send() 过来的值
        if x is None:      # None 表示“只想读总计”
            continue
        total += x

gen = accumulator()
next(gen)            # 预激活 → 得到初始 total=0
gen.send(10)         # -> 10
gen.send(5)          # -> 15
gen.close()          # 结束
-- 2025 年 9 月 25 日止 --

迭代器

生成器就是“迭代器工厂”,带 yield 的生成器函数返回的对象天然是迭代器。

  1. 迭代器维护迭代状态、一次产出一个元素的对象;实现了 __iter__()(返回自身)__next__()(取下一个;无则抛 StopIteration
  2. 可迭代对象不一定是迭代器,但可以通过 iter() 函数转换为迭代器。迭代器总是可迭代的。
# 可以使用 isinstance () 函数来检查一个对象是否为可迭代对象或迭代器。
# Iterable(可迭代对象):表示可以被迭代的对象。
# Iterator(迭代器):表示一个数据流,可以通过 next () 函数逐个获取元素。
from collections.abc import Iterable, Iterator

# 创建一个列表(可迭代对象)
fruits = ['苹果', '香蕉', '橙子']
# 检查是否为可迭代对象
print(isinstance(fruits, Iterable))  # 输出:True
# 迭代对象不是迭代器
print(isinstance(fruits, Iterator))  # 输出:False

# 将可迭代对象转换为迭代器
fruit_iterator = iter(fruits)
# 检查是否为迭代器
print(isinstance(fruit_iterator, Iterable))  # 输出:True
# 迭代器是可迭代对象
print(isinstance(fruit_iterator, Iterator))  # 输出:True

# for x in obj 的幕后
it = iter(obj)          # 调 obj.__iter__() 得到迭代器
while True:
    try:
        x = next(it)    # 调 it.__next__()
    except StopIteration:
        break
    # 使用 x
    
# 把可迭代对象变成迭代器
it = iter([10, 20, 30])
next(it)  # 10
next(it)  # 20
next(it)  # 30
next(it)  # StopIteration

# 自定义迭代器
class CountDown:
    def __init__(self, n): self.n = n
    def __iter__(self):     return self            # 迭代器返回自身
    def __next__(self):
        if self.n <= 0:     raise StopIteration    # 告诉外界“没有更多元素了”
        self.n -= 1
        return self.n + 1

for x in CountDown(3):      # 3, 2, 1
    print(x)

# 迭代器工厂
def countdown(n):
    while n > 0:
        yield n
        n -= 1
        
# `raise` 关键词,抛出异常用的语句。抛出后,当前流程会立刻中断,转去寻找匹配的 `except` 处理;若没人接住,程序就报错结束。
raise ValueError("bad input")           # 抛出内置异常
raise MyError("oops")                   # 抛出自定义异常
raise RuntimeError from exc             # 异常链(把原因exc附带上)

# `StopIteration` 异常类,表示迭代已经结束,这是迭代器协议里规定的“终止信号”。
# `for` 循环、`list(iterable)` 等迭代消费者看见这个异常就会正常停止,不会把它当错误显示出来。
# 在生成器函数(用 `yield` 的函数)内部,不要手动 `raise StopIteration` 来结束 
# 正确做法是自然走到函数末尾或 `return 值`(`return` 的值会成为 `StopIteration.value`)
# Python 3.7 起 `StopIteration` 从生成器冒出,会被转成 `RuntimeError`(PEP 479)
def gen_ok():
    yield 1
    return 42                 # 推荐:结束并带“返回值”

def gen_bad():
    yield 1
    raise StopIteration(42)   # 会被改写成 RuntimeError("generator raised StopIteration")

生成器函数 vs 生成器 vs 迭代器

关系链:生成器对象 ⊂ 迭代器 ⊂ 可迭代对象

# 定义一个生成器函数
def my_generator():
    yield 1
    yield 2
    yield 3

# 创建生成器对象
gen = my_generator()

# 使用next()获取数据
print(next(gen))  # 输出:1
print(next(gen))  # 输出:2
print(next(gen))  # 输出:3

# 使用for循环遍历生成器
for num in my_generator():
    print(num)  # 输出:1, 2, 3

lambda

lambda 表达式就是用一行写出的匿名函数

lambda 参数列表: 表达式

# 用lambda表达式实现两个数相加
add = lambda x, y: x + y  # 定义一个匿名函数,接收x和y,返回它们的和
print(add(3, 5))  # 输出: 8

# 作为回调 / key 函数
nums = [3, -10, 5]
sorted(nums, key=lambda x: abs(x))     # [3, 5, -10]

# 等价写法
def keyfunc(x): 
    return abs(x)
sorted(nums, key=keyfunc)

# map / filter 的轻量函数
list(map(lambda x: x*x, [1, 2, 3]))      # [1, 4, 9]
list(filter(lambda s: s.isalpha(), ["a", "3", "bc"]))  # ['a', 'bc']

# 条件表达式
f = lambda x: "even" if x % 2 == 0 else "odd"
f(3)  # 'odd'

# 只能是表达式,不能包含语句;但可以用条件表达式、逻辑运算、以及海象运算符 `:=` 等表达式技巧:
lam = lambda s: (n := len(s)) * (n > 3)   # 长度>3则返回长度,否则返回0
list(map(lam, ["a", "bb", "ccc", "dddd"]))

# 默认参数
num = 5  # 定义一个变量num,赋值为5
func = lambda i=num: i * i  # 参数i默认值为5,函数返回i的平方
print(func())  # 由于没有传参,使用默认值5,结果为25

函数式编程

# 纯函数
# 纯函数没有副作用,副作用是指函数除了返回值之外,还对外部环境产生了影响。
def square(x): return x * x

# map / filter / reduce(functools.reduce 需导入)
from functools import reduce
nums = [1, 2, 3, 4]
squares = list(map(square, nums))          # [1, 4, 9, 16]
evens   = list(filter(lambda x: x % 2 == 0, nums))  # [2, 4]
total   = reduce(lambda a, b: a + b, nums)          # 10

# 列表/字典推导(更 Pythonic——符合 Python 风格的 的 map/filter)
squares2 = [x*x for x in nums]
evens2   = [x for x in nums if x % 2 == 0]

# 偏函数 / 组合
from functools import partial
import operator
add10 = partial(operator.add, 10)  # add10(x) == 10 + x

# 不可变类型
point = (3, 4)            # tuple 不可变
tags  = frozenset({"a", "b"})

# 高阶函数
def process_numbers(numbers, filter_func, map_func):
    """
    处理数字列表
    :param numbers: 数字列表
    :param filter_func: 过滤函数
    :param map_func: 映射函数
    :return: 处理后的列表
    """
    # 先过滤,再映射
    filtered = [n for n in numbers if filter_func(n)]  # 使用过滤函数
    result = [map_func(n) for n in filtered]  # 使用映射函数
    return result

# 定义过滤和映射函数
def is_even(n):  # 判断是否为偶数
    return n % 2 == 0

def double(n):  # 将数字翻倍
    return n * 2

# 使用高阶函数处理数据
numbers = [1, 2, 3, 4, 5, 6, 7, 8, 9, 10]
result = process_numbers(numbers, is_even, double)
print(f"处理结果: {result}")  # 输出: [4, 8, 12, 16, 20]

# 应用示例
# 这段代码用到了函数式编程的元素(一等函数 + 高阶函数 + 闭包),但它不是“纯粹的函数式编程”。原因是里面有可变状态(`count += 1`),属于副作用,因此 `counter()` 不是纯函数(相同输入并不保证相同输出)。
def create_counter():
    """
    创建一个计数器函数
    :return: 计数器函数
    """
    count = 0  # 计数器初始值
    
    def counter():  # 内部函数
        # nonlocal 关键字用于声明使用外部变量
        nonlocal count  # 声明使用外部变量
        count += 1  # 计数器加1
        return count  # 返回当前计数
    
    return counter  # 返回计数器函数

# 测试代码
counter1 = create_counter()  # 创建第一个计数器
counter2 = create_counter()  # 创建第二个计数器

print("第一个计数器:")
print(counter1())  # 输出: 1
print(counter1())  # 输出: 2
print(counter1())  # 输出: 3

print("\n第二个计数器:")
print(counter2())  # 输出: 1
print(counter2())  # 输出: 2

# 闭包 返回函数
def count(n):
    fs = []
    for i in range(1, 4):
        fs.append(lambda i = i: i * i)
    return fs

fs = count(3)
print(fs)
print(fs[0]())
print(fs[1]())
print(fs[2]())

map(function, iterable, …)

把 function 依次作用到可迭代里的每个元素,返回一个惰性迭代器

# 基本用法
list(map(abs, [-1, 2, -3]))          # [1, 2, 3]
list(map(str.upper, ["a", "b"]))     # ['A', 'B']

# 多个可迭代会“并行”取值,长度以最短的为准
a, b = [1, 2, 3], [10, 20]
list(map(lambda x, y: x + y, a, b))  # [11, 22]

# 等价写法
[x*x for x in range(5)]                 # 列表推导式
(x*x for x in range(5))                 # 生成器表达式(同样惰性)
[x+y for x, y in zip(a, b)]             # 多输入时配 zip

reduce(function, iterable, initializer)

reduce 函数需要从 functools 模块导入

from functools import reduce
from operator import add, mul

# 求和 / 求积(其实直接用内置更好)
reduce(add, [1, 2, 3, 4], 0)     # 10
reduce(mul, [1, 2, 3, 4], 1)     # 24

# 把字符串拼起来
reduce(add, ["py", "thon"], "")  # 'python'
# 更推荐 ''.join(...)
"".join(["py", "thon"])

# 高级用法
# 1) 函数组合(把多个函数合成一个)
from functools import reduce
def compose(*funcs):
    return lambda x: reduce(lambda v, f: f(v), reversed(funcs), x)

f = compose(str, abs)   # 等价于 lambda x: str(abs(x))
f(-3)  # '3'

# 2) 自定义聚合:累加到字典/集合(也可用循环/Counter)
def merge_counts(acc, x):
    acc[x] = acc.get(x, 0) + 1
    return acc
reduce(merge_counts, "banana", {})     # {'b':1,'a':3,'n':2}

# 3) 扁平化
lists = [[1,2],[3,4],[5]]
# 不推荐:
reduce(add, lists, [])     # list 拼接会 O(n^2)
# 更推荐 itertools.chain
from itertools import chain
list(chain.from_iterable(lists))     # 更高效

filter(func, iterable)

把 iterable 里的元素按条件函数 func 的真假过滤,返回一个惰性迭代器(Python 3)。

特性与注意点

# 只保留偶数
list(filter(lambda x: x % 2 == 0, range(10)))   # [0, 2, 4, 6, 8]

# 去掉空白行
lines = ["a", " ", "", "b"]
list(filter(str.strip, lines))                  # ['a', 'b']

# 过滤掉“假值”
xs = [0, 1, "", "hi", None, [], [1]]
list(filter(None, xs))                          # [1, 'hi', [1]]

# 等价于推导式
[x for x in range(10) if x % 2 == 0]
(s for s in lines if s.strip())      # 惰性:生成器表达式

# 例子:筛选出所有能被3整除的数
nums = list(range(1, 21))
# 用lambda表达式直接写判断逻辑
divisible_by_3 = filter(lambda x: x % 3 == 0, nums)
print(list(divisible_by_3))  # 输出: [3, 6, 9, 12, 15, 18]

sorted

list.sort 的区别

# 语法
sorted(iterable, /, *, key=None, reverse=False) -> list
# list.sort 就地改动,返回 None
list.sort(self, /, *, key=None, reverse=False)

names = ["Tom", "alice", "BOB"]
sorted(names)                     # ['BOB','Tom','alice'](默认大小写先大写)
sorted(names, key=str.lower)      # ['alice','BOB','Tom'](忽略大小写)
sorted(names, key=str.casefold)   # 更稳的大小写折叠

# 多字段排序
from operator import attrgetter

records = [
    {"name":"A", "age":30, "score":90},
    {"name":"B", "age":30, "score":85},
    {"name":"C", "age":25, "score":95},
]
# 先按 age 升序,再按 score 降序
sorted(records, key=lambda r: (r["age"], -r["score"]))

# 对对象:
class Emp:
    def __init__(self, name, dept, age):
        self.name = name
        self.dept = dept
        self.age = age

objs = [
    Emp("Alice", "Sales", 30),
    Emp("Bob",   "Sales", 25),
    Emp("Caro",  "Tech",  28),
]

# 先按 dept 升序,再按 age 升序(稳定排序)
out = sorted(objs, key=attrgetter("dept", "age"))
[(o.name, o.dept, o.age) for o in out]
# → [('Alice','Sales',30), ('Bob','Sales',25), ('Caro','Tech',28)]  # 注意稳定性

# 字典相关的排序
d = {"x": 5, "y": 2, "z": 9}
sorted(d)                          # ['x','y','z']  —— 按键
sorted(d.items(), key=lambda kv: kv[1], reverse=True)  # 按值
# 若想得到“按值排序后”的字典(Py3.7+字典保持插入顺序):
dict(sorted(d.items(), key=lambda kv: kv[1]))

# 处理 None/缺失值
rows = [{"age": 30}, {"age": None}, {"age": 20}]
# 让 None 排在最后:
sorted(rows, key=lambda r: (r["age"] is None, r["age"]))

# 小抄
sorted(xs)                              # 基本
sorted(xs, reverse=True)                # 降序
sorted(xs, key=len)                     # 按长度
sorted(xs, key=lambda s: (len(s), s))   # 多键 先按长度,再按字母序
sorted(d.items(), key=lambda kv: kv[1]) # 字典按值
-- 2025 年 9 月 26 日止 --

partial & partialmethod

partial 预先固定函数的部分位置/关键字参数,返回一个新函数,后续只需提供剩余参数。

partialmethod 和 partial 的区别

# 语法 partial(func, /, *args, **kwargs)
# 返回 newfunc,调用时执行 func(*args, *new_args, **kwargs, **new_kwargs)。
from functools import partial

def power(base, exp):
    return base ** exp

square = partial(power, exp=2)   # 先把 exp 固定为 2
square(9)                        # 81  —— 等价 power(9, exp=2)

# partialmethod
# partialmethod(func, /, *args, **kwargs) 是一个描述符(descriptor),专门用于在类里定义“预先固定了一部分参数”的方法。
# 调用效果等价:func(self, *pre_args, *call_args, **pre_kwargs, **call_kwargs)
from functools import partialmethod

class Greeter:
    def speak(self, who, punct="!"):
        return f"Hello, {who}{punct}"

    hi = partialmethod(speak, punct=" :)")  # 预绑定 punct
    
# 对比差异
class A:
    def add(self, x, y):
        return x + y

    add5_wrong = partial(add, 5)         # ❌ 不会绑定 self
    add5       = partialmethod(add, 5)   # ✅ 正确:先把 self 绑定,再把 5 拼上

a = A()
a.add5_wrong(10)     # TypeError: missing 'self'
a.add5(10)           # 15

装饰器

装饰器(decorator)是一种在不改动函数/方法/类源码与调用方式的前提下,给它“加功能”的写法。技术上,装饰器就是接收一个可调用对象并返回一个可调用对象的高阶函数(或类)。

# 定义一个简单的装饰器,在函数执行前打印日志
def log(func):
    def wrapper(*args, **kw):
        print('开始执行函数:', func.__name__)  # 打印函数名
        result = func(*args, **kw)  # 调用原函数,* 和 ** 在这里起到解包作用
        print('函数执行完毕')  # 打印结束信息
        return result  # 返回原函数的结果
    return wrapper  # 返回包装后的函数

# 使用装饰器
@log
def hello(name, age,**kw):
    print(name, age, kw)

hello('张三', 20,city='上海', job='工程师')  
# 开始执行函数: hello
# 张三 20 {'city': '上海', 'job': '工程师'}
# 函数执行完毕

# 函数执行计时
# 定义一个装饰器,计算函数执行时间
import time
def timer(func):
    def wrapper(*args, **kw):
        start = time.time()  # 记录开始时间
        result = func(*args, **kw)  # 调用原函数
        end = time.time()  # 记录结束时间
        print(f'函数 {func.__name__} 执行了 {end - start:.2f} 秒')  # 打印执行时间
        return result  # 返回原函数的结果
    return wrapper  # 返回包装后的函数

# 使用装饰器
@timer
def slow_function():
    time.sleep(1)  # 模拟耗时操作
    print('函数执行完毕')

slow_function()  
#函数执行完毕
#函数 slow_function 执行了 1.02 秒

import asyncio, time
from functools import wraps

# 异步装饰器
def atime(fn):
    @wraps(fn)
    async def w(*a, **kw):
        t = time.perf_counter()
        try:
            return await fn(*a, **kw)
        finally:
            print("cost:", time.perf_counter()-t)
    return w

@atime
async def fetch():
    await asyncio.sleep(1) # 模拟耗时操作
    print('函数执行完毕')

@A
@B
def f(): ...
# 等价于 f = A(B(f))

保留原函数元数据

使用 functools.wraps 不加 wraps 会让 __name____doc__ 等看起来像 wrapper

from functools import wraps

def greet(fn):
    @wraps(fn)                     # 关键:保留签名/文档/名称
    def wrapper(*args, **kwargs):
        print(">>> before")
        return fn(*args, **kwargs)
    return wrapper

带参数的装饰器

from functools import wraps

def deco(n):                      # 外层:装饰器工厂(接参数)
    print("factory called with", n)
    def decorator(func):          # 内层:真正装饰器(接函数)
        print("decorating", func.__name__)
        @wraps(func)
        def wrapper(*a, **kw):
            print("n is", n)      # 用到外层参数(闭包捕获)
            return func(*a, **kw)
        return wrapper
    return decorator

@deco(5)
def hello():
    print("hello")

# 装饰器工厂:带重试次数参数
def retry(times=3):
    def deco(fn):
        @wraps(fn)
        def wrapper(*a, **kw):
            last = None
            for attempt in range(1, times + 1):
                try:
                    return fn(*a, **kw)
                except Exception as e:
                    last = e
                    # 打印每次失败信息,方便调试
                    print(f"[retry {attempt}/{times}] {e}")

            # 所有重试都失败后,抛出最后一次异常
            raise last
        return wrapper
    return deco

@retry(times=5)
def flaky():
    import random
    # 把随机种子固定,确保每次运行结果一致,方便观察重试过程
    # random.seed(1)
    if random.randint(0, 1) == 0:
        raise ValueError("随机失败")
    return "成功"

类装饰器

# 方法装饰器
def log_call(fn):
    @wraps(fn)
    def w(self, *a, **kw):
        print(f"call {fn.__name__} with", a, kw)
        return fn(self, *a, **kw)
    return w

class Svc:
    @log_call
    def run(self, x): return x*2

# 类装饰器
def add_repr(cls):
    cls.__repr__ = lambda self: f"<{cls.__name__} {self.__dict__}>"
    return cls

@add_repr
class Point:
    def __init__(self,x,y): self.x,self.y=x,y
-- 2025 年 9 月 27 日止 --

配套代码:vsme/learn-python



上一篇
Python 面向对象
下一篇
Python 基础知识