Python面试题(二)

今天我们继续聊聊Python相关的一些面试题目,前面的文章已经提到几个比较基础的问题,比如 Python 的特点、List 和 Tuple 的区别、深拷贝与浅拷贝、GIL 以及字典为什么这么快。

这一篇继续整理三个 Python 面试知识点:装饰器、生成器以及迭代器机制。这三个概念很多人刚学 Python 时都会觉得有点抽象,但其实理解之后,会发现它们是 Python 非常优雅的一套设计。

当然,以下内容仅是博主的个人理解与整理,如果有误,欢迎指正。

一、Python装饰器

Python的装饰器是什么?

简单说,

装饰器本质上就是一个函数 ,用来"包装 "另一个函数,从而在不修改原函数代码的情况下给它增加功能。

比如

python 复制代码
def log(func):
    def wrapper():
        print("start")
        func()
        print("end")
    return wrapper

这里的 log 就是一个装饰器函数。它接收一个函数 func 作为参数,然后在内部定义了一个新的函数 wrapper。在 wrapper 中,我们在函数执行前后分别打印了一些信息。

比如我们有一个函数如下

python 复制代码
def say_hello():
    print("hello")

现在希望在执行这个函数时打印一些日志,如果不用装饰器,可能会这样写:

python 复制代码
def say_hello():
    print("start")
    print("hello")
    print("end")

问题是,如果有很多函数都需要这样的功能,我们就要在每个函数里重复写同样的代码,这显然不太优雅。

装饰器就是为了解决这个问题。我们可以这样使用它:

python 复制代码
@log
def say_hello():
    print("hello")

这样当调用这个函数时,就可以实现前面的打印效果

很多初学者第一次看到 @log 时会觉得有点陌生,其实 Python 在背后做的事情很简单,本质上相当于:

python 复制代码
say_hello = log(say_hello)

也就是说,装饰器只是把原函数传进去,然后返回一个新的函数。

在实际开发中,装饰器的应用非常广泛。例如:

  • Web 框架中的 路由

  • 权限校验

  • 日志记录

  • 性能统计

  • 缓存处理

如果你用过 Flask,就会看到类似这样的代码:

python 复制代码
@app.route("/")
def index():
    return "Hello"

这里的 @app.route 本质上就是一个装饰器。

所以从设计角度来看,装饰器其实是一种非常典型的 AOP(面向切面编程)思想。

二、Python生成器

生成器是什么?

在 Python 中,生成器是一种可以按需生成数据的对象 。它最大的特点就是:不会一次性把所有数据都生成出来,而是需要的时候才生成。

生成器通常和一个关键字一起出现,那就是 yield。

我们先看一个简单的例子。

python 复制代码
def my_generator():
    yield 1
    yield 2
    yield 3

当我们调用这个函数时

python 复制代码
g = my_generator()

此时并不会立刻执行函数,而是返回一个 生成器对象。

当我们逐个取值时:

python 复制代码
print(next(g))
print(next(g))
print(next(g))

"""
1
2
3

"""

每调用一次 next(),函数就会执行到下一个 yield 位置,并返回对应的值。

如果你熟悉普通函数,可以把 yield 理解为一种 "暂停并返回" 的机制。函数运行到 yield 时会暂停,下次再继续从这个位置往下执行。

生成器最大的优势是 节省内存。

举个很典型的例子。如果我们想生成一千万个数字:

python 复制代码
nums = [i for i in range(10000000)]

这会在内存中一次性创建一个非常大的列表。

而如果用生成器:

python 复制代码
nums = (i for i in range(10000000))

此时 Python 不会真正创建这一千万个数字,而是 用一个生成器按需生成。只有当你遍历的时候,数据才会逐个产生。

因此生成器特别适合处理:

  • 大数据集

  • 流式数据

  • 文件读取

  • 网络数据流

例如 Python 中非常经典的写法:

python 复制代码
for line in open("file.txt"):
    print(line)

其实文件对象本身就是一个 迭代器 + 生成器式读取,每次只读取一行,而不是一次把整个文件读入内存。

三、Python 迭代器和可迭代对象

很多人在学习 Python 时会遇到两个很像的概念:

可迭代对象(Iterable)
迭代器(Iterator)

这两个概念其实是 Python 迭代机制的核心。

简单来说,可迭代对象就是"可以被遍历的对象"。

例如我们经常写:

python 复制代码
for i in [1,2,3]:
    print(i)

这里的列表 [1,2,3] 就是一个可迭代对象。

Python 中很多数据结构都是可迭代的,比如:

  • list

  • tuple

  • dict

  • string

  • set

只要一个对象可以被 for 循环遍历,我们通常就可以认为它是可迭代对象。

从实现角度来说,一个对象只要实现了 __iter__() 方法,就可以被称为可迭代对象。

不过 for 循环真正工作的对象,其实并不是可迭代对象本身,而是 迭代器。

迭代器是一种更底层的对象,它需要实现两个方法:

__next__() 的作用就是返回下一个元素,如果数据已经遍历完,就会抛出 StopIteration 异常。

举个简单例子,我们可以手动创建一个迭代器:

python 复制代码
nums = [1,2,3]

it = iter(nums)

print(next(it))
print(next(it))
print(next(it))

输出:1 2 3

这里的流程其实是:

  1. iter(nums) 把列表变成一个迭代器

  2. next(it) 每次获取下一个元素

而 for 循环其实就是帮我们自动完成了这些步骤。Python 在内部大致做的是这样的事情:

python 复制代码
it = iter(nums)

while True:
    try:
        item = next(it)
        print(item)
    except StopIteration:
        break

所以总结一句话:

可迭代对象是"可以被遍历的对象",而迭代器是"真正执行遍历动作的对象"。

另外,前面提到的 生成器其实也是一种特殊的迭代器 。只要函数中包含 yield,Python 就会自动帮我们创建一个迭代器对象。


总结

本文我们主要整理了 Python 面试中另外三个非常经典的问题。

装饰器 其实就是一个函数包装器,它可以在不修改 原函数代码的情况下扩展功能,在 Web 框架、日志记录和权限控制中都有大量应用。

生成器 则是一种非常优雅的数据生成方式,通过 yield 按需产生数据 ,可以大幅节省内存,非常适合处理大规模数据或流式数据。

而迭代器和可迭代对象 则构成了 Python 的整个迭代机制。像 for 循环、生成器、文件读取等功能,本质上都建立在这套机制之上。

理解了这些概念之后,你会发现 Python 在很多设计上其实都非常统一。

相关推荐
默_笙3 天前
🍙 给每个请求过安检:FastAPI 是怎么把校验写进类型注解的
python
小羊没烦恼!3 天前
初探性能优化——2个月到4小时的性能提升
java·开发语言·windows·算法·c#
qq_426003963 天前
启动playwright录制codegen生成自动化测试脚本
python·自动化
虎头金猫3 天前
4K 视频总卡在公网带宽?用 N1 + OpenList 把网盘播放链路重新理顺
运维·服务器·网络·python·容器·beautifulsoup·pandas
长沙三为智能科技3 天前
家政小程序开发从0到上线:五阶段交付流程与验收清单
python
伞伞悦读3 天前
【第38期】Python 模块与包详解:import、from、模块搜索路径、包结构和 __init__
开发语言·python
只睡四小时3 天前
Canvas 弹道联机实战:700 行 + 固定时间步长
python·websocket·html5·游戏开发·canvas
C语言小火车3 天前
C/C++ 为什么需要编译器?
开发语言·c++
奇思妙想聪明勤奋的小羊3 天前
DeepAgents第5章:子Agent 与上下文隔离—让 Agent学会委派
人工智能·python·学习·语言模型
lpfasd1233 天前
2026年第38周GitHub趋势周报
python·科技·github