Python のメモリビューのポイントは何ですか? 質問する

Python のメモリビューのポイントは何ですか? 質問する

チェック中ドキュメンテーションメモリビューについて:

memoryview オブジェクトを使用すると、Python コードはコピーせずにバッファ プロトコルをサポートするオブジェクトの内部データにアクセスできます。

クラスメモリビュー(オブジェクト)

obj を参照するメモリビューを作成します。obj はバッファ プロトコルをサポートしている必要があります。バッファ プロトコルをサポートする組み込みオブジェクトには、bytes と bytearray があります。

次にサンプルコードを示します。

>>> v = memoryview(b'abcefg')
>>> v[1]
98
>>> v[-1]
103
>>> v[1:4]
<memory at 0x7f3ddc9f4350>
>>> bytes(v[1:4])
b'bce'

引用は終わりました。次は詳しく見てみましょう。

>>> b = b'long bytes stream'
>>> b.startswith(b'long')
True
>>> v = memoryview(b)
>>> vsub = v[5:]
>>> vsub.startswith(b'bytes')
Traceback (most recent call last):
  File "<stdin>", line 1, in <module>
AttributeError: 'memoryview' object has no attribute 'startswith'
>>> bytes(vsub).startswith(b'bytes')
True
>>> 

上記から私が得た結論は次のとおりです。

バッファ オブジェクトの内部データをコピーせずに公開するために、メモリビュー オブジェクトを作成しますが、オブジェクトで何か有用なことを行うには (オブジェクトによって提供されるメソッドを呼び出すことによって)、コピーを作成する必要があります。

通常、メモリビュー (または古いバッファ オブジェクト) は、大きなオブジェクトがある場合に必要になり、スライスも大きくなる可能性があります。大きなスライスを作成する場合、または小さなスライスを何度も作成する場合は、効率を向上させる必要があります。

上記のスキームでは、私がここで何を見逃しているのかを誰かが説明してくれない限り、どちらの状況でもそれがどのように役立つのかわかりません。

編集1:

大量のデータがあり、それを最初から最後まで進めて処理したいとします。たとえば、文字列バッファの先頭からトークンを抽出して、バッファが消費されるまで処理します。C 用語では、これはバッファを介してポインタを進めることであり、ポインタはバッファ タイプを期待する任意の関数に渡すことができます。同様のことを Python で実行するにはどうすればよいですか?

回避策を提案する人もいます。たとえば、多くの文字列関数や正規表現関数は、ポインターを進めることをエミュレートするために使用できる位置引数を取ります。これには 2 つの問題があります。1 つ目は回避策であるため、欠点を克服するためにコーディング スタイルを変更する必要があります。2 つ目は、すべての関数に位置引数があるわけではないことです。たとえば、正規表現関数やstartswithdo、encode()/ decode()don't などです。

データをチャンクでロードしたり、最大トークンよりも大きい小さなセグメントでバッファを処理したりすることを提案する人もいるかもしれません。これらの回避策があることはわかっていますが、言語に合わせてコーディング スタイルを曲げることなく、Python でより自然な方法で作業するはずですよね?

編集2:

コード サンプルがあれば、より明確になります。これが私がやりたいことであり、一見すると、memoryview でできることだと想定していたことです。探している機能には、pmview (適切なメモリ ビュー) を使用しましょう。

tokens = []
xlarge_str = get_string()
xlarge_str_view =  pmview(xlarge_str)

while True:
    token =  get_token(xlarge_str_view)
    if token: 
        xlarge_str_view = xlarge_str_view.vslice(len(token)) 
        # vslice: view slice: default stop paramter at end of buffer
        tokens.append(token)
    else:   
        break

ベストアンサー1

が便利な理由の 1 つは、 /memoryviewとは異なり、基になるデータをコピーせずにスライスできることです。bytesstr

たとえば、次のおもちゃの例を見てみましょう。

import time
for n in (100000, 200000, 300000, 400000):
    data = b'x'*n
    start = time.time()
    b = data
    while b:
        b = b[1:]
    print(f'     bytes {n} {time.time() - start:0.3f}')

for n in (100000, 200000, 300000, 400000):
    data = b'x'*n
    start = time.time()
    b = memoryview(data)
    while b:
        b = b[1:]
    print(f'memoryview {n} {time.time() - start:0.3f}')

私のパソコンでは

     bytes 100000 0.211
     bytes 200000 0.826
     bytes 300000 1.953
     bytes 400000 3.514
memoryview 100000 0.021
memoryview 200000 0.052
memoryview 300000 0.043
memoryview 400000 0.077

繰り返し文字列スライスの 2 次複雑度がはっきりとわかります。400000 回の反復だけでも、すでに手に負えない状態です。一方、memoryviewバージョンは線形複雑度を持ち、非常に高速です。

編集:これは CPython で行われたことに注意してください。Pypy 4.0.1 までは、メモリビューのパフォーマンスが 2 乗になるバグがありました。

おすすめ記事