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 在很多设计上其实都非常统一。

相关推荐
2603_965148115 小时前
如何解析JSON数据?API返回的商品信息处理教程
开发语言·数据库·python·自动化·json·api
jufeng13076 小时前
【系列:手搓自主 AI Agent:Hermes 架构原理剖析 · 第 8 篇】
python·ai agent·配置系统
circuitsosk7 小时前
跨境电商智能化实战:AI如何赋能客服自动回复、广告智能投放与供应链预测
大数据·人工智能·python·langchain·智能客服
2601_956319887 小时前
2026年零基础学量化:从看懂示例到写清条件和动作
人工智能·python
知无不研7 小时前
lambda表达式的使用(3)
开发语言·c++·lambda
过期的秋刀鱼!8 小时前
LangChain-D1-模型的工作流程
人工智能·python·langchain
萤火工厂目视化设计8 小时前
智能制造与企业文化目视化浪潮下:中小工厂的机遇与挑战
python·制造
cfm_29148 小时前
SpringAI + Ollama 本地大模型
java·开发语言·人工智能·语言模型
DeepVisionary9 小时前
从GPT-5.6 Sol的Ultrafast模式看推理速度竞赛:750 token/秒背后,Cerebras的晶圆级生意
python·自动化
C++ 老炮儿的技术栈9 小时前
基于Qt实现轻量化本地音乐播放器
开发语言·c++·qt·c·播放器·音乐