Computação preguiçosa na vida cotidiana

E, embora as pessoas que usam scripts python para escrever uma lista de compras ou compilem dados de aluguel compilem por cabeça, mas se isso acontece, você usa scripts para resolver tarefas de rotina e às vezes os scripts funcionam por um tempo inaceitavelmente longo, então a idéia é usar cálculos preguiçosos para tudo que se move, você vai gostar.


Nos capítulos anteriores da minha carta, dei uma descrição gratuita do funcionamento da biblioteca evalcache.
Link: Armazenamento em cache em disco de árvores de computação lenta


Para não aborrecê-lo com a necessidade de estudar esse material antes de ler isso, um breve resumo da última parte:


O evalcache agrupa dados, funções e métodos em objetos preguiçosos. Cada objeto lento tem uma chave de hash especial caracterizada pelo método de sua construção. As operações podem ser executadas em objetos preguiçosos, levando à geração de novos objetos preguiçosos. Essas operações se parecem exatamente com operações em dados comuns, mas, na realidade, nenhum cálculo é realizado; em vez disso, é construída uma árvore de objetos preguiçosos que se referem um ao outro, lembrando suas operações e argumentos. Se for necessário obter dados, é executada uma operação para abrir o objeto lento, que ativa a cadeia de cálculos ou extrai o resultado do cache, se o objeto com essa chave tiver sido calculado anteriormente.


Sobre algumas mudanças


Desde a redação do último artigo, o evalcache ganhou uma mecânica extra.


Mecânica de execução não armazenada em cache


Acontece que o hash de um objeto lento é uma coisa tão útil que você deseja usá-lo em uma situação em que o cache do objeto em si é impossível e desnecessário.


Uma sintaxe especial foi introduzida para este fim:


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

Nesta versão, o objeto é calculado imediatamente no momento da criação, mas um arquivo lento é retornado de qualquer maneira. Isso permite criar árvores de cálculo sem a necessidade de armazenar em cache alguns objetos.


Mecânica implícita de divulgação


Divulgação implícita é o comportamento esperado de um memorizador. Embora o evalcache tenha sido originalmente projetado não para memorização, mas para trabalhar com árvores de computação, é possível obter uma divulgação implícita com base nos algoritmos do evalcache. Duas novas opções onplace e onuse foram introduzidas para isso. onplace leva à divulgação do objeto lento imediatamente após a criação e ao uso ao tentar usá-lo em algumas das operações permitidas para o objeto lento.


 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)) 

Mas não estamos falando sobre essa adição desnecessária, projetada para tornar a biblioteca um pouco mais semelhante a outros lenificadores. E sobre arquivos preguiçosos:


Arquivos preguiçosos


O Evalcache contém um suplemento para funções de lenificação que geram arquivos. Inicialmente, essa funcionalidade deveria ser usada para lenificar a geração de capturas de tela. Como se viu depois, usando a mecânica de arquivos preguiçosos, você pode fazer outras coisas interessantes.


 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") 

Como isso funciona ...
Em geral, a lógica do trabalho é a mesma de todos os objetos preguiçosos.
foo("HelloWorld","data.dat") Começa a construir um objeto lento cuja chave de hash está vinculada aos argumentos passados ​​para ele. Depois disso, a mecânica da divulgação implícita é aplicada, levando a um início instantâneo do cálculo.


Mas então o curso da ação muda.
@lazyfile(field="path") decorador do lazyfile analisa um parâmetro com o nome especificado no campo. O decorador espera que, após a execução da função, um arquivo seja criado nesse caminho. O evalcache pega esse arquivo e cria um link ".evalfile" para ele no diretório de hash ".evalfile" . O nome do arquivo de link físico corresponde à chave de hash do objeto lento. Mais tarde, quando um arquivo com esse nome está no cache, o evalcache, ao expandir o objeto em vez de chamar a função, simplesmente cria um link físico no local necessário para o arquivo existente no cache.


É útil que um arquivo lento seja um objeto lento comum e outros objetos preguiçosos possam ser usados ​​para gerá-lo.


 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 lenificação à edição de vídeo por meio da ferramenta moviepy.


Como você sabe, qualquer tarefa pode ser resolvida com um script python. O conjunto de bibliotecas Python é tão vasto que é muito difícil encontrar uma tarefa não coberta pelos módulos Python.


Em particular, a biblioteca de filmes e duas horas de estudo da documentação fornecem um editor de vídeo simples e funcional. Instalação - por favor. Som a impor - por favor. Efeitos especiais - por favor.


No entanto, como sempre, trabalhar com scripts tem uma desvantagem. Cada vez que o script é executado, todos os artefatos são reconstruídos novamente. Ao instalar um vídeo de uma hora, a operação desse script pode durar muito tempo.


O uso da biblioteca evalcache ajudou a fazer ajustes nessa situação.


 #!/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) 

O que há lá?


Usamos a mecânica de execução não armazenada em cache, pois não há desejo ou necessidade de lidar com o cache de objetos moviepy. Ao fazer isso, obtemos todos os benefícios de objetos preguiçosos para rastrear alterações na árvore de execução.


Agrupar chamadas de biblioteca em lenificators:


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

Usando essa construção, geraremos os arquivos:


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

Depois de concluir as operações necessárias na sequência de vídeo, escrevemos partes do nosso clipe em arquivos separados. Com e sem 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))) 

Esses arquivos serão recompilados apenas quando as ramificações de execução correspondentes forem alteradas.


Quando o resultado estiver completo, colete as peças em um arquivo grande usando a opção 'compilar':


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

Em vez de uma conclusão:


Este texto mostra como, com a ajuda da biblioteca evalcache, você pode lenientizar um algoritmo que assume a geração de arquivos.
Essa abordagem permite reduzir a dependência de software especializado ou evitar escrever lógica complexa da montagem seletiva.


Referências:


Projeto Github
Projeto Pypi

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


All Articles