En Python, ¿cómo leo en un archivo binario y hago un bucle sobre cada byte de ese archivo?
En Python, ¿cómo leo en un archivo binario y hago un bucle sobre cada byte de ese archivo?
Respuestas:
Python 2.4 y anteriores
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)
Tenga en cuenta que la declaración with no está disponible en versiones de Python inferiores a 2.5. Para usarlo en v 2.5 necesitarás importarlo:
from __future__ import with_statement
En 2.6 esto no es necesario.
Python 3
En Python 3, es un poco diferente. Ya no obtendremos caracteres sin procesar de la secuencia en modo byte sino objetos de byte, por lo tanto, debemos modificar la condición:
with open("myfile", "rb") as f:
byte = f.read(1)
while byte != b"":
# Do stuff with byte.
byte = f.read(1)
O como dice benhoyt, omita lo no igual y aproveche el hecho de que se b""
evalúa como falso. Esto hace que el código sea compatible entre 2.6 y 3.x sin ningún cambio. También le evitaría cambiar la condición si pasa del modo byte al texto o al revés.
with open("myfile", "rb") as f:
byte = f.read(1)
while byte:
# Do stuff with byte.
byte = f.read(1)
python 3.8
De ahora en adelante gracias a: = operador, el código anterior se puede escribir de una manera más corta.
with open("myfile", "rb") as f:
while (byte := f.read(1)):
# Do stuff with byte.
Este generador produce bytes de un archivo, leyendo el archivo en fragmentos:
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)
Consulte la documentación de Python para obtener información sobre iteradores y generadores .
8192 Byte = 8 kB
(en realidad es, KiB
pero eso no es tan conocido). El valor es "totalmente" aleatorio, pero 8 kB parece ser un valor apropiado: no se desperdicia demasiada memoria y todavía no hay "demasiadas" operaciones de lectura como en la respuesta aceptada por Skurmedel ...
for b in chunk:
ciclo más interno con yield from chunk
. Esta forma de yield
se agregó en Python 3.3 (ver Expresiones de rendimiento ).
Si el archivo no es demasiado grande, mantenerlo en la memoria es un problema:
with open("filename", "rb") as f:
bytes_read = f.read()
for b in bytes_read:
process_byte(b)
donde process_byte representa alguna operación que desea realizar en el byte pasado.
Si desea procesar un fragmento a la vez:
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)
La with
declaración está disponible en Python 2.5 y superior.
Para leer un archivo, un byte a la vez (ignorando el almacenamiento en búfer), puede usar la función incorporada de dos argumentositer(callable, sentinel)
:
with open(filename, 'rb') as file:
for byte in iter(lambda: file.read(1), b''):
# Do stuff with byte
Llama file.read(1)
hasta que no devuelve nada b''
(cadena de bytes vacía). La memoria no crece de forma ilimitada para archivos grandes. Puede pasar buffering=0
a open()
, para deshabilitar el almacenamiento en búfer: garantiza que solo se lea un byte por iteración (lento).
with
-declaración cierra el archivo automáticamente, incluido el caso en que el código debajo genera una excepción.
A pesar de la presencia de almacenamiento en búfer interno de forma predeterminada, sigue siendo ineficiente procesar un byte a la vez. Por ejemplo, aquí está la blackhole.py
utilidad que se come todo lo que se le da:
#!/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)
Ejemplo:
$ dd if=/dev/zero bs=1M count=1000 | python3 blackhole.py
Procesa ~ 1.5 GB / s cuando está chunksize == 32768
en mi máquina y solo ~ 7.5 MB / s cuando chunksize == 1
. Es decir, es 200 veces más lento leer un byte a la vez. Téngalo en cuenta si puede reescribir su procesamiento para usar más de un byte a la vez y si necesita rendimiento.
mmap
le permite tratar un archivo como un bytearray
objeto de archivo y simultáneamente. Puede servir como una alternativa a cargar todo el archivo en la memoria si necesita acceder a ambas interfaces. En particular, puede iterar un byte a la vez sobre un archivo mapeado en memoria simplemente usando un for
bucle simple :
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
admite la notación de corte. Por ejemplo, mm[i:i+len]
devuelve len
bytes del archivo comenzando en la posición i
. El protocolo del administrador de contexto no es compatible antes de Python 3.2; necesita llamar mm.close()
explícitamente en este caso. Iterar sobre cada byte usando mmap
consume más memoria que file.read(1)
, pero mmap
es un orden de magnitud más rápido.
numpy
matrices equivalentes asignadas en memoria (byte).
numpy.memmap()
y puede obtener los datos de un byte a la vez (ctypes.data). Se podría pensar en matrices numpy como solo un poco más que blobs en memoria + metadatos.
Leer un archivo binario en Python y recorrer cada byte
Nuevo en Python 3.5 es el pathlib
módulo, que tiene un método conveniente específicamente para leer en un archivo como bytes, lo que nos permite iterar sobre los bytes. Considero que esta es una respuesta decente (aunque rápida y sucia):
import pathlib
for byte in pathlib.Path(path).read_bytes():
print(byte)
Es interesante que esta sea la única respuesta para mencionar pathlib
.
En Python 2, probablemente harías esto (como también sugiere Vinay Sajip):
with open(path, 'b') as file:
for byte in file.read():
print(byte)
En el caso de que el archivo sea demasiado grande para iterar sobre la memoria, lo fragmentaría, idiomáticamente, utilizando la iter
función con la callable, sentinel
firma, la versión de 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)
(Varias otras respuestas mencionan esto, pero pocas ofrecen un tamaño de lectura razonable).
Creemos una función para hacer esto, incluidos los usos idiomáticos de la biblioteca estándar para 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
Tenga en cuenta que usamos file.read1
. file.read
bloquea hasta que obtiene todos los bytes solicitados o EOF
. file.read1
nos permite evitar el bloqueo, y puede regresar más rápidamente debido a esto. Ninguna otra respuesta menciona esto también.
Hagamos un archivo con un megabyte (en realidad mebibyte) de datos pseudoaleatorios:
import random
import pathlib
path = 'pseudorandom_bytes'
pathobj = pathlib.Path(path)
pathobj.write_bytes(
bytes(random.randint(0, 255) for _ in range(2**20)))
Ahora vamos a iterar sobre él y materializarlo en la memoria:
>>> l = list(file_byte_iterator(path))
>>> len(l)
1048576
Podemos inspeccionar cualquier parte de los datos, por ejemplo, los últimos 100 y los primeros 100 bytes:
>>> 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]
No haga lo siguiente: esto extrae un trozo de tamaño arbitrario hasta que llega a un carácter de nueva línea, demasiado lento cuando los trozos son demasiado pequeños y posiblemente demasiado grandes también:
with open(path, 'rb') as file:
for chunk in file: # text newline iteration - not for bytes
yield from chunk
Lo anterior solo es bueno para lo que son archivos de texto legibles semánticamente humanos (como texto plano, código, marcado, marcado, etc., esencialmente cualquier cosa codificada en ASCII, UTF, latín, etc.) que debe abrir sin la 'b'
bandera.
path = Path(path), with path.open('rb') as file:
lugar de usar la función abierta incorporada? Ambos hacen lo mismo, ¿correcto?
Path
objeto porque es una nueva forma muy conveniente de manejar rutas. En lugar de pasar una cadena a las funciones "correctas" cuidadosamente elegidas, simplemente podemos llamar a los métodos en el objeto de ruta, que esencialmente contiene la mayor parte de la funcionalidad importante que desea con lo que semánticamente es una cadena de ruta. Con IDEs que pueden inspeccionar, también podemos obtener más fácilmente el autocompletado. Podríamos lograr lo mismo con el open
incorporado, pero hay muchas ventajas al escribir el programa para que el programador use el Path
objeto en su lugar.
file_byte_iterator
es mucho más rápido que todos los métodos que he probado en esta página. ¡Felicitaciones a usted!
Para resumir todos los puntos brillantes de chrispy, Skurmedel, Ben Hoyt y Peter Hansen, esta sería la solución óptima para procesar un archivo binario de un byte a la vez:
with open("myfile", "rb") as f:
while True:
byte = f.read(1)
if not byte:
break
do_stuff_with(ord(byte))
Para python versiones 2.6 y superiores, porque:
O use la solución JF Sebastians para mejorar la velocidad
from functools import partial
with open(filename, 'rb') as file:
for byte in iter(partial(file.read, 1), b''):
# Do stuff with byte
O si lo desea como una función generadora como lo demuestra 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)
Después de probar todo lo anterior y usar la respuesta de @Aaron Hall, recibía errores de memoria para un archivo de ~ 90 Mb en una computadora con Windows 10, 8 Gb RAM y Python 3.5 de 32 bits. Un colega me recomendó usar numpy
en su lugar y funciona de maravilla.
De lejos, el más rápido para leer un archivo binario completo (que he probado) es:
import numpy as np
file = "binary_file.bin"
data = np.fromfile(file, 'u1')
Multitudes más rápidas que cualquier otro método hasta ahora. Espero que ayude a alguien!
numpy
, entonces podría valer la pena.
Si tiene muchos datos binarios para leer, puede considerar el módulo de estructura . Está documentado como una conversión "entre tipos C y Python", pero por supuesto, los bytes son bytes, y si esos fueron creados como tipos C no importa. Por ejemplo, si sus datos binarios contienen dos enteros de 2 bytes y un entero de 4 bytes, puede leerlos de la siguiente manera (ejemplo tomado de la struct
documentación):
>>> struct.unpack('hhl', b'\x00\x01\x00\x02\x00\x00\x00\x03')
(1, 2, 3)
Puede encontrar esto más conveniente, más rápido o ambos, que hacer un bucle explícito sobre el contenido de un archivo.
Esta publicación en sí no es una respuesta directa a la pregunta. En cambio, es un punto de referencia extensible basado en datos que se puede utilizar para comparar muchas de las respuestas (y las variaciones de la utilización de nuevas características agregadas en versiones posteriores, más modernas, de Python) que se han publicado en esta pregunta, y por lo tanto deberían Ser útil para determinar cuál tiene el mejor rendimiento.
En algunos casos, modifiqué el código en la respuesta referenciada para que sea compatible con el marco de referencia.
Primero, aquí están los resultados de lo que actualmente son las últimas versiones de Python 2 y 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
También lo ejecuté con un archivo de prueba de 10 MiB mucho más grande (que tardó casi una hora en ejecutarse) y obtuve resultados de rendimiento que eran comparables a los mostrados anteriormente.
Aquí está el código utilizado para hacer la evaluación comparativa:
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
lugar for byte in chunk: yield byte
? Estoy pensando que debería ajustar mi respuesta con eso.
yield from
.
enumerate
ya que debe entenderse que la iteración se completa; si no, la última vez que lo verifiqué, enumerate tiene un poco de sobrecarga con los costos sobre la contabilidad para el índice con + = 1, por lo que alternativamente puede hacer la contabilidad en su código propio O incluso pasar a una deque con maxlen=0
.
enumerate
. Gracias por la respuesta. Agregaré una actualización a mi publicación que no la tiene (aunque no creo que cambie mucho los resultados). También agregará la numpy
respuesta basada en @Rick M.
super().
lugar de tuple.
en su __new__
podría usar los namedtuple
nombres de los atributos en lugar de los índices.
Si está buscando algo rápido, aquí hay un método que he estado usando que funcionó durante años:
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)
si desea iterar caracteres en lugar de ints, simplemente puede usar data = file.read()
, que debería ser un objeto bytes () en py3.