60

bytesPython 3でオブジェクトを反復処理すると、個人bytesは次のようになりintsます。

>>> [b for b in b'123']
[49, 50, 51]

代わりに1レングスbytesのオブジェクトを取得するにはどうすればよいですか?

以下は可能ですが、読者にとってはあまり明白ではなく、おそらくパフォーマンスが悪いです。

>>> [bytes([b]) for b in b'123']
[b'1', b'2', b'3']
4

7 に答える 7

45

このコードのパフォーマンスが心配でint、バイトとしてのインターフェイスが適切でない場合は、使用するデータ構造を再検討する必要があります。たとえば、str代わりにオブジェクトを使用します。

オブジェクトをスライスして、bytes1つの長さのオブジェクトを取得できbytesます。

L = [bytes_obj[i:i+1] for i in range(len(bytes_obj))]

PEP0467があります-メソッドを提案するバイナリシーケンスのマイナーAPIの改善bytes.iterbytes()

>>> list(b'123'.iterbytes())
[b'1', b'2', b'3']
于 2013-01-10T21:53:00.003 に答える
22

int.to_bytes

intオブジェクトには、intを対応するバイトに変換するために使用できるto_bytesメソッドがあります。

>>> import sys
>>> [i.to_bytes(1, sys.byteorder) for i in b'123']
[b'1', b'2', b'3']

他のいくつかの回答と同様に、これがOPの元のソリューションよりも読みやすいかどうかは明らかではありません。長さとバイトオーダーの引数により、ノイズが増えると思います。

struct.unpack

もう1つのアプローチは、 struct.unpackを使用することですが、structモジュールに精通していない限り、これも読みにくいと見なされる可能性があります。

>>> import struct
>>> struct.unpack('3c', b'123')
(b'1', b'2', b'3')

(jfsがコメントで観察しているように、のフォーマット文字列はstruct.unpack動的に作成できます。この場合、結果の個々のバイト数は元のバイト文字列のバイト数と等しくなければならないことがわかっているので、struct.unpack(str(len(bytestring)) + 'c', bytestring)可能です。)

パフォーマンス

>>> import random, timeit
>>> bs = bytes(random.randint(0, 255) for i in range(100))

>>> # OP's solution
>>> timeit.timeit(setup="from __main__ import bs",
                  stmt="[bytes([b]) for b in bs]")
46.49886950897053

>>> # Accepted answer from jfs
>>> timeit.timeit(setup="from __main__ import bs",
                  stmt="[bs[i:i+1] for i in range(len(bs))]")
20.91463226894848

>>>  # Leon's answer
>>> timeit.timeit(setup="from __main__ import bs", 
                  stmt="list(map(bytes, zip(bs)))")
27.476876026019454

>>> # guettli's answer
>>> timeit.timeit(setup="from __main__ import iter_bytes, bs",        
                  stmt="list(iter_bytes(bs))")
24.107485140906647

>>> # user38's answer (with Leon's suggested fix)
>>> timeit.timeit(setup="from __main__ import bs", 
                  stmt="[chr(i).encode('latin-1') for i in bs]")
45.937552741961554

>>> # Using int.to_bytes
>>> timeit.timeit(setup="from __main__ import bs;from sys import byteorder", 
                  stmt="[x.to_bytes(1, byteorder) for x in bs]")
32.197659170022234

>>> # Using struct.unpack, converting the resulting tuple to list
>>> # to be fair to other methods
>>> timeit.timeit(setup="from __main__ import bs;from struct import unpack", 
                  stmt="list(unpack('100c', bs))")
1.902243083808571

struct.unpackおそらくバイトレベルで動作するため、他の方法よりも少なくとも1桁高速であるように思われます。 int.to_bytes一方、「明らかな」アプローチのほとんどよりもパフォーマンスが低下します。

于 2019-08-18T10:08:12.737 に答える
12

さまざまなアプローチのランタイムを比較することが役立つかもしれないと思ったので、(ライブラリを使用してsimple_benchmark)ベンチマークを作成しました。

ここに画像の説明を入力してください

おそらく当然のことながら、NumPyソリューションはラージバイトオブジェクトの最速のソリューションです。

ただし、結果のリストが必要な場合は、NumPyソリューション(を使用tolist())とソリューションの両方structが他の選択肢よりもはるかに高速です。

ジェネレーター関数が使用される理解の代わりにjfsソリューションとほとんど同じであるため、guettlisの回答は含めませんでした。

import numpy as np
import struct
import sys

from simple_benchmark import BenchmarkBuilder
b = BenchmarkBuilder()

@b.add_function()
def jfs(bytes_obj):
    return [bytes_obj[i:i+1] for i in range(len(bytes_obj))]

@b.add_function()
def snakecharmerb_tobytes(bytes_obj):
    return [i.to_bytes(1, sys.byteorder) for i in bytes_obj]

@b.add_function()
def snakecharmerb_struct(bytes_obj):
    return struct.unpack(str(len(bytes_obj)) + 'c', bytes_obj)

@b.add_function()
def Leon(bytes_obj):
    return list(map(bytes, zip(bytes_obj)))

@b.add_function()
def rusu_ro1_format(bytes_obj):
    return [b'%c' % i for i in bytes_obj]

@b.add_function()
def rusu_ro1_numpy(bytes_obj):
    return np.frombuffer(bytes_obj, dtype='S1')

@b.add_function()
def rusu_ro1_numpy_tolist(bytes_obj):
    return np.frombuffer(bytes_obj, dtype='S1').tolist()

@b.add_function()
def User38(bytes_obj):
    return [chr(i).encode() for i in bytes_obj]

@b.add_arguments('byte object length')
def argument_provider():
    for exp in range(2, 18):
        size = 2**exp
        yield size, b'a' * size

r = b.run()
r.plot()
于 2019-08-20T14:20:37.530 に答える
10

Python 3.5以降、バイトとバイト配列に%フォーマットを使用できます。

[b'%c' % i for i in b'123']

出力:

[b'1', b'2', b'3']

上記のソリューションは、最初のアプローチより2〜3倍高速です。より高速なソリューションが必要な場合は、numpy.frombufferを使用することをお勧めします。

import numpy as np
np.frombuffer(b'123', dtype='S1')

出力:

array([b'1', b'2', b'3'], 
      dtype='|S1')

2番目のソリューションはstruct.unpackよりも約10%高速です(ランダムな100バイトに対して、@ snakecharmerbと同じパフォーマンステストを使用しました)

于 2019-08-19T19:28:34.607 に答える
7

のトリオmap()bytes()そしてzip()トリックを行います:

>>> list(map(bytes, zip(b'123')))
[b'1', b'2', b'3']

[bytes([b]) for b in b'123']ただし、これよりも読みやすく、パフォーマンスが優れているとは思いません。

于 2019-08-14T20:34:45.183 に答える
6

私はこのヘルパーメソッドを使用します:

def iter_bytes(my_bytes):
    for i in range(len(my_bytes)):
        yield my_bytes[i:i+1]

Python2およびPython3で動作します。

于 2019-08-14T10:46:07.533 に答える
1

これを行う簡単な方法:

[bytes([i]) for i in b'123\xaa\xbb\xcc\xff']
于 2019-08-18T00:22:35.253 に答える