Pythonでは、バイナリファイルを読み込んで、そのファイルの各バイトをループする方法を教えてください。
Pythonでは、バイナリファイルを読み込んで、そのファイルの各バイトをループする方法を教えてください。
回答:
Python 2.4およびそれ以前
f = open("myfile", "rb")
try:
byte = f.read(1)
while byte != "":
# Do stuff with byte.
byte = f.read(1)
finally:
f.close()
Python 2.5〜2.7
with open("myfile", "rb") as f:
byte = f.read(1)
while byte != "":
# Do stuff with byte.
byte = f.read(1)
withステートメントは、2.5より前のバージョンのPythonでは使用できないことに注意してください。v 2.5で使用するには、インポートする必要があります。
from __future__ import with_statement
2.6では、これは必要ありません。
Python 3
Python 3では少し異なります。バイトモードのストリームから生の文字を取得するのではなく、バイトオブジェクトを取得するため、条件を変更する必要があります。
with open("myfile", "rb") as f:
byte = f.read(1)
while byte != b"":
# Do stuff with byte.
byte = f.read(1)
または、benhoytが言うように、等しくないことをスキップして、b""
評価がfalse であるという事実を利用します。これにより、コードは変更なしで2.6と3.xの間で互換性があります。また、バイトモードからテキストまたはその逆に移行した場合でも、条件を変更する必要がなくなります。
with open("myfile", "rb") as f:
byte = f.read(1)
while byte:
# Do stuff with byte.
byte = f.read(1)
Python 3.8
今から:=演算子のおかげで、上記のコードはより短い方法で書くことができます。
with open("myfile", "rb") as f:
while (byte := f.read(1)):
# Do stuff with byte.
このジェネレーターは、ファイルからバイトを生成し、チャンクでファイルを読み取ります。
def bytes_from_file(filename, chunksize=8192):
with open(filename, "rb") as f:
while True:
chunk = f.read(chunksize)
if chunk:
for b in chunk:
yield b
else:
break
# example:
for b in bytes_from_file('filename'):
do_stuff_with(b)
8192 Byte = 8 kB
(実際にはそうですKiB
が、それほど一般的には知られていません)。値は「完全に」ランダムですが、8 kBが適切な値のようです。メモリが浪費されず、Skurmedelによる承認済みの回答のように、「読み取り」操作が多すぎません...
for b in chunk:
ループ全体をに置き換えることで、さらに20〜25%スピードアップできますyield from chunk
。この形式のyield
はPython 3.3で追加されました(Yield Expressionsを参照)。
ファイルが大きすぎず、メモリに保持することが問題である場合:
with open("filename", "rb") as f:
bytes_read = f.read()
for b in bytes_read:
process_byte(b)
ここで、process_byteは、渡されたバイトに対して実行する操作を表します。
一度にチャンクを処理する場合:
with open("filename", "rb") as f:
bytes_read = f.read(CHUNKSIZE)
while bytes_read:
for b in bytes_read:
process_byte(b)
bytes_read = f.read(CHUNKSIZE)
このwith
ステートメントはPython 2.5以降で使用できます。
ファイルを読み取るには、一度に1バイトずつ(バッファリングを無視して)、2つの引数のiter(callable, sentinel)
組み込み関数を使用できます。
with open(filename, 'rb') as file:
for byte in iter(lambda: file.read(1), b''):
# Do stuff with byte
file.read(1)
何も返さなくなるまでb''
(空のバイト文字列)呼び出します。大きなファイルの場合、メモリが無制限に大きくなることはありません。に渡しbuffering=0
てopen()
、バッファリングを無効にすることができます。これにより、反復ごとに1バイトのみが読み取られることが保証されます(低速)。
with
-statementはファイルを自動的に閉じます—下のコードで例外が発生した場合も含まれます。
デフォルトでは内部バッファリングが存在しますが、一度に1バイトを処理することは依然として非効率的です。たとえば、指定されたblackhole.py
すべてを食べるユーティリティは次のとおりです。
#!/usr/bin/env python3
"""Discard all input. `cat > /dev/null` analog."""
import sys
from functools import partial
from collections import deque
chunksize = int(sys.argv[1]) if len(sys.argv) > 1 else (1 << 15)
deque(iter(partial(sys.stdin.detach().read, chunksize), b''), maxlen=0)
例:
$ dd if=/dev/zero bs=1M count=1000 | python3 blackhole.py
これは、プロセス〜1.5ギガバイト/秒のchunksize == 32768
私のマシンだけに〜7.5メガバイト/秒chunksize == 1
。つまり、一度に1バイトを読み取るのは200倍遅くなります。一度に複数のバイトを使用するように処理を書き換えることができ、パフォーマンスが必要な場合は、この点を考慮してください。
mmap
ファイルをbytearray
とファイルオブジェクトとして同時に扱うことができます。両方のインターフェイスにアクセスする必要がある場合は、メモリにファイル全体をロードする代わりに使用できます。特に、プレーンfor
ループを使用するだけで、メモリマップトファイルに対して一度に1バイトを反復できます。
from mmap import ACCESS_READ, mmap
with open(filename, 'rb', 0) as f, mmap(f.fileno(), 0, access=ACCESS_READ) as s:
for byte in s: # length is equal to the current file size
# Do stuff with byte
mmap
スライス表記をサポートします。たとえば、ファイルの位置から始まるバイトをmm[i:i+len]
返しlen
ますi
。コンテキストマネージャプロトコルは、Python 3.2以前ではサポートされていません。mm.close()
この場合、明示的に呼び出す必要があります。を使用して各バイトを反復処理すると、mmap
よりも多くのメモリが消費されますがfile.read(1)
、mmap
桁違いに高速です。
numpy
メモリマップ(バイト)配列はありません。
numpy.memmap()
、一度に1バイトのデータを取得できます(ctypes.data)。numpy配列は、メモリ内のblob +メタデータに過ぎないと考えることができます。
Pythonでバイナリファイルを読み取り、各バイトをループする
Python 3.5の新機能はpathlib
モジュールです。このモジュールには、ファイルをバイトとして読み取るための便利なメソッドがあり、バイトを反復処理することができます。私はこれを(迅速で汚い場合は)まともな答えだと考えています:
import pathlib
for byte in pathlib.Path(path).read_bytes():
print(byte)
これが言及する唯一の答えであることは興味深いpathlib
。
Python 2では、おそらくこれを行うでしょう(Vinay Sajipも示唆しています):
with open(path, 'b') as file:
for byte in file.read():
print(byte)
ファイルが大きすぎてメモリ内で反復できない場合は、慣用的にiter
、callable, sentinel
署名付きの関数を使用して、チャンクを作成します-Python 2バージョン:
with open(path, 'b') as file:
callable = lambda: file.read(1024)
sentinel = bytes() # or b''
for chunk in iter(callable, sentinel):
for byte in chunk:
print(byte)
(他のいくつかの回答ではこれについて言及していますが、実用的な読み取りサイズを提供する回答はほとんどありません。)
Python 3.5以降の標準ライブラリの慣用的な使用を含め、これを行う関数を作成してみましょう。
from pathlib import Path
from functools import partial
from io import DEFAULT_BUFFER_SIZE
def file_byte_iterator(path):
"""given a path, return an iterator over the file
that lazily loads the file
"""
path = Path(path)
with path.open('rb') as file:
reader = partial(file.read1, DEFAULT_BUFFER_SIZE)
file_iterator = iter(reader, bytes())
for chunk in file_iterator:
yield from chunk
を使用することに注意してくださいfile.read1
。file.read
要求されたすべてのバイトを取得するまでブロックしますEOF
。file.read1
ブロックを回避することができます。これにより、より早く戻ることができます。他の回答もこれについて言及していません。
メガバイト(実際にはメビバイト)の疑似ランダムデータを含むファイルを作成してみましょう。
import random
import pathlib
path = 'pseudorandom_bytes'
pathobj = pathlib.Path(path)
pathobj.write_bytes(
bytes(random.randint(0, 255) for _ in range(2**20)))
今度はそれを繰り返して、メモリに具体化しましょう:
>>> l = list(file_byte_iterator(path))
>>> len(l)
1048576
たとえば、最後の100バイトと最初の100バイトなど、データの任意の部分を検査できます。
>>> l[-100:]
[208, 5, 156, 186, 58, 107, 24, 12, 75, 15, 1, 252, 216, 183, 235, 6, 136, 50, 222, 218, 7, 65, 234, 129, 240, 195, 165, 215, 245, 201, 222, 95, 87, 71, 232, 235, 36, 224, 190, 185, 12, 40, 131, 54, 79, 93, 210, 6, 154, 184, 82, 222, 80, 141, 117, 110, 254, 82, 29, 166, 91, 42, 232, 72, 231, 235, 33, 180, 238, 29, 61, 250, 38, 86, 120, 38, 49, 141, 17, 190, 191, 107, 95, 223, 222, 162, 116, 153, 232, 85, 100, 97, 41, 61, 219, 233, 237, 55, 246, 181]
>>> l[:100]
[28, 172, 79, 126, 36, 99, 103, 191, 146, 225, 24, 48, 113, 187, 48, 185, 31, 142, 216, 187, 27, 146, 215, 61, 111, 218, 171, 4, 160, 250, 110, 51, 128, 106, 3, 10, 116, 123, 128, 31, 73, 152, 58, 49, 184, 223, 17, 176, 166, 195, 6, 35, 206, 206, 39, 231, 89, 249, 21, 112, 168, 4, 88, 169, 215, 132, 255, 168, 129, 127, 60, 252, 244, 160, 80, 155, 246, 147, 234, 227, 157, 137, 101, 84, 115, 103, 77, 44, 84, 134, 140, 77, 224, 176, 242, 254, 171, 115, 193, 29]
次のことは行わないでください。これにより、改行文字に到達するまで任意のサイズのチャンクが引き出されます。チャンクが小さすぎる場合は遅すぎ、場合によっては大きすぎる可能性もあります。
with open(path, 'rb') as file:
for chunk in file: # text newline iteration - not for bytes
yield from chunk
上記は、'b'
フラグなしで開く必要がある意味的に人間が読めるテキストファイル(プレーンテキスト、コード、マークアップ、マークダウンなど、本質的にASCII、UTF、ラテン文字などのエンコードされたもの)にのみ適しています。
path = Path(path), with path.open('rb') as file:
代わりに組み込みのopen関数を使用する代わりに使用することを選択した理由はありますか?どちらも同じことを正しく行いますか?
Path
パスを処理する非常に便利な新しい方法であるため、オブジェクトを使用します。文字列を慎重に選択された「適切な」関数に渡す代わりに、パスオブジェクトのメソッドを呼び出すだけです。これは、本質的に、パス文字列であるものに必要なほとんどの重要な機能を含んでいます。検査可能なIDEを使用すると、オートコンプリートもより簡単に取得できます。open
ビルトインでも同じことができますが、プログラマーがPath
オブジェクトを代わりに使用するためのプログラムを作成する場合、多くの利点があります。
file_byte_iterator
は、このページで試したすべての方法よりもはるかに高速です。どうぞよろしくお願いいたします。
chrispy、Skurmedel、Ben Hoyt、Peter Hansenのすばらしい点をすべてまとめると、バイナリファイルを一度に1バイトずつ処理するには、これが最適なソリューションです。
with open("myfile", "rb") as f:
while True:
byte = f.read(1)
if not byte:
break
do_stuff_with(ord(byte))
Pythonバージョン2.6以降の場合:
または速度を向上させるためにJF Sebastiansソリューションを使用する
from functools import partial
with open(filename, 'rb') as file:
for byte in iter(partial(file.read, 1), b''):
# Do stuff with byte
または、codeapeで示されているようなジェネレーター関数として使用する場合:
def bytes_from_file(filename):
with open(filename, "rb") as f:
while True:
byte = f.read(1)
if not byte:
break
yield(ord(byte))
# example:
for b in bytes_from_file('filename'):
do_stuff_with(b)
上記すべてを試し、@ Aaron Hallからの回答を使用した後、Window 10、8 Gb RAM、Python 3.5 32ビットを実行しているコンピューターで〜90 Mbファイルのメモリエラーが発生しました。使用するよう同僚から勧められましたnumpy
代わりよう、それは不思議に機能します。
(テストした)バイナリファイル全体を読み込むのが最も速いのは次のとおりです。
import numpy as np
file = "binary_file.bin"
data = np.fromfile(file, 'u1')
これまでのところ、他のどの方法よりも高速に群がります。それが誰かを助けることを願っています!
読み取るバイナリデータが多い場合は、構造体モジュールを検討することをお勧めします。「CとPythonの間の型」の変換として記載されていますが、バイトはバイトであり、C型として作成されたかどうかは関係ありません。たとえば、バイナリデータに2つの2バイト整数と1つの4バイト整数が含まれている場合、次のように読み取ることができます(struct
ドキュメントからの例)。
>>> struct.unpack('hhl', b'\x00\x01\x00\x02\x00\x00\x00\x03')
(1, 2, 3)
これは、ファイルのコンテンツを明示的にループするよりも便利、高速、またはその両方である場合があります。
この投稿自体は、質問に対する直接の回答ではありません。代わりに、この質問に投稿されている多くの回答(および、より新しいバージョンのPythonに追加された新機能を利用するためのバリエーション)を比較するために使用できる、データ駆動型の拡張可能なベンチマークです。最高のパフォーマンスを持つものを決定するのに役立ちます。
いくつかのケースでは、参照されている回答のコードを変更して、ベンチマークフレームワークと互換性があるようにしています。
最初に、Python 2および3の現在の最新バージョンの結果を示します。
Fastest to slowest execution speeds with 32-bit Python 2.7.16
numpy version 1.16.5
Test file size: 1,024 KiB
100 executions, best of 3 repetitions
1 Tcll (array.array) : 3.8943 secs, rel speed 1.00x, 0.00% slower (262.95 KiB/sec)
2 Vinay Sajip (read all into memory) : 4.1164 secs, rel speed 1.06x, 5.71% slower (248.76 KiB/sec)
3 codeape + iter + partial : 4.1616 secs, rel speed 1.07x, 6.87% slower (246.06 KiB/sec)
4 codeape : 4.1889 secs, rel speed 1.08x, 7.57% slower (244.46 KiB/sec)
5 Vinay Sajip (chunked) : 4.1977 secs, rel speed 1.08x, 7.79% slower (243.94 KiB/sec)
6 Aaron Hall (Py 2 version) : 4.2417 secs, rel speed 1.09x, 8.92% slower (241.41 KiB/sec)
7 gerrit (struct) : 4.2561 secs, rel speed 1.09x, 9.29% slower (240.59 KiB/sec)
8 Rick M. (numpy) : 8.1398 secs, rel speed 2.09x, 109.02% slower (125.80 KiB/sec)
9 Skurmedel : 31.3264 secs, rel speed 8.04x, 704.42% slower ( 32.69 KiB/sec)
Benchmark runtime (min:sec) - 03:26
Fastest to slowest execution speeds with 32-bit Python 3.8.0
numpy version 1.17.4
Test file size: 1,024 KiB
100 executions, best of 3 repetitions
1 Vinay Sajip + "yield from" + "walrus operator" : 3.5235 secs, rel speed 1.00x, 0.00% slower (290.62 KiB/sec)
2 Aaron Hall + "yield from" : 3.5284 secs, rel speed 1.00x, 0.14% slower (290.22 KiB/sec)
3 codeape + iter + partial + "yield from" : 3.5303 secs, rel speed 1.00x, 0.19% slower (290.06 KiB/sec)
4 Vinay Sajip + "yield from" : 3.5312 secs, rel speed 1.00x, 0.22% slower (289.99 KiB/sec)
5 codeape + "yield from" + "walrus operator" : 3.5370 secs, rel speed 1.00x, 0.38% slower (289.51 KiB/sec)
6 codeape + "yield from" : 3.5390 secs, rel speed 1.00x, 0.44% slower (289.35 KiB/sec)
7 jfs (mmap) : 4.0612 secs, rel speed 1.15x, 15.26% slower (252.14 KiB/sec)
8 Vinay Sajip (read all into memory) : 4.5948 secs, rel speed 1.30x, 30.40% slower (222.86 KiB/sec)
9 codeape + iter + partial : 4.5994 secs, rel speed 1.31x, 30.54% slower (222.64 KiB/sec)
10 codeape : 4.5995 secs, rel speed 1.31x, 30.54% slower (222.63 KiB/sec)
11 Vinay Sajip (chunked) : 4.6110 secs, rel speed 1.31x, 30.87% slower (222.08 KiB/sec)
12 Aaron Hall (Py 2 version) : 4.6292 secs, rel speed 1.31x, 31.38% slower (221.20 KiB/sec)
13 Tcll (array.array) : 4.8627 secs, rel speed 1.38x, 38.01% slower (210.58 KiB/sec)
14 gerrit (struct) : 5.0816 secs, rel speed 1.44x, 44.22% slower (201.51 KiB/sec)
15 Rick M. (numpy) + "yield from" : 11.8084 secs, rel speed 3.35x, 235.13% slower ( 86.72 KiB/sec)
16 Skurmedel : 11.8806 secs, rel speed 3.37x, 237.18% slower ( 86.19 KiB/sec)
17 Rick M. (numpy) : 13.3860 secs, rel speed 3.80x, 279.91% slower ( 76.50 KiB/sec)
Benchmark runtime (min:sec) - 04:47
また、はるかに大きな10 MiBテストファイル(実行に約1時間かかりました)を使用して実行したところ、上記と同じパフォーマンス結果が得られました。
ベンチマークを行うために使用されるコードは次のとおりです。
from __future__ import print_function
import array
import atexit
from collections import deque, namedtuple
import io
from mmap import ACCESS_READ, mmap
import numpy as np
from operator import attrgetter
import os
import random
import struct
import sys
import tempfile
from textwrap import dedent
import time
import timeit
import traceback
try:
xrange
except NameError: # Python 3
xrange = range
class KiB(int):
""" KibiBytes - multiples of the byte units for quantities of information. """
def __new__(self, value=0):
return 1024*value
BIG_TEST_FILE = 1 # MiBs or 0 for a small file.
SML_TEST_FILE = KiB(64)
EXECUTIONS = 100 # Number of times each "algorithm" is executed per timing run.
TIMINGS = 3 # Number of timing runs.
CHUNK_SIZE = KiB(8)
if BIG_TEST_FILE:
FILE_SIZE = KiB(1024) * BIG_TEST_FILE
else:
FILE_SIZE = SML_TEST_FILE # For quicker testing.
# Common setup for all algorithms -- prefixed to each algorithm's setup.
COMMON_SETUP = dedent("""
# Make accessible in algorithms.
from __main__ import array, deque, get_buffer_size, mmap, np, struct
from __main__ import ACCESS_READ, CHUNK_SIZE, FILE_SIZE, TEMP_FILENAME
from functools import partial
try:
xrange
except NameError: # Python 3
xrange = range
""")
def get_buffer_size(path):
""" Determine optimal buffer size for reading files. """
st = os.stat(path)
try:
bufsize = st.st_blksize # Available on some Unix systems (like Linux)
except AttributeError:
bufsize = io.DEFAULT_BUFFER_SIZE
return bufsize
# Utility primarily for use when embedding additional algorithms into benchmark.
VERIFY_NUM_READ = """
# Verify generator reads correct number of bytes (assumes values are correct).
bytes_read = sum(1 for _ in file_byte_iterator(TEMP_FILENAME))
assert bytes_read == FILE_SIZE, \
'Wrong number of bytes generated: got {:,} instead of {:,}'.format(
bytes_read, FILE_SIZE)
"""
TIMING = namedtuple('TIMING', 'label, exec_time')
class Algorithm(namedtuple('CodeFragments', 'setup, test')):
# Default timeit "stmt" code fragment.
_TEST = """
#for b in file_byte_iterator(TEMP_FILENAME): # Loop over every byte.
# pass # Do stuff with byte...
deque(file_byte_iterator(TEMP_FILENAME), maxlen=0) # Data sink.
"""
# Must overload __new__ because (named)tuples are immutable.
def __new__(cls, setup, test=None):
""" Dedent (unindent) code fragment string arguments.
Args:
`setup` -- Code fragment that defines things used by `test` code.
In this case it should define a generator function named
`file_byte_iterator()` that will be passed that name of a test file
of binary data. This code is not timed.
`test` -- Code fragment that uses things defined in `setup` code.
Defaults to _TEST. This is the code that's timed.
"""
test = cls._TEST if test is None else test # Use default unless one is provided.
# Uncomment to replace all performance tests with one that verifies the correct
# number of bytes values are being generated by the file_byte_iterator function.
#test = VERIFY_NUM_READ
return tuple.__new__(cls, (dedent(setup), dedent(test)))
algorithms = {
'Aaron Hall (Py 2 version)': Algorithm("""
def file_byte_iterator(path):
with open(path, "rb") as file:
callable = partial(file.read, 1024)
sentinel = bytes() # or b''
for chunk in iter(callable, sentinel):
for byte in chunk:
yield byte
"""),
"codeape": Algorithm("""
def file_byte_iterator(filename, chunksize=CHUNK_SIZE):
with open(filename, "rb") as f:
while True:
chunk = f.read(chunksize)
if chunk:
for b in chunk:
yield b
else:
break
"""),
"codeape + iter + partial": Algorithm("""
def file_byte_iterator(filename, chunksize=CHUNK_SIZE):
with open(filename, "rb") as f:
for chunk in iter(partial(f.read, chunksize), b''):
for b in chunk:
yield b
"""),
"gerrit (struct)": Algorithm("""
def file_byte_iterator(filename):
with open(filename, "rb") as f:
fmt = '{}B'.format(FILE_SIZE) # Reads entire file at once.
for b in struct.unpack(fmt, f.read()):
yield b
"""),
'Rick M. (numpy)': Algorithm("""
def file_byte_iterator(filename):
for byte in np.fromfile(filename, 'u1'):
yield byte
"""),
"Skurmedel": Algorithm("""
def file_byte_iterator(filename):
with open(filename, "rb") as f:
byte = f.read(1)
while byte:
yield byte
byte = f.read(1)
"""),
"Tcll (array.array)": Algorithm("""
def file_byte_iterator(filename):
with open(filename, "rb") as f:
arr = array.array('B')
arr.fromfile(f, FILE_SIZE) # Reads entire file at once.
for b in arr:
yield b
"""),
"Vinay Sajip (read all into memory)": Algorithm("""
def file_byte_iterator(filename):
with open(filename, "rb") as f:
bytes_read = f.read() # Reads entire file at once.
for b in bytes_read:
yield b
"""),
"Vinay Sajip (chunked)": Algorithm("""
def file_byte_iterator(filename, chunksize=CHUNK_SIZE):
with open(filename, "rb") as f:
chunk = f.read(chunksize)
while chunk:
for b in chunk:
yield b
chunk = f.read(chunksize)
"""),
} # End algorithms
#
# Versions of algorithms that will only work in certain releases (or better) of Python.
#
if sys.version_info >= (3, 3):
algorithms.update({
'codeape + iter + partial + "yield from"': Algorithm("""
def file_byte_iterator(filename, chunksize=CHUNK_SIZE):
with open(filename, "rb") as f:
for chunk in iter(partial(f.read, chunksize), b''):
yield from chunk
"""),
'codeape + "yield from"': Algorithm("""
def file_byte_iterator(filename, chunksize=CHUNK_SIZE):
with open(filename, "rb") as f:
while True:
chunk = f.read(chunksize)
if chunk:
yield from chunk
else:
break
"""),
"jfs (mmap)": Algorithm("""
def file_byte_iterator(filename):
with open(filename, "rb") as f, \
mmap(f.fileno(), 0, access=ACCESS_READ) as s:
yield from s
"""),
'Rick M. (numpy) + "yield from"': Algorithm("""
def file_byte_iterator(filename):
# data = np.fromfile(filename, 'u1')
yield from np.fromfile(filename, 'u1')
"""),
'Vinay Sajip + "yield from"': Algorithm("""
def file_byte_iterator(filename, chunksize=CHUNK_SIZE):
with open(filename, "rb") as f:
chunk = f.read(chunksize)
while chunk:
yield from chunk # Added in Py 3.3
chunk = f.read(chunksize)
"""),
}) # End Python 3.3 update.
if sys.version_info >= (3, 5):
algorithms.update({
'Aaron Hall + "yield from"': Algorithm("""
from pathlib import Path
def file_byte_iterator(path):
''' Given a path, return an iterator over the file
that lazily loads the file.
'''
path = Path(path)
bufsize = get_buffer_size(path)
with path.open('rb') as file:
reader = partial(file.read1, bufsize)
for chunk in iter(reader, bytes()):
yield from chunk
"""),
}) # End Python 3.5 update.
if sys.version_info >= (3, 8, 0):
algorithms.update({
'Vinay Sajip + "yield from" + "walrus operator"': Algorithm("""
def file_byte_iterator(filename, chunksize=CHUNK_SIZE):
with open(filename, "rb") as f:
while chunk := f.read(chunksize):
yield from chunk # Added in Py 3.3
"""),
'codeape + "yield from" + "walrus operator"': Algorithm("""
def file_byte_iterator(filename, chunksize=CHUNK_SIZE):
with open(filename, "rb") as f:
while chunk := f.read(chunksize):
yield from chunk
"""),
}) # End Python 3.8.0 update.update.
#### Main ####
def main():
global TEMP_FILENAME
def cleanup():
""" Clean up after testing is completed. """
try:
os.remove(TEMP_FILENAME) # Delete the temporary file.
except Exception:
pass
atexit.register(cleanup)
# Create a named temporary binary file of pseudo-random bytes for testing.
fd, TEMP_FILENAME = tempfile.mkstemp('.bin')
with os.fdopen(fd, 'wb') as file:
os.write(fd, bytearray(random.randrange(256) for _ in range(FILE_SIZE)))
# Execute and time each algorithm, gather results.
start_time = time.time() # To determine how long testing itself takes.
timings = []
for label in algorithms:
try:
timing = TIMING(label,
min(timeit.repeat(algorithms[label].test,
setup=COMMON_SETUP + algorithms[label].setup,
repeat=TIMINGS, number=EXECUTIONS)))
except Exception as exc:
print('{} occurred timing the algorithm: "{}"\n {}'.format(
type(exc).__name__, label, exc))
traceback.print_exc(file=sys.stdout) # Redirect to stdout.
sys.exit(1)
timings.append(timing)
# Report results.
print('Fastest to slowest execution speeds with {}-bit Python {}.{}.{}'.format(
64 if sys.maxsize > 2**32 else 32, *sys.version_info[:3]))
print(' numpy version {}'.format(np.version.full_version))
print(' Test file size: {:,} KiB'.format(FILE_SIZE // KiB(1)))
print(' {:,d} executions, best of {:d} repetitions'.format(EXECUTIONS, TIMINGS))
print()
longest = max(len(timing.label) for timing in timings) # Len of longest identifier.
ranked = sorted(timings, key=attrgetter('exec_time')) # Sort so fastest is first.
fastest = ranked[0].exec_time
for rank, timing in enumerate(ranked, 1):
print('{:<2d} {:>{width}} : {:8.4f} secs, rel speed {:6.2f}x, {:6.2f}% slower '
'({:6.2f} KiB/sec)'.format(
rank,
timing.label, timing.exec_time, round(timing.exec_time/fastest, 2),
round((timing.exec_time/fastest - 1) * 100, 2),
(FILE_SIZE/timing.exec_time) / KiB(1), # per sec.
width=longest))
print()
mins, secs = divmod(time.time()-start_time, 60)
print('Benchmark runtime (min:sec) - {:02d}:{:02d}'.format(int(mins),
int(round(secs))))
main()
yield from chunk
代わりに私がやると思いますfor byte in chunk: yield byte
か?私はそれで私の答えをきつく締めるべきだと思っています。
yield from
。
enumerate
、反復が完了したことを理解する必要があるため、ドロップすることをお勧めします。完了していない場合は、最後に確認しました-列挙には、+ = 1でインデックスの簿記を実行する場合のコストと多少のオーバーヘッドがあるため、代わりに独自のコード。または、で両端キューに渡すこともできmaxlen=0
ます。
enumerate
ます。フィードバックをお寄せいただきありがとうございます。それがない私の投稿に更新を追加します(ただし、結果が大きく変わるとは思いません)。@Rick M.のnumpy
ベースの回答も追加されます。
super().
代わりにを使用するとtuple.
、インデックスの代わりに属性名を__new__
使用できると思いnamedtuple
ます。
あなたがスピーディーな何かを探しているなら、ここに私が長年働いてきた私が使ってきた方法があります:
from array import array
with open( path, 'rb' ) as file:
data = array( 'B', file.read() ) # buffer the file
# evaluate it's data
for byte in data:
v = byte # int value
c = chr(byte)
intの代わりにcharを反復処理したい場合は、単に使用できますdata = file.read()
。これは、py3のbytes()オブジェクトである必要があります。