第 12 章 高级函数
2026/4/16 18:04:26 网站建设 项目流程

1. 一等对象(First-Class Objects)

(1)概念引入

在 Python 中,函数是一等对象(First-Class Objects)。
所谓“一等对象”,是指在某种编程语言中,某类实体可以像普通数据一样被自由使用,而不受特殊限制。

如果一种语言中的函数能够像变量、字符串或数值一样被对待,那么我们就称该语言支持一等函数(First-Class Functions)特性。
Python、JavaScript 等现代高级语言均具备这一特性。

具体而言,作为一等对象的函数,至少应具备以下能力:

  • 可以赋值给变量
  • 可以作为参数传入另一个函数
  • 可以作为另一个函数的返回值
  • 可以存储在容器类型(如列表、元组、字典)中

这使得函数不仅仅是“可执行的代码块”,而是完整的对象,能够参与更高级的程序抽象。

(2)函数赋值给变量

在 Python 中,函数名本质上是一个指向函数对象的引用。因此,函数可以被直接赋值给变量。

def add(a, b): return a + b # 将函数赋值给变量 calc = add print(calc(2, 3)) # 输出:5

此时,calcadd指向同一个函数对象。
通过变量调用函数,与通过原函数名调用在行为上完全一致。

这一特性为函数别名、策略切换等编程模式提供了基础。

(2)函数作为参数传入另一个函数

函数既然是对象,就可以作为参数传入其他函数。这一点与数学中的复合函数概念高度一致。

def apply(func, x, y): return func(x, y) def multiply(a, b): return a * b result = apply(multiply, 3, 4) print(result) # 输出:12

在该示例中:

  • multiply被作为参数传入apply
  • apply并不关心具体执行什么运算
  • 实际的行为由传入的函数决定

这种设计方式显著提升了代码的通用性与可扩展性。

(4)函数作为返回值

Python 中的函数还可以由另一个函数返回。这一能力是闭包(Closure)与函数工厂(Function Factory)的基础。

def make_power(n): def power(x): return x ** n return power square = make_power(2) cube = make_power(3) print(square(5)) # 输出:25 print(cube(5)) # 输出:125

在上述代码中:

  • make_power根据参数动态生成函数
  • 返回的函数保留了外部变量n
  • 不同返回值对应不同行为的函数实例

这使得函数可以被“按需生成”,极大增强了程序的表达能力。

(5)函数存入容器类型

函数既然是对象,也可以像普通数据一样存储在列表、元组或字典中。

def add(a, b): return a + b def sub(a, b): return a - b operations = { "add": add, "sub": sub } print(operations["add"](10, 5)) # 输出:15 print(operations["sub"](10, 5)) # 输出:5

这种写法在实际项目中非常常见,例如:

  • 命令分发表
  • 路由映射
  • 插件机制
  • 状态机行为绑定

相比大量的if-elif结构,函数映射更加简洁、易维护。

(6)高阶函数的概念

高阶函数(Higher-Order Function),是指满足以下任意条件的函数:

  • 接收一个或多个函数作为参数
  • 返回一个函数作为结果

由于 Python 中函数是一等对象,因此高阶函数成为一种自然且常用的编程手段。

Python 内置的mapfiltersorted等,都是典型的高阶函数。

nums = [1, 2, 3, 4, 5] result = list(map(lambda x: x * 2, nums)) print(result) # 输出:[2, 4, 6, 8, 10]

(7)与其他语言的对比

在 Python、JavaScript 等语言中,函数与字符串、数字、对象拥有相同的“公民权利”,因此被称为一等公民(First-Class Citizens)。

而在传统的 Java、C++(不考虑现代函数对象与 Lambda 的情况下)中:

  • 函数本身不能直接赋值给变量
  • 不能作为普通参数或返回值使用
  • 通常需要通过接口、函数指针或类进行间接实现

这使得函数在语言层面上属于“二等对象”,表达能力相对受限。

2. 装饰器(Decorator)

(1)装饰器的本质

在前一节中我们提到,以函数作为参数或返回值的函数称为高阶函数。
在 Python 中,使用高阶函数对另一个函数进行“包装(wrapping)”是一种非常常见的编程模式。

先看一个不使用装饰器语法的示例:

def higherOrder(inner): def func(): # 前置逻辑 print("before inner function") inner() # 后置逻辑 print("after inner function") return func def someFunc(): print("someFunc running") finalFunc = higherOrder(someFunc) finalFunc()

执行结果为:

before inner function someFunc running after inner function

在该示例中:

  • higherOrder是一个高阶函数
  • inner是被包装的原始函数
  • func是新的函数,用于在调用前后插入额外逻辑
  • higherOrder(someFunc)返回一个“增强版”的函数

这正是装饰器的核心思想:
在不修改原函数代码的前提下,为其添加额外功能。

(2)装饰器语法糖的由来

虽然上述写法功能完整,但在实际开发中存在明显问题:

  • 代码冗长
  • 原函数名被新的变量覆盖
  • 可读性和可维护性较差

为了解决这些问题,Python 提供了装饰器语法糖(Decorator Syntax),用于简化高阶函数的使用方式。

使用装饰器语法后,代码可以改写为:

def higherOrder(inner): def func(): # 前置逻辑 print("before inner function") inner() # 后置逻辑 print("after inner function") return func @higherOrder def someFunc(): print("someFunc running") someFunc()

此时:

@higherOrder def someFunc(): ...

等价于:

someFunc = higherOrder(someFunc)

也就是说,装饰器本质上只是一个语法层面的简化写法,并没有引入新的语言能力。

(3)装饰器的执行时机

需要特别注意的一点是:
装饰器在函数定义阶段执行,而不是在函数调用阶段执行。

def decorator(func): print("decorator executed") def wrapper(): print("wrapper executed") func() return wrapper @decorator def test(): print("test executed") test()

执行顺序为:

decorator executed wrapper executed test executed

这说明:

  • decorator在解释器加载函数定义时立即执行
  • wrapper在调用test()时执行

这一特性在框架设计和代码自动注册机制中尤为重要。

(4)带参数的函数装饰器(基础版)

现实中的函数通常都包含参数,因此装饰器也需要能够处理任意参数。

def log(func): def wrapper(*args, **kwargs): print(f"call function: {func.__name__}") return func(*args, **kwargs) return wrapper @log def add(a, b): return a + b result = add(3, 5) print(result)

输出结果:

call function: add 8

这里的关键点包括:

  • 使用*args**kwargs接收任意参数
  • 在不影响原函数行为的前提下增加日志功能

这是装饰器最常见、最实用的使用模式之一。

(5)装饰器的工程价值

装饰器的最大优势在于横切关注点(Cross-Cutting Concerns)的抽离,例如:

  • 日志记录
  • 权限校验
  • 参数校验
  • 性能统计
  • 缓存控制
  • 事务管理

通过装饰器,这些逻辑可以与业务代码彻底解耦。

def auth_required(func): def wrapper(user): if not user.get("is_login"): raise PermissionError("User not logged in") return func(user) return wrapper @auth_required def view_profile(user): print("view profile") user = {"is_login": True} view_profile(user)

(6)装饰器在框架中的应用:以 Flask 为例

装饰器在 Python Web 框架中被大量使用,其中最典型的代表是Flask

在传统方式下,开发 Web 服务器需要手动处理:

  • TCP 连接
  • HTTP 协议解析
  • 路由分发
  • 请求与响应生命周期

而在 Flask 中,只需通过装饰器即可完成路由绑定:

from flask import Flask app = Flask(__name__) @app.route("/hello") def hello(): return "Hello, Flask!"

这里的@app.route("/hello")本质上是:

  • 接收hello函数
  • 将其注册到路由表中
  • 自动完成 HTTP 请求与函数调用的映射

开发者无需关心底层协议细节,只需专注业务逻辑。

3. 生成器(Generator)

(1)生成器的回顾与引入

在第 4 章中,我们已经接触过生成器语法,即与列表推导式非常相似的表达方式。例如:

gen = (x * x for x in range(5)) for value in gen: print(value)

这种写法被称为生成器表达式(Generator Expression)。
它不会一次性生成所有结果,而是在遍历过程中按需计算并返回值。

然而,生成器表达式适用于逻辑简单的场景。当生成过程包含:

  • 多分支判断
  • 循环嵌套
  • 状态保存
  • 分阶段产出结果

时,就需要一种更通用、可控性更强的方式来创建生成器。

这正是yield语句存在的意义。

(2)使用yield定义生成器函数

在 Python 中,只要函数体中出现了yield关键字,该函数就不再是普通函数,而是一个生成器函数。

return的核心区别在于:

  • return:结束函数执行,并返回一个值
  • yield:返回一个值,并暂停函数执行状态

示例如下:

def count_up_to(n): i = 1 while i <= n: yield i i += 1

调用该函数时:

gen = count_up_to(3) print(next(gen)) # 1 print(next(gen)) # 2 print(next(gen)) # 3

此过程中:

  • 每次调用next(),函数都会从上一次yield的位置继续执行
  • 局部变量的状态会被自动保存
  • 直到函数执行结束,抛出StopIteration异常

(3)yieldreturn的本质区别

从执行模型上看,生成器函数具有以下特点:

特性

普通函数

生成器函数

返回值

return一次性返回

多次yield返回

执行状态

不可恢复

可暂停、可恢复

内存占用

可能较高

极低(惰性计算)

示例对比:

def normal_func(): return [1, 2, 3] def generator_func(): yield 1 yield 2 yield 3

normal_func()会立即构造完整列表,而generator_func()则按需产生数据。

(4)生成器的典型应用场景

生成器非常适合以下场景:

  • 大数据量遍历
  • 文件逐行读取
  • 流式数据处理
  • 网络数据消费
  • 状态机实现

例如,逐行读取文件:

def read_lines(filepath): with open(filepath, "r", encoding="utf-8") as f: for line in f: yield line.strip()

该方式相比一次性读取整个文件,更加节省内存,且具备良好的可扩展性。

(5)yield from的动机与作用

在 Python 3.3 中,引入了新的关键字yield from,用于生成器之间的委托(delegation)。

在没有yield from之前,如果一个生成器需要迭代另一个生成器,通常需要写成:

def gen1(): yield 1 yield 2 def gen2(): for value in gen1(): yield value yield 3

虽然功能正确,但语法略显冗长。

(6)使用yield from简化生成器嵌套

使用yield from后,上述代码可以改写为:

def gen1(): yield 1 yield 2 def gen2(): yield from gen1() yield 3

此时:

  • yield from gen1()会自动迭代gen1
  • gen1产生的每一个值逐个yield给调用方
  • 不需要显式编写循环逻辑

从行为上看,两种写法是完全等价的,但yield from更简洁,也更易维护。

(7)yield from的高级语义(概念层面)

在更深层次上,yield from不仅仅是语法简化,它还负责:

  • 自动传递StopIteration的返回值
  • 转发send()throw()等生成器方法
  • 构建生成器协程的基础能力

这些能力是实现协程(Coroutine)与 async / await 机制的重要基础(本书后续章节将详细展开)。

(8)综合示例:拆分生成逻辑

def produce_numbers(): yield from range(1, 4) def produce_letters(): yield from ["a", "b", "c"] def combined(): yield from produce_numbers() yield from produce_letters() for item in combined(): print(item)

输出结果:

1 2 3 a b c

该示例展示了:

  • 多个生成器的组合
  • 清晰的职责拆分
  • 极低的额外内存开销

4. 迭代、可迭代对象与迭代器

(1)迭代(Iteration)的概念

迭代是一个通用术语,指的是逐个获取某一对象中元素的过程。
当我们使用显式或隐式循环结构,对一组元素进行顺序访问时,这一过程就称为迭代。

例如:

for x in [1, 2, 3]: print(x)

无论是for循环、列表推导式,还是mapfilter等高阶函数,其底层行为都依赖于“迭代”这一抽象概念。

需要强调的是:

迭代本身只是一个概念,而不是某种具体的数据结构或对象类型。

(2)可迭代对象(Iterable)

根据 Python 官方文档的定义,可迭代对象(Iterable)是指满足以下任一条件的对象:

  1. 实现了__iter__()方法,该方法返回一个迭代器
  2. 或者实现了__getitem__()方法,并且:
    • 支持从0开始的连续整数索引
    • 当索引无效时抛出IndexError异常

常见的可迭代对象包括:

  • list
  • tuple
  • str
  • dict
  • set
  • range
  • 文件对象

示例:

nums = [1, 2, 3] print(hasattr(nums, "__iter__")) # True

(3)迭代器(Iterator)

迭代器是实现了迭代器协议(Iterator Protocol)的对象,必须同时具备以下两个方法:

  • __iter__():返回迭代器自身
  • __next__():返回下一个元素;若无元素可返回,则抛出StopIteration异常

形式化定义如下:

Iterator = __iter__() + __next__()

示例:手动获取列表的迭代器

nums = [1, 2, 3] it = iter(nums) print(next(it)) # 1 print(next(it)) # 2 print(next(it)) # 3 # next(it) -> StopIteration

此处:

  • nums是可迭代对象
  • it是由iter(nums)返回的迭代器

(4)可迭代对象与迭代器的区别

对比项

可迭代对象

迭代器

是否可多次遍历

否(一次性)

是否保存状态

是否实现__next__

不一定

必须

是否实现__iter__

必须

必须

示例对比:

nums = [1, 2, 3] it1 = iter(nums) it2 = iter(nums) print(next(it1)) # 1 print(next(it1)) # 2 print(next(it2)) # 1(独立的迭代器)

同一个可迭代对象可以生成多个彼此独立的迭代器。

(5)iter()函数的作用

当可迭代对象作为参数传入内置函数iter()时,Python 会返回该对象对应的迭代器。

s = "abc" it = iter(s) print(next(it)) # a print(next(it)) # b print(next(it)) # c

对于用户代码而言,通常无需显式调用iter(),因为 Python 会在必要时自动完成这一步。

(6)for循环背后的真实机制

在 Python 中,for循环本质上是基于迭代器协议的语法糖。

以下两段代码在行为上是等价的:

for x in iterable: print(x)

等价于:

_it = iter(iterable) while True: try: x = next(_it) print(x) except StopIteration: break

由此可以看出:

  • for循环会自动调用iter()创建迭代器
  • 在每一次循环中调用next()
  • 捕获StopIteration以结束循环

这也是为什么只要对象“可迭代”,就能直接用于for循环。

(7)自定义迭代器示例

class CountDown: def __init__(self, start): self.current = start def __iter__(self): return self def __next__(self): if self.current <= 0: raise StopIteration value = self.current self.current -= 1 return value for num in CountDown(3): print(num)

输出结果:

3 2 1

该示例完整实现了迭代器协议。

(8)生成器与迭代器的关系

需要特别说明的是:

Python 中的生成器,本身就是一种迭代器。

生成器对象自动实现了:

  • __iter__()
  • __next__()

因此可以直接用于for循环、next()等所有迭代场景。

def gen(): yield 1 yield 2 g = gen() print(next(g)) # 1 print(next(g)) # 2

5. 标准输入、标准输出与管道

(1)基本概念概述

在终端(Terminal)或命令提示符(Command Prompt)中执行 Python 程序时,标准输入(stdin)、标准输出(stdout)管道(pipe)是最基础、也是最常见的 I/O 机制。

在操作系统层面,一个进程在启动时,默认会关联三种标准数据流:

  • 标准输入(stdin):程序读取数据的来源
  • 标准输出(stdout):程序正常输出数据的去向
  • 标准错误(stderr):程序异常或错误信息的输出通道

在本节中,我们重点关注前两者以及它们之间的组合方式。

(2)Python 中的标准输入与标准输出

在 Python 中:

  • input()用于从 标准输入 读取数据
  • print()用于向 标准输出 写入数据

示例如下:

name = input("Enter your name: ") print("Hello,", name)

当该程序在终端中运行时:

  • 用户通过键盘输入内容
  • 输入内容被传递给input()
  • 结果通过print()输出到终端屏幕

此时,终端既是程序的输入源,也是输出目标。

(3)标准输入与标准输出的重定向

标准输入和标准输出并不一定只能连接到终端。
在命令行环境中,它们可以被重定向到文件或其他程序。

① 输出重定向

输出重定向常用符号包括:

  • >:覆盖写入
  • >>:追加写入

示例:

python demo.py > output.txt

该命令会将程序原本输出到终端的内容,全部写入output.txt文件中(若文件已存在则覆盖)。

python demo.py >> output.txt

则表示在文件末尾追加内容。

② 输入重定向

输入重定向使用<符号,用于将文件作为程序的输入来源。

python demo.py < input.txt

在这种情况下:

  • 程序中的input()将从input.txt中读取数据
  • 程序无需关心数据来自键盘还是文件

示例代码(demo.py):

text = input() print(text.upper())

(4)使用命令行参数实现文件读写

在实际工程中,更通用的方式是通过命令行参数指定输入与输出文件。

import sys input_file = sys.argv[1] output_file = sys.argv[2] with open(input_file, "r", encoding="utf-8") as f: content = f.read() with open(output_file, "w", encoding="utf-8") as f: f.write(content.upper())

执行方式如下:

python convert.py input.txt output.txt

这种方式相比纯重定向更清晰,也更利于脚本复用与自动化。

(5)管道(Pipe)的概念

管道是一种用于连接多个程序的数据传递机制,其核心思想是:

将一个程序的标准输出,直接作为另一个程序的标准输入。

管道使用|符号表示。

program_a | program_b

在该结构中:

  • program_a负责产生数据
  • program_b负责消费数据

两者之间不需要中间文件。

(6)Python 程序中的管道示例

示例 1:统计文本行数

cat data.txt | python count_lines.py

count_lines.py内容如下:

import sys count = 0 for _ in sys.stdin: count += 1 print(count)

这里:

  • cat负责读取文件并输出内容
  • Python 程序从sys.stdin中读取数据
  • 管道完成了程序间的数据衔接

示例 2:多程序组合处理数据

cat data.txt | python clean.py | python analyze.py

每个程序只负责一项功能:

  • clean.py:清洗数据
  • analyze.py:分析数据

这种方式具有极高的组合灵活性。

(7)UNIX 管道思想与软件设计哲学

管道的概念源自 UNIX 系统设计哲学,其核心原则包括:

  • 程序应当只做一件事,并把它做好
  • 程序应当通过标准输入/输出进行通信
  • 程序应当可以被自由组合

正因如此,小程序之间可以通过管道形成强大的功能组合。

例如,假设有 5 个功能单一的程序:

  • 每个程序只负责一种处理逻辑
  • 通过管道进行任意组合

理论上可以形成:

5 × 5 = 25 种不同的组合程序

而无需编写 25 个独立程序。

需要专业的网站建设服务?

联系我们获取免费的网站建设咨询和方案报价,让我们帮助您实现业务目标

立即咨询