deque+yield+next语法

一、deque

  • deque (双端队列) 是 Python 标准库中的一个数据结构。
    from collections import deque
    previous_lines = deque(maxlen=history)
  • deque (双端队列) 是 Python 标准库中的一个数据结构。
  • maxlen=history 是关键参数。它设置了队列的最大长度。当你往队列里添加新元素时,如果队列已满(达到 maxlen),它会自动把最老的那个元素挤出去(FIFO,先进先出)。

具体使用方法

deque(发音类似 "deck")是 Python 标准库 collections 模块中的一个双端队列数据结构。

它的最大特点是:在两端添加或删除元素的速度非常快 ,比普通列表 list 快得多。

1. 导入与基本语法

导入:

因为是标准库里的模块,使用前需要先导入:

python 复制代码
from collections import deque

创建对象:

python 复制代码
# 创建一个空的双端队列
d = deque()
# 创建一个包含数据的队列
d = deque([1, 2, 3])
# 创建一个固定长度的队列(重点!)
d = deque(maxlen=5)

2. 核心方法详解

deque 提供了在两端操作数据的方法,记住 append 是加,pop 是删。

A. 右端操作(和 list 一样)
  • d.append(x):在右端添加元素 x。
  • d.pop():移除并返回右端的元素。
B. 左端操作(deque 的强项,list 做这个很慢)
  • d.appendleft(x):在左端添加元素 x。
  • d.popleft():移除并返回左端的元素。
C. 扩展操作
  • d.extend(iterable):在右端扩展多个元素。
  • d.extendleft(iterable):在左端扩展多个元素。

3. 代码示例:具体怎么用

场景一:两端操作对比 list

这是 deque 最基础的优势。如果你要在列表头部频繁插入数据,用 list 效率极低,用 deque 是瞬间完成。

python 复制代码
from collections import deque
d = deque([1, 2, 3])
# 右端操作
d.append(4)      # deque([1, 2, 3, 4])
print("append后:", d)
# 左端操作
d.appendleft(0)  # deque([0, 1, 2, 3, 4])
print("appendleft后:", d)
# 左端删除
x = d.popleft()  # x = 0, d 变成 deque([1, 2, 3, 4])
print("popleft取出了:", x)
场景二:固定长度窗口(maxlen 参数)

这是你最初代码中用到的高级用法。设置了 maxlen 后,队列就像一个固定大小的管子,新数据进去,老数据就会被自动挤出来。

python 复制代码
from collections import deque
# 创建一个最大长度为 3 的队列
d = deque(maxlen=3)
d.append(1)
d.append(2)
d.append(3)
print(d)  # 输出: deque([1, 2, 3], maxlen=3) -> 满了
# 再加一个数据 4
d.append(4) 
# 因为长度限制,最左边的 1 被自动挤走了
print(d)  # 输出: deque([2, 3, 4], maxlen=3)
场景三:移动窗口(类似日志滚动)

这就完美对应了你最开始贴出的代码逻辑:保留最近 N 条记录。

python 复制代码
def tail(filename, n=5):
    """模拟 tail -n 命令,返回文件的最后 n 行"""
    return deque(open(filename), maxlen=n)
# 对于你的代码逻辑:
# previous_lines = deque(maxlen=history)
# 每次循环 append 进去,如果超过了 history,最老的那一行自动消失,不用你手动写代码去删除。

4. 性能对比:为什么要用 deque?

这是面试或深入理解时常问的点。

  • list (列表) :底层是数组。如果在头部插入数据(insert(0, x)),后面的所有元素都要在内存里向后移动一位。数据量大时,非常慢。
  • deque :底层是双向链表。在头部或尾部插入数据,只需要改一下指针指向,不管数据量多大,速度都是一样的快(O(1) 复杂度)。
    实验代码(你可以跑一下感受差距):
python 复制代码
import time
from collections import deque
# 测试在头部插入 100,000 个数据
# 1. 使用普通 list
start = time.time()
lst = []
for i in range(100000):
    lst.insert(0, i)  # 在头部插入
print(f"List 耗时: {time.time() - start:.4f} 秒")
# 2. 使用 deque
start = time.time()
dq = deque()
for i in range(100000):
    dq.appendleft(i)  # 在头部插入
print(f"Deque 耗时: {time.time() - start:.4f} 秒")

结果预测list 会明显卡顿(可能几秒),deque 几乎是瞬间完成(0.0x 秒)。

总结

  1. 普通列表 list:适合随机访问、在尾部操作。
  2. 双端队列 deque
    • 适合频繁在头部尾部操作(如队列、栈)。
    • maxlen 参数是实现"保留最近N条记录"的神器,自动过期老数据。

二、yield+next

yield 是 Python 中定义生成器 的关键字。

简单来说,yield 是一个"懒惰"的 return

  • return:返回结果,函数结束,销毁局部变量。
  • yield:返回结果,函数暂停(休眠),保存当前状态(变量值、代码运行到哪一行),等待下次被唤醒继续执行。

1. 基本语法结构

python 复制代码
def my_generator():
    # 代码块 1
    yield 值A
    
    # 代码块 2
    yield 值B
    
    # 代码块 3
    return 结束  # (可选,生成器结束时会自动抛出 StopIteration)

核心规则:

  1. 只要函数内部包含 yield,调用这个函数时,代码不会立即执行,而是返回一个生成器对象。
  2. 只有当你去迭代这个对象==(如 for 循环或 next())==时,函数体才开始执行。
  3. 遇到 yield,函数暂停并返回后面的值。
  4. 下次迭代时,从上次暂停的地方继续往下跑。

2. 具体使用场景与示例

场景一:像流水线一样逐个产出(手动控制)

这是理解 yield 最好的方式。我们对比一下"一次性做完"和"分步做完"。

python 复制代码
def count_down(n):
    print("开始倒计时...")
    while n > 0:
        yield n  # 暂停,把 n 交出去
        n -= 1
    print("倒计时结束!")
# 1. 创建生成器(此时并没有打印"开始倒计时")
gen = count_down(3)
print(f"生成器对象: {gen}")
# 2. 手动获取第一个值
print(f"第一次: {next(gen)}") 
# 执行流程:开始运行函数 -> 打印"开始倒计时" -> 遇到 yield 3 -> 暂停,返回 3
# 输出: 第一次: 3
# 3. 手动获取第二个值
print(f"第二次: {next(gen)}")
# 执行流程:从上次暂停处醒来 -> n 减 1 变成 2 -> 遇到 yield 2 -> 暂停,返回 2
# 输出: 第二次: 2
# 4. 手动获取第三个值
print(f"第三次: {next(gen)}")
# 输出: 第三次: 1
场景二:处理海量数据(节省内存)

这是 yield 最实用的场景。假设你要处理 1 亿条数据,如果用 return 返回列表,内存直接爆满;用 yield 则只占用一条数据的内存。
用 return(内存杀手):

python 复制代码
def get_all_numbers(n):
    nums = []
    for i in range(n):
        nums.append(i)
    return nums  # 必须等 1亿个数都存好,内存爆炸
# 调用
res = get_all_numbers(100000000) 

用 yield(内存友好):

python 复制代码
def get_numbers_stream(n):
    for i in range(n):
        yield i  # 生成一个,给出去一个,内存里永远只有一个 i
# 调用
for num in get_numbers_stream(100000000):
    print(num)  # 每次只处理一个数,内存几乎不占用
场景三:保留上下文(你的原始代码)

在你的文件搜索代码中,yield 的作用是保留现场

python 复制代码
def search(lines, pattern, history=5):
    previous_lines = deque(maxlen=history)
    for li in lines:
        if pattern in li:
            # 此时 yield 暂停了函数,把结果给外部处理
            # 关键是:previous_lines 这个变量被保留在内存里了!
            # 等外部处理完,函数"醒来"继续往下读下一行
            yield li, previous_lines
        previous_lines.append(li)

如果这里用 return,函数一结束,previous_lines 就销毁了,你就无法维护"最近5行"这个滑动窗口了。


3. 进阶用法:yield 也可以接收数据

除了产出数据,yield 还能接收外部传进来的数据(使用 .send() 方法),这可以让生成器变成"协程"。

python 复制代码
def accumulator():
    total = 0
    while True:
        # x 接收外部 send 进来的值
        x = yield total  
        if x is None: break
        total += x
gen = accumulator()
print(next(gen))      # 启动生成器,输出: 0 (运行到 yield total 暂停)
print(gen.send(10))   # 发送 10 进去,yield 接收赋值给 x,total 变成 10,再次 yield,输出: 10
print(gen.send(20))   # 发送 20 进去,total 变成 30,输出: 30

(注:这属于高级用法,初学者了解即可)

总结

  1. 语法:写在函数里,后面跟要返回的值。
  2. 机制 :遇到 yield 就暂停,下次调用继续。
  3. 怎么用
    • 配合 for 循环使用(自动迭代)。
    • 配合 next() 函数使用(手动获取)。
  4. 核心价值节省内存。将"生成所有数据"变成"按需生成数据"。
相关推荐
小王毕业啦1 小时前
2007-2024年 上市公司-投资者情绪数据(xlsx)
大数据·人工智能·数据挖掘·数据分析·数据统计·社科数据·经管数据
(; ̄ェ ̄)。1 小时前
机器学习入门(二十一)特征工程
人工智能·机器学习
CeshirenTester2 小时前
保姆级教程 | 人工智能应用开发平台 Coze
人工智能
瑶光守护者2 小时前
【学习笔记】3GPP NR-NTN 移动性IRAT分析
笔记·学习·卫星通信·nr-ntn
码农三叔2 小时前
《卷2:人形机器人的环境感知与多模态融合》
人工智能·嵌入式硬件·算法·机器人·人形机器人
MaoziShan2 小时前
CMU Subword Modeling | 02 Signs, Minimal Signs, and Compositionality
人工智能·自然语言处理
烁烁闪闪烁烁2 小时前
【weelinking系列Claude教程】 04 - Claude Code 安装与配置
人工智能·chatgpt·ai编程·claude·cursor·claude code·opencode
wwj20242 小时前
红海云数字化如何赋能“十五五”人才规划高效落地
大数据·人工智能
福大大架构师每日一题2 小时前
2026-01-15:下一个特殊回文数。用go语言,给定一个整数 n,求出一个比 n 更大的最小整数,该整数需要满足两条规则: 1. 它的十进制表示从左到右与从右到左完全一致(即读起来是对称的)。 2
python·算法·golang