105

memoryviewのドキュメントを確認します。

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

クラスメモリビュー(obj)

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
>>> 

したがって、上記から収集したもの:

コピーせずに buffer オブジェクトの内部データを公開するために memoryview オブジェクトを作成しますが、(オブジェクトによって提供されるメソッドを呼び出すことによって) オブジェクトで何か役に立つことを行うには、コピーを作成する必要があります!

通常、オブジェクトが大きく、スライスも大きくなる場合は、memoryview (または古いバッファ オブジェクト) が必要になります。大きなスライスを作成する場合、または小さなスライスを作成する回数が多い場合は、効率を高める必要があります。

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

編集1:

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

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

他の人は、データをチャンクでロードするか、最大トークンより大きい小さなセグメントでバッファを処理することを提案するかもしれません。わかりましたので、これらの可能な回避策を認識していますが、言語に合わせてコーディング スタイルを曲げようとすることなく、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
4

5 に答える 5

98

s が便利な理由の 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 で行われたことに注意してください。4.0.1 までの Pypy には、メモリビューのパフォーマンスが 2 次になるというバグがありました。

于 2015-12-13T22:51:16.680 に答える