Escribiendo enormes cuerdas en python.

Tengo una cadena muy larga, de casi un megabyte de largo, que necesito escribir en un archivo de texto. El regular

file = open("file.txt","w") file.write(string) file.close() 

funciona pero es muy lento, ¿hay alguna manera de escribir más rápido?

Estoy tratando de escribir un número de varios millones de dígitos en un archivo de texto, el número es del orden de math.factorial(67867957)

Esto es lo que se muestra en el perfil:

  203 function calls (198 primitive calls) in 0.001 seconds Ordered by: standard name ncalls tottime percall cumtime percall filename:lineno(function) 1 0.000 0.000 0.000 0.000 :1() 1 0.000 0.000 0.000 0.000 re.py:217(compile) 1 0.000 0.000 0.000 0.000 re.py:273(_compile) 1 0.000 0.000 0.000 0.000 sre_compile.py:172(_compile_charset) 1 0.000 0.000 0.000 0.000 sre_compile.py:201(_optimize_charset) 4 0.000 0.000 0.000 0.000 sre_compile.py:25(_identityfunction) 3/1 0.000 0.000 0.000 0.000 sre_compile.py:33(_compile) 1 0.000 0.000 0.000 0.000 sre_compile.py:341(_compile_info) 2 0.000 0.000 0.000 0.000 sre_compile.py:442(isstring) 1 0.000 0.000 0.000 0.000 sre_compile.py:445(_code) 1 0.000 0.000 0.000 0.000 sre_compile.py:460(compile) 5 0.000 0.000 0.000 0.000 sre_parse.py:126(__len__) 12 0.000 0.000 0.000 0.000 sre_parse.py:130(__getitem__) 7 0.000 0.000 0.000 0.000 sre_parse.py:138(append) 3/1 0.000 0.000 0.000 0.000 sre_parse.py:140(getwidth) 1 0.000 0.000 0.000 0.000 sre_parse.py:178(__init__) 10 0.000 0.000 0.000 0.000 sre_parse.py:183(__next) 2 0.000 0.000 0.000 0.000 sre_parse.py:202(match) 8 0.000 0.000 0.000 0.000 sre_parse.py:208(get) 1 0.000 0.000 0.000 0.000 sre_parse.py:351(_parse_sub) 2 0.000 0.000 0.000 0.000 sre_parse.py:429(_parse) 1 0.000 0.000 0.000 0.000 sre_parse.py:67(__init__) 1 0.000 0.000 0.000 0.000 sre_parse.py:726(fix_flags) 1 0.000 0.000 0.000 0.000 sre_parse.py:738(parse) 3 0.000 0.000 0.000 0.000 sre_parse.py:90(__init__) 1 0.000 0.000 0.000 0.000 {built-in method compile} 1 0.001 0.001 0.001 0.001 {built-in method exec} 17 0.000 0.000 0.000 0.000 {built-in method isinstance} 39/38 0.000 0.000 0.000 0.000 {built-in method len} 2 0.000 0.000 0.000 0.000 {built-in method max} 8 0.000 0.000 0.000 0.000 {built-in method min} 6 0.000 0.000 0.000 0.000 {built-in method ord} 48 0.000 0.000 0.000 0.000 {method 'append' of 'list' objects} 1 0.000 0.000 0.000 0.000 {method 'disable' of '_lsprof.Profiler' objects} 5 0.000 0.000 0.000 0.000 {method 'find' of 'bytearray' objects} 1 0.000 0.000 0.000 0.000 {method 'items' of 'dict' objects} 

Su problema es que str(long) es muy lento para los intergers grandes (millones de dígitos) en Python. Es una operación cuadrática (en número de dígitos) en Python , es decir, para ~ 1e8 dígitos puede requerir ~ 1e16 operaciones para convertir el número entero en una cadena decimal.

Escribir en un archivo de 500 MB no debería llevar horas, por ejemplo:

 $ python3 -c 'open("file", "w").write("a"*500*1000000)' 

Vuelve casi de inmediato. ls -l file confirma que el archivo está creado y tiene el tamaño esperado.

El cálculo de math.factorial(67867957) (el resultado tiene ~ 500M dígitos) puede llevar varias horas, pero guardarlo con pickle es instantáneo:

 import math import pickle n = math.factorial(67867957) # takes a long time with open("file.pickle", "wb") as file: pickle.dump(n, file) # very fast (comparatively) 

Para volver a cargarlo utilizando n = pickle.load(open('file.pickle', 'rb')) toma menos de un segundo.

str(n) sigue funcionando (después de 50 horas) en mi máquina.

Para obtener la representación decimal rápida, puedes usar gmpy2 :

 $ python -c'import gmpy2;open("file.gmpy2", "w").write(str(gmpy2.fac(67867957)))' 

Lleva menos de 10 minutos en mi máquina.

bien, esto realmente no es una respuesta, es más que probar su razonamiento para el retraso incorrecto

Primera prueba de velocidad de escritura de una cadena grande.

  import timeit def write_big_str(n_bytes=1000000): with open("test_file.txt","wb") as f: f.write("a"*n_bytes) print timeit.timeit("write_big_str()","from __main__ import write_big_str",number=100) 

deberías ver una velocidad bastante respetable (y eso es repetirla 100 veces)

A continuación, veremos cuánto tiempo se tarda en convertir un número muy grande en un str

 import timeit,math n = math.factorial(200000) print timeit.timeit("str(n)","from __main__ import n",number=1) 

probablemente tomará ~ 10 segundos (y eso es un número de un millón de dígitos), lo que se otorga es lento … pero no de horas lento (bueno, es bastante lento para convertir a cadena: P … pero aún así no debería tomar horas) (bueno, Tomé más como 243 segundos para mi caja, supongo: P)