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

相关推荐
c++之路几秒前
C++ 模板
linux·开发语言·c++
幻影七幻1 分钟前
js中send的作用和使用 $.ajax的作用
开发语言·前端·javascript
xcbrand3 分钟前
餐饮品牌全案公司哪家可靠
运维·python
鸿儒5176 分钟前
记录一个C++ Windows程序移植到Linux系统的bug
开发语言·c++·bug
浮尘笔记10 分钟前
在Snowy后台无需编码实现自动化生成CRUD操作流程
java·开发语言·经验分享·spring boot·后端·程序人生·mybatis
2401_8463395611 分钟前
CSS如何解决Less与CSS兼容性问题_通过配置文件实现平滑过渡与混合开发
jvm·数据库·python
qq_4138474012 分钟前
CSS如何控制全屏显示的元素样式
jvm·数据库·python
scan72415 分钟前
上下文摘要
python
第一程序员17 分钟前
2026年GitHub上最火的10个Python项目,Rust开发者必看
python·rust·github
MoonBit月兔22 分钟前
MoonBit 作为重大成果亮相广东省人工智能应用对接大会,展示 AI 原生编程语言最新进展
开发语言·人工智能·moonbit