非C程序员的缓冲区和Memoryview对象解释

11
Python 2.7引入了新的buffers和memoryview对象API
我阅读了相关文档,我认为我已经掌握了基本概念(以原始形式访问对象的内部数据而不进行复制,这意味着一种“更快且占用更少内存”的获取对象数据的方式),但要真正理解文档,读者应该具备超出我所知的C语言知识。
如果有人能够抽出时间:
- 用通俗易懂的语言解释buffers和memoryview对象 - 描述一个使用buffers和memoryview对象的场景,这将是“Pythonic方式”做事情的例子
我将非常感激。

请查看这个答案,它解释了它们在纯Python中的使用,但没有涉及C API。 - Scott Griffiths
@Scott - 在我发布这个问题之前已经阅读(并且投票)了。:)非常有用。@agf的答案(和链接!)帮助我更好地理解...但是C-API的事情有点让我困惑:这只是为了解释创建memoryview类型的基本原理,还是还有其他我绝对需要知道的东西?另外:为什么无法编写公开缓冲区接口的对象?这是Guido & Co.的设计选择还是Python内部工作的隐含限制? - mac
@agf - 感谢您的评论,但我认为我的评论被误解了:我是在提到@Scott自己在评论中链接的答案。答案的最后一句话是:“还要注意,如果不深入C API,即纯Python无法实现自己对象的缓冲区接口。” - mac
1
@mac 是的,我错过了那个。我认为之所以经常提到C-API,是因为直到Python 2.6,它是使用(旧的)缓冲区接口/协议的唯一方法。因此,已经熟悉Python中“缓冲区”的人会以那种方式考虑它们。您不需要知道任何关于它的知识就可以使用2.6+缓冲区对象或2.7+ memoryview对象。然而,这解释了为什么缓冲区对象是只读的:因为从用Python编写的代码中,缓冲区始终是只读的,因此我们需要将可供Python代码调用的读写缓冲区称为其他名称(memoryview)。 - agf
1
我的理解是,纯Python类型为什么不能实现memoryview接口并没有根本性的原因,只是还没有提供。我猜如果你真的需要它,那么你可能正在做一些相当低级和性能关键的事情,并且已经在使用C(或者应该使用C)。这不是我的经验,但无论如何,我认为我们不会很快得到纯Python memoryview类型。 - Scott Griffiths
2个回答

5

这是我编写的哈希函数中的一行代码:

M = tuple(buffer(M, i, Nb) for i in range(0, len(M), Nb))

这将把一个长字符串M分割成较短的长度为Nb的“字符串”,其中Nb是我一次可以处理的字节数/字符数。它在不复制字符串任何部分的情况下完成此操作,如果我像这样对字符串进行切片,将会发生复制:

M = tuple(M[i*Nb:i*Nb+Nb] for i in range(0, len(M), Nb))

我现在可以像切片一样迭代M:

H = key
for Mi in M:
    H = encrypt(H, Mi)

基本上,缓冲区和内存视图是处理Python的字符串不可变性和切片等复制行为的高效方法。内存视图就像缓冲区一样,但您还可以写入它,而不仅仅是读取。
虽然主要的缓冲器/内存视图文档是关于C语言实现的,但标准类型页面在内存视图下也有一些信息:http://docs.python.org/library/stdtypes.html#memoryview-type
编辑:在我的书签中找到了这个http://webcache.googleusercontent.com/search?q=cache:Ago7BXl1_qUJ:mattgattis.com/2010/3/9/python-memory-views+site:mattgattis.com+python&hl=en&client=firefox-a&gl=us&strip=1是一个非常好的简短概述。
编辑2:事实证明,我最初从当应该使用内存视图时?那个问题中得到了那个链接,那个问题从未详细回答过,并且该链接已死,因此希望这有所帮助。

等等……您是否真的可以使用 memoryview 修改字符串(和/或其他不可变对象)? - kindall
不可以 - 你只能改变可变对象,比如bytearray。memoryview有一个readonly属性,它会告诉你是否可以这样做。 - Russia Must Remove Putin

1
我正在寻找的答案部分是,buffer 是“旧方法”,memoryview 是新方法,但已被回溯到2.7版本 - 请参见存档博客here
这并没有回答我的问题,即为什么我认为在2.7中实现的C API 可以让我构建一个buffer,但不能构建memoryview...
要使memoryview在Python 2.7中工作,您需要在tp_flags中设置Py_TPFLAGS_HAVE_NEWBUFFER标志。 我发现内置的bytearray源是一个很好的参考; 它在Include/bytearrayobject.hObjects/bytearrayobject.c中。

这个答案中的链接已经失效。 - shuttle87
我恢复了链接,buhahah。 - Mr_and_Mrs_D

网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接