日常生活中的懒惰计算

而且,尽管使用python脚本来编写购物清单或编译租金数据的人都是按头编译的,但是如果发生这种情况,您使用脚本来解决例行任务,有时脚本会工作很长时间,那么这种想法就是使用惰性计算对于所有移动的东西,您都会喜欢。


在我来信的前几章中,我免费描述了evalcache库的操作。
链接: 惰性计算树的磁盘缓存


为了使您不必在阅读本文之前先学习该材料,请对最后一部分进行简要总结:


evalcache将数据,函数和方法包装在惰性对象中。 每个惰性对象都有一个特殊的哈希键,其特征在于其构造方法。 可以对惰性对象执行操作,从而导致生成新的惰性对象。 这些操作看起来与对普通数据的操作完全一样,但是实际上不执行任何计算;而是构建了相互引用的惰性对象树,并记住了它们的操作和参数。 如果必须获取数据,则执行操作以打开惰性对象,如果带有此键的对象是较早计算的,则该对象可以激活计算链,也可以从缓存中提取结果。


关于一些变化


自从写了上一篇文章以来,evalcache有了一些额外的机制。


非缓存执行机制


事实证明,懒惰对象的哈希是如此有用,以至于在无法缓存对象本身且不必要缓存对象的情况下,您想使用它。


为此引入了一种特殊的语法:


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

在此版本中,对象是在创建时立即计算的,但是无论如何都会返回一个惰性文件。 这使您可以构建计算树,而无需缓存某些对象。


隐式披露机制


隐式披露是回忆器的预期行为。 尽管evalcache最初不是为记忆而设计的,而是为与计算树一起使用而设计的,但可以实现基于evalcache算法的隐式公开。 onplace ,引入了两个新的onplaceonuse选项。 onplace会在创建后立即公开惰性对象,并在尝试用于惰性对象的某些操作中使用它时会导致onuse的使用。


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

但是,我们并不是在谈论这种不必要的添加,其目的是使库与其他修饰符更加相似。 关于惰性文件:


懒文件


Evalcache包含一个加载项,用于简化生成文件的功能。 最初,应该将此功能用于简化屏幕截图的生成。 后来证明,使用惰性文件的机制,您可以做其他有趣的事情。


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

如何运作...
通常,工作逻辑与所有懒惰对象相同。
foo("HelloWorld","data.dat")开始构造一个惰性对象,该对象的哈希键与传递给它的参数相关。 之后,应用隐式披露的机制,从而立即开始计算。


但是随后行动的方向发生了变化。
@lazyfile(field="path")惰性文件装饰器解析具有在字段中指定的名称的参数。 装饰者希望函数执行后,将沿着该路径创建文件。 evalcache会获取该文件,并在".evalfile"哈希目录中创建指向该文件的硬链接。 硬链接文件名对应于惰性对象的哈希键。 稍后,当具有该名称的文件位于高速缓存中时,evalcache在扩展对象而不是调用函数时,只需在所需位置中创建到高速缓存中现有文件的硬链接。


惰性文件是一个普通的惰性对象,可以使用其他惰性对象来生成它,这很有用。


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

通过moviepy工具将放大率应用于视频编辑。


如您所知,任何任务都可以使用python脚本解决。 Python库非常庞大,以至于很难找到Python模块未涵盖的任务。


尤其是moviepy库和两个小时的学习文档为我们提供了一个简单实用的视频编辑器。 安装-请。 声音要强加-请。 特效-请。


但是,与往常一样,使用脚本有一个缺点。 每次运行脚本时,都会重新构建所有工件。 安装一小时的视频时,此类脚本的操作可能会持续很长时间。


使用evalcache库有助于对这种情况进行调整。


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

有什么


我们使用非缓存的执行机制,因为不需要或不需要处理对moviepy对象的缓存。 这样做,我们获得了懒惰对象的所有好处,可用于跟踪执行树中的更改。


在修饰符中包装库调用:


 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) 

完成视频序列上的必要操作后,我们将剪辑的一部分写入单独的文件中。 有无音乐:


 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) 

而不是结论:


本文显示了如何在evalcache库的帮助下简化假定生成文件的算法。
这种方法可以减少对专用软件的依赖,也可以避免编写选择性装配的复杂逻辑。


参考文献:


Github项目
Pypi项目

Source: https://habr.com/ru/post/zh-CN430186/


All Articles