Computación perezosa en la vida cotidiana

Y aunque las personas que usan scripts de python para escribir una lista de compras o compilan datos de alquiler compilan por cabeza, pero si sucede que usas scripts para resolver tareas rutinarias y, a veces, los scripts funcionan durante un tiempo inaceptablemente largo, entonces la idea es usar cálculos perezosos a todo lo que se mueva, te gustará.


En los capítulos anteriores de mi carta, di una descripción gratuita del funcionamiento de la biblioteca evalcache.
Enlace: Almacenamiento en caché de disco de árboles de computación diferida


Para no aburrirlo con la necesidad de estudiar ese material antes de leer esto, un breve resumen de la última parte:


evalcache envuelve datos, funciones y métodos en objetos perezosos. Cada objeto vago tiene una clave hash especial caracterizada por el método de su construcción. Las operaciones se pueden realizar en objetos perezosos, lo que lleva a la generación de nuevos objetos perezosos. Estas operaciones se ven exactamente como operaciones en datos ordinarios, pero en realidad no se realizan cálculos; en cambio, se construye un árbol de objetos perezosos que se refieren entre sí, recordando sus operaciones y sus argumentos. Si es necesario obtener datos, se realiza una operación para abrir el objeto vago, que activa la cadena de cálculos o extrae el resultado del caché, si el objeto con esta clave se calculó antes.


Sobre algunos cambios


Desde que escribí el último artículo, evalcache ha obtenido algunos mecanismos adicionales.


Mecánica de ejecución no almacenada en caché


Resulta que el hash de un objeto perezoso es algo tan útil que desea usarlo en una situación en la que el almacenamiento en caché del objeto en sí es imposible e innecesario.


Se ha introducido una sintaxis especial para este propósito:


lazyhash = evalcache.LazyHash() #: #evalcache.Lazy(self, cache=None, fastdo=True) @lazyhash def foo(): ... 

En esta versión, el objeto se calcula inmediatamente en el momento de la creación, pero de todos modos se devuelve un archivo diferido. Esto le permite construir árboles de cálculo sin la necesidad de almacenar en caché algunos objetos.


Mecánica de divulgación implícita


La divulgación implícita es el comportamiento esperado de un recordatorio. Aunque evalcache se diseñó originalmente no para la memorización, sino para trabajar con árboles de cálculo, se puede lograr una divulgación implícita basada en algoritmos de evalcache. Se onuse introducido dos nuevas opciones en el onplace y en onuse para esto. onplace conduce a la divulgación del objeto perezoso inmediatamente después de la creación, y se usa cuando se intenta usarlo en algunas de las operaciones permitidas para el objeto perezoso.


 import evalcache lazy = evalcache.Lazy(cache={}, onuse=True) #lazy = evalcache.Lazy(cache={}, onplace=True) ###   . #lazy = evalcache.Memoize() ###   . #lazy = evalcache.Memoize(onplace=True) ###   . @lazy def fib(n): if n < 2: return n return fib(n - 1) + fib(n - 2) for i in range(0,100): print(fib(i)) 

Pero no estamos hablando de esta adición innecesaria, diseñada para hacer que la biblioteca sea un poco más similar a otros lenificadores. Y sobre archivos perezosos:


Archivos perezosos


Evalcache contiene un complemento para lenificar funciones que generan archivos. Inicialmente, se suponía que esta funcionalidad se utilizaría para lenificar la generación de capturas de pantalla. Como resultó más tarde, utilizando la mecánica de los archivos perezosos, puede hacer otras cosas interesantes.


 import evalcache import evalcache.lazyfile lazyfile = evalcache.lazyfile.LazyFile(cache = evalcache.DirCache(".evalfile")) @lazyfile(field="path") def foo(data, path): f = open(path, "w") f.write(data) f.close() foo("HelloWorld","data.dat") 

¿Cómo funciona?
En general, la lógica del trabajo es la misma que con todos los objetos perezosos.
foo("HelloWorld","data.dat") Comienza a construir un objeto vago cuya clave hash está vinculada a los argumentos que se le pasan. Después de eso, se aplica la mecánica de la divulgación implícita, lo que lleva a un inicio instantáneo del cálculo.


Pero luego cambia el curso de acción.
@lazyfile(field="path") decorador lazyfile analiza un parámetro con el nombre especificado en el campo. El decorador espera que, tras la ejecución de la función, se cree un archivo a lo largo de esta ruta. evalcache toma este archivo y crea un enlace rígido en el directorio hash ".evalfile" . El nombre del archivo de enlace duro corresponde a la clave hash del objeto vago. Más tarde, cuando un archivo con ese nombre está en el caché, evalcache, al expandir el objeto en lugar de llamar a la función, simplemente crea un enlace fijo en el lugar requerido para el archivo existente en el caché.


Es útil que un archivo diferido sea un objeto diferido ordinario, y se pueden usar otros objetos diferidos para generarlo.


 import evalcache import evalcache.lazyfile lazy = evalcache.lazy.Lazy(cache = evalcache.DirCache(".evalcache")) lazyfile = evalcache.lazyfile.LazyFile(cache = evalcache.DirCache(".evalfile")) @lazyfile(field="path") def foo(data, path): f = open(path, "w") f.write(data) f.close() @lazy def datagenerator(): return "HelloWorld" foo(datagenerator(),"data.dat") 

Aplicando lenificación a la edición de video a través de la herramienta moviepy.


Como sabes, cualquier tarea se puede resolver con un script de Python. El conjunto de bibliotecas de Python es tan vasto que es muy difícil encontrar una tarea que no esté cubierta por los módulos de Python.


En particular, la biblioteca moviepy y las dos horas de estudio de la documentación nos proporcionan un editor de video simple y funcional. Instalación - por favor. Sonido para imponer, por favor. Efectos especiales, por favor.


Sin embargo, como siempre, trabajar con scripts tiene un inconveniente. Cada vez que se ejecuta el script, todos los artefactos se reconstruyen nuevamente. Al instalar un video de una hora, la operación de dicho script puede durar mucho tiempo.


El uso de la biblioteca evalcache ayudó a hacer ajustes a esta situación.


 #!/usr/bin/env python3 #coding:utf-8 import sys import types from moviepy.editor import * import evalcache.lazyfile lazyhash = evalcache.LazyHash() lazyfile = evalcache.lazyfile.LazyFile() LazyVideoClip = lazyhash(VideoClip) VideoFileClip = lazyhash(VideoFileClip) AudioFileClip = lazyhash(AudioFileClip) CompositeVideoClip = lazyhash(CompositeVideoClip) concatenate_videoclips = lazyhash(concatenate_videoclips) @lazyfile("path") def lazy_write_videofile(path, clip): clip.write_videofile(path) source = VideoFileClip("source.mp4") music0 = AudioFileClip("music0.mp3") music1 = AudioFileClip("music1.mp3") music = music0 dur = 3 s0 = 1 s1 = 8 s2 = 16 part0 = source.subclip(s0, s0 + dur) part1 = source.subclip(s1, s1 + dur * 2).fl_time(lambda t: t * 2).set_duration(2) part2 = source.subclip(s2, s2 + dur * 4).fl_time(lambda t: t * 5).set_duration(2) clip = concatenate_videoclips([part0, part1, part2]) clip = clip.set_audio(music.set_duration(clip.duration)) lazy_write_videofile("part0.mp4", part0) lazy_write_videofile("part1.mp4", part1) lazy_write_videofile("part2.mp4", part2) lazy_write_videofile("part0_mus.mp4", part0.set_audio(music.set_duration(part0.duration))) lazy_write_videofile("part1_mus.mp4", part1.set_audio(music.set_duration(part1.duration))) lazy_write_videofile("part2_mus.mp4", part2.set_audio(music.set_duration(part2.duration))) if len(sys.argv) > 1 and sys.argv[1] == "compile": clip.lazy_write_videofile("clip.mp4", clip) 

Lo que hay


Usamos mecanismos de ejecución no almacenados en caché, ya que no existe el deseo o la necesidad de lidiar con el almacenamiento en caché de objetos moviepy. Al hacerlo, obtenemos todos los beneficios de los objetos perezosos para rastrear cambios en el árbol de ejecución.


Envuelva las llamadas de la biblioteca en lenificadores:


 LazyVideoClip = lazyhash(VideoClip) VideoFileClip = lazyhash(VideoFileClip) AudioFileClip = lazyhash(AudioFileClip) CompositeVideoClip = lazyhash(CompositeVideoClip) concatenate_videoclips = lazyhash(concatenate_videoclips) 

Usando esta construcción, generaremos los archivos:


 @lazyfile("path") def lazy_write_videofile(path, clip): clip.write_videofile(path) 

Una vez completadas las operaciones necesarias en la secuencia de video, escribimos partes de nuestro clip en archivos separados. Con y sin música:


 lazy_write_videofile("part0.mp4", part0) lazy_write_videofile("part1.mp4", part1) lazy_write_videofile("part2.mp4", part2) lazy_write_videofile("part0_mus.mp4", part0.set_audio(music.set_duration(part0.duration))) lazy_write_videofile("part1_mus.mp4", part1.set_audio(music.set_duration(part1.duration))) lazy_write_videofile("part2_mus.mp4", part2.set_audio(music.set_duration(part2.duration))) 

Estos archivos se recompilarán solo cuando cambien las ramas de ejecución correspondientes.


Cuando el resultado esté completo, recolecte las partes en un archivo grande usando la opción 'compilar':


 if len(sys.argv) > 1 and sys.argv[1] == "compile": clip.lazy_write_videofile("clip.mp4", clip) 

En lugar de una conclusión:


Este texto muestra cómo, con la ayuda de la biblioteca evalcache, puede indultar un algoritmo que asume la generación de archivos.
Este enfoque le permite reducir la dependencia de un software especializado o evitar escribir una lógica compleja del ensamblaje selectivo.


Referencias


Proyecto Github
Proyecto Pypi

Source: https://habr.com/ru/post/es430186/


All Articles