L'informatique paresseuse au quotidien

Et bien que les gens qui utilisent des scripts python pour écrire une liste de courses ou compiler des données de loyer compilent par tête, mais s'il se trouve que vous utilisez des scripts pour résoudre des tâches de routine et parfois des scripts fonctionnent pendant une période inacceptable, alors peut-être l'idée est d'utiliser des calculs paresseux à tout ce qui bouge, vous l'aimerez.


Dans les chapitres précédents de ma lettre, j'ai donné une description gratuite du fonctionnement de la bibliothèque evalcache.
Lien: mise en cache disque des arbres de calcul paresseux


Afin de ne pas vous ennuyer avec la nécessité d'étudier ce matériel avant de le lire, un bref résumé de la dernière partie:


evalcache encapsule les données, les fonctions et les méthodes dans des objets paresseux. Chaque objet paresseux a une clé de hachage spéciale caractérisée par la méthode de sa construction. Les opérations peuvent être effectuées sur des objets paresseux, conduisant à la génération de nouveaux objets paresseux. Ces opérations ressemblent exactement à des opérations sur des données ordinaires, mais en réalité aucun calcul n'est effectué; à la place, un arbre d'objets paresseux se référant les uns aux autres est construit, se souvenant de leurs opérations et de leurs arguments. S'il est nécessaire d'obtenir des données, une opération est effectuée pour ouvrir l'objet paresseux, qui active la chaîne de calculs ou extrait le résultat du cache, si l'objet avec cette clé a été calculé plus tôt.


Quelques changements


Depuis la rédaction du dernier article, evalcache a obtenu quelques mécanismes supplémentaires.


Mécaniques d'exécution non mises en cache


Il s'avère que le hachage d'un objet paresseux est une chose tellement utile que vous souhaitez l'utiliser dans une situation où la mise en cache de l'objet lui-même est impossible et inutile.


Une syntaxe spéciale a été introduite à cet effet:


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

Dans cette version, l'objet est calculé immédiatement au moment de la création, mais un fichier paresseux est quand même retourné. Cela vous permet de créer des arbres de calcul sans avoir à mettre en cache certains objets.


Mécanismes de divulgation implicite


La divulgation implicite est le comportement attendu d'un mémorisateur. Bien que evalcache ait été initialement conçu non pour la mémorisation, mais pour travailler avec des arbres de calcul, une divulgation implicite basée sur des algorithmes evalcache peut être obtenue. Deux nouvelles options onplace et onuse été introduites pour cela. onplace conduit à la divulgation de l'objet paresseux immédiatement après sa création, et onuse lorsqu'il essaie de l'utiliser dans certaines des opérations autorisées pour l'objet paresseux.


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

Mais nous ne parlons pas de cet ajout inutile, conçu pour rendre la bibliothèque un peu plus similaire aux autres lénifiants. Et sur les fichiers paresseux:


Fichiers paresseux


Evalcache contient un complément pour les fonctions de lénification qui génèrent des fichiers. Initialement, cette fonctionnalité était censée être utilisée pour lénifier la génération de captures d'écran. Comme il s'est avéré plus tard, en utilisant la mécanique des fichiers paresseux, vous pouvez faire d'autres choses intéressantes.


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

Comment ça marche ...
En général, la logique du travail est la même que pour tous les objets paresseux.
foo("HelloWorld","data.dat") Commence à construire un objet paresseux dont la clé de hachage est liée aux arguments qui lui sont passés. Après cela, la mécanique de la divulgation implicite est appliquée, conduisant à un démarrage instantané du calcul.


Mais alors le cours de l'action change.
@lazyfile(field="path") décorateur lazyfile analyse un paramètre avec le nom spécifié dans field. Le décorateur s'attend à ce que lors de l'exécution de la fonction, un fichier soit créé le long de ce chemin. evalcache prend ce fichier et crée un lien dur vers celui-ci dans le ".evalfile" hachage ".evalfile" . Le nom du fichier de liaison fixe correspond à la clé de hachage de l'objet paresseux. Plus tard, lorsqu'un fichier portant ce nom se trouve dans le cache, evalcache, lors de l'expansion de l'objet au lieu d'appeler la fonction, crée simplement un lien dur à l'emplacement requis vers le fichier existant dans le cache.


Il est utile qu'un fichier paresseux soit un objet paresseux ordinaire et que d'autres objets paresseux puissent être utilisés pour le générer.


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

Application de la lénification au montage vidéo via l'outil MoviePy.


Comme vous le savez, n'importe quelle tâche peut être résolue avec un script python. L'ensemble des bibliothèques Python est si vaste qu'il est très difficile de trouver une tâche non couverte par les modules Python.


En particulier, la bibliothèque moviepy et deux heures d'étude de la documentation nous donnent un éditeur vidéo simple et fonctionnel. Installation - s'il vous plaît. Son à imposer - s'il vous plaît. Effets spéciaux - s'il vous plaît.


Cependant, comme toujours, travailler avec des scripts présente un inconvénient. Chaque fois que le script est exécuté, tous les artefacts sont reconstruits à nouveau. Lors de l'installation d'une vidéo d'une heure, le fonctionnement d'un tel script peut durer très longtemps.


L'utilisation de la bibliothèque evalcache a permis d'apporter des ajustements à cette situation.


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

Qu'y a-t-il.


Nous utilisons des mécanismes d'exécution non mis en cache, car il n'y a aucun désir ou besoin de gérer la mise en cache d'objets MoviePy. Ce faisant, nous obtenons tous les avantages des objets paresseux pour suivre les modifications dans l'arbre d'exécution.


Enveloppez les appels de bibliothèque dans les lénifiants:


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

En utilisant cette construction, nous générerons les fichiers:


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

Après avoir effectué les opérations nécessaires sur la séquence vidéo, nous écrivons des parties de notre clip dans des fichiers séparés. Avec et sans musique:


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

Ces fichiers ne seront recompilés que lorsque les branches d'exécution correspondantes changeront.


Une fois le résultat terminé, collectez les pièces dans un fichier volumineux à l'aide de l'option «compiler»:


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

Au lieu d'une conclusion:


Ce texte montre comment, à l'aide de la bibliothèque evalcache, vous pouvez alléger un algorithme qui suppose la génération de fichiers.
Cette approche vous permet de réduire la dépendance à l'égard d'un logiciel spécialisé ou d'éviter d'écrire une logique complexe de l'assemblage sélectif.


Références:


Projet Github
Projet Pypi

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


All Articles