编程学习网 > 编程语言 > Python > Python 3.15 take_bytes() 来了!这个零拷贝方法,新手越早知道越好
2026
07-01

Python 3.15 take_bytes() 来了!这个零拷贝方法,新手越早知道越好


你有没有遇到过这种情况——

你写一个脚本处理大文件,为了性能,先用 bytearray 当缓冲区。读一段、改一段、拼一段,最后要把结果交给下游函数。你顺手写下:

data = bytes(buffer)

buffer.clear()

看起来没问题,对吧?

但数据量一大,这段代码会突然变得很慢。因为你调用 bytes(buffer) 的时候,Python 要把整段内容复制一份出来。 buffer 里要是几十兆字节,这一复制就是几十兆字节的内存开销。

你查资料,人家说:"memoryview 啊。" 你试了试,发现 memoryview 不拥有数据,原 buffer 一清它就失效,各种坑防不胜防。

你陷入一种熟悉的纠结:bytearray 灵活但慢,bytes 快但不可变。两者之间反复横跳,代码越写越复杂。

现在,Python 3.15 给了一个正儿八经的解决方案:新方法 bytearray.take_bytes()。这个被社区喊了十几年的零拷贝需求,总算落地了。

这篇文章,我就用大白话告诉你它到底解决了什么痛点,以及新手怎么用才不会踩坑。

一、take_bytes() 到底做了什么?

一句话:它从 bytearray 开头提取一段字节,返回一个 bytes 对象,同时把这段字节从原 buffer 删掉

语法很简单:

bytearray.take_bytes(n=None)

参数 n 是可选的:

- 不传 n,提取全部内容;

- n,提取前 n 个字节。

看个最直观的例子:

buffer = bytearray(b"hello world")

data = buffer.take_bytes(5)

 

print(data)      # b"hello"

print(buffer)    # bytearray(b" world")

注意两个细节:

1. 返回的是 bytes,不是 bytearraydownstream 函数可以放心用,不会被人意外修改。

2. buffer 被改了。前 5 个字节被"拿走"buffer 变成剩下的内容。这点非常像队列的 pop 操作。

如果你只是想把整个 buffer 变成 bytes 并清空,原来要写两行:

data = bytes(buffer)   # 复制一次

buffer.clear()

现在一行搞定:

data = buffer.take_bytes()   # 不复制,直接拿走

性能上的好处显而易见:数据没有发生二次复制。在大型数据流、网络协议解析、视频处理等场景,这种零拷贝能省下大量内存和 CPU

二、三种场景,看完你就知道该在哪用了

光说概念不直观。下面这三个场景,是新手最容易遇到的情况。

场景一:按分隔符拆分数据流

假设你在处理一个 TCP 数据流,数据按换行符分隔。你先把收到的字节塞进 buffer,然后想取出第一行。

老写法:

buffer = bytearray(b"line1\nline2\nline3")

n = buffer.find(b"\n")

line = bytes(buffer[:n + 1])   # 复制一次

del buffer[:n + 1]             # 再删除一次

新写法:

buffer = bytearray(b"line1\nline2\nline3")

n = buffer.find(b"\n")

line = buffer.take_bytes(n + 1)  # 提取 + 删除,一步完成

代码少了一行,而且避免了 bytes(buffer[:n + 1]) 造成的切片复制。缓冲区越大,差距越明显。

场景二:读取并返回固定大小数据块

比如你写一个文件分块读取器,每次返回 4KB 数据:

def read_chunk(file, buffer, chunk_size=4096):

    # 先把文件内容读到 buffer

    while len(buffer) < chunk_size:

        chunk = file.read(1024)

        if not chunk:

            break

        buffer += chunk

 

    # 返回前 chunk_size 字节,并清空已返回部分

    return buffer.take_bytes(chunk_size)

take_bytes 的好处是:返回的 chunk 是独立的 bytes,原 buffer 自动释放掉已返回的部分。你不需要手动维护 read pointer,逻辑清爽很多。

场景三:丢弃分隔符之后的内容

有时你读到某个分隔符就够了,后面一大段数据不想要。比如读取 HTTP header

buffer = bytearray(b"header body body body")

n = buffer.find(b" ")

buffer.resize(n)          # 只保留 header 部分

header = buffer.take_bytes()  # 全部拿走

这里 resize(n) 把不需要的后半段直接截掉,然后 take_bytes() 把剩下的部分以 bytes 形式取出。整个过程没有额外复制。

三、新手最容易踩的3个坑

新方法虽然好用,但有几个细节不注意就会翻车。

坑一:以为它不会修改原 buffer

take_bytes 的副作用是"拿走"数据。如果你后续还需要 buffer 里的内容,千万别直接调用。

buffer = bytearray(b"abc")

a = buffer.take_bytes(1)   # a = b"a"

print(buffer)              # bytearray(b"bc"),原数据没了!

如果你只是想复制一份出来看看,还是要用 bytes(buffer[:n])take_bytes 是为了"取出并消费",不是为了"复制查看"

坑二:n 超过 buffer 长度

如果 n buffer 还长,Python 会报什么?答案是:直接拿走全部,不会报错。也就是说,take_bytes "最多拿 n ",不是"必须拿 n "

buffer = bytearray(b"hi")

data = buffer.take_bytes(100)

print(data)      # b"hi"

print(buffer)    # bytearray(b"")

这个行为很像列表的 pop queue get。如果你需要严格保证拿到 n 个字节,自己先判断长度。

坑三:在不需要零拷贝的场景硬用

如果你只是处理几个 KB 的字符串,复制一次根本无所谓。这时候用 take_bytes 虽然不会错,但收益也不大,反而让代码更难懂。

零拷贝的真正价值在:大数据、高并发、网络 IO、音视频处理。日常小脚本,别为了炫技而炫技。

四、写在最后

bytearray.take_bytes() Python 3.15 里一个很小但非常实用的改动。它没有改变你的编程范式,但解决了新手在"可变缓冲区""不可变字节"之间反复横跳的老大难问题。

总结三句话:

1. 想把 bytearray 变成 bytes 并清空,用 take_bytes(),不用复制。

2. 想按长度或分隔符取出一段数据,用 take_bytes(n),取出后原 buffer 自动释放。

3. 只是复制查看,不要用它;数据量小,也没必要硬上。

这个改动不算轰动,但对你写高性能 Python 代码会很有帮助。等 3.15 正式发布后,你在大文件、网络协议、数据流这些场景里,会明显感觉到它省了不少事。

以上就是“Python 3.15 take_bytes() 来了!这个零拷贝方法,新手越早知道越好的详细内容,想要了解更多Python教程欢迎持续关注编程学习网。 

扫码二维码 获取免费视频学习资料

Python编程学习

查 看2022高级编程视频教程免费获取