简体   繁体   English

在Python中使用闭包和动态定义的函数是一种自然的设计模式吗?

[英]Is it a natural design pattern to use closures and dynamically defined functions in Python?

I find that defining functions which ask the user to define and then pass in another function to be a very natural design pattern for me. 我发现定义函数要求用户定义然后将另一个函数传递给我是一个非常自然的设计模式。 For example, 例如,

def gradient_descent(x0, grad_f):
    x = x0
    for _ in range(100):
        x -= 0.1 * grad_f(x)
    return x

Implements a generic gradient descent routine; 实现通用梯度下降例程; all the user has to do is define the gradient function for f. 用户所要做的就是为f定义渐变函数。 This is basically the interface used by scipy.optimize, and the programs I write tend to use various function closures and dynamically defined functions in a similar way. 这基本上是scipy.optimize使用的接口,我编写的程序倾向于以类似的方式使用各种函数闭包和动态定义的函数。

However, I have found myself facing some serious difficulties in taking advantage of parallelism with multiprocessing since functions can't be pickled. 但是,我发现自己在利用多处理的并行性方面遇到了一些严重的困难,因为函数无法被腌制。 I know that there are ways around this, but it makes me question whether programming like this is even a "pythonic" way to do things. 我知道有很多方法可以解决这个问题,但这让我怀疑这样的编程是否是一种“pythonic”的做事方式。

Is this a natural design pattern in Python? 这是Python中的自然设计模式吗? Is there a better way to design programs that will likely need to be refactored to use multiple processes? 是否有更好的方法来设计可能需要重构以使用多个流程的程序?

This is perfectly Pythonic, but you have to write a pickler for your closures. 这完全是Pythonic,但你必须为你的闭合写一个pickler。

Python doesn't do it for you automatically because there are a few different options you might want. Python不会自动为您执行此操作,因为您可能需要一些不同的选项。 In particular, you have to decide how far you want to "fake the closureness". 特别是,你必须决定你想要“伪造封闭性”的程度。 Do you just want the captured values copied? 您是否只想复制捕获的值? Or do you want to copy the whole stack frame and capture cells out of that? 或者你想复制整个堆栈框架并从中捕获单元格? Or do you want to actually insert a Manager or the like in the way to force the captures to stay in sync with the parent? 或者您是否希望实际插入Manager等,以强制捕获与父级保持同步?

Once you decide exactly what rules you want to apply, you can write code that does that. 一旦确定要应用的确切规则,就可以编写执行该操作的代码。 Read the pickle docs for details, and also see the multiprocessing docs and the linked source to see how it extends pickle in other ways. 阅读pickle文档了解详细信息,还可以查看multiprocessing文档和链接源,以了解它如何以其他方式扩展pickle


But the good news is what you want is most likely going to be either exactly what dill does, or exactly what cloudpickle does. 但好消息是,你想要的最有可能是dill所做的,或者恰恰是什么样的cloudpickle

In general: 一般来说:

  • dill tries to be as portable as possible, so you can save the pickles to disk and use them later, even if that means some things you probably don't care about are slightly different under the covers. dill尝试尽可能便携,因此您可以将泡菜保存到磁盘并在以后使用它们,即使这意味着您可能不关心的一些事情在封面下略有不同。
  • cloudpickle tries to be as exact as possible, even if that means the pickles don't work in anything but an exact clone of your process. cloudpickle试图尽可能准确,即使这意味着泡菜不能用于任何东西,只能是你的过程的精确克隆。 If neither of them are exactly what you want, you can of course look at the source for both and work out how to do exactly what you do want. 如果它们都不是你想要的,你当然可以查看两者的来源,并找出如何做到你想要的。

Here's a trivial closure: 这是一个微不足道的关闭:

def f():
    def g(): return i
    i=1
    return g
g = f()

Compare: 相比:

>>> pickle.dumps(g)
AttributeError: Can't pickle local object 'f.<locals>.g'
>>> dill.loads(dill.dumps(g))
<function __main__.g>
>>> dill.loads(dill.dumps(g)).__closure__
(<cell at 0x108819618: int object at 0x1009e0980>,)
>>> dill.loads(dill.dumps(g))()
1
>>> cloudpickle.loads(cloudpickle.dumps(g))
<function __main__.f.<locals>.g>
>>> cloudpickle.loads(cloudpickle.dumps(g)).__closure__
(<cell at 0x108819618: int object at 0x1009e0980>,)
>>> cloudpickle.loads(cloudpickle.dumps(g))()
1

Notice that both of them end up generating a closure that captures one cell referencing an the value 1, but cloudpickle got the name exactly right, while dill didn't. 请注意,它们都会生成一个闭包,捕获一个引用值1的单元格,但cloudpickle的名称恰好是正确的,而dill没有。 If you try to pickle.dumps the dill version, you'll get an error about g not being the same function as g , while if you try to pickle.dumps the cloudpickle version you'll get exactly the same error about pickling local objects as you started with. 如果您尝试pickle.dumpsdill的版本,你会得到有关错误g不是相同的功能, g ,而如果你试图pickle.dumpscloudpickle版本,你会得到关于酸洗本地对象完全相同的错误当你开始。

声明:本站的技术帖子网页,遵循CC BY-SA 4.0协议,如果您需要转载,请注明本站网址或者原文地址。任何问题请咨询:yoyou2525@163.com.

相关问题 可以使用闭包来简化Python中的函数吗? - Can one use closures to simplify functions in Python? 用于动态选择和排序过滤功能列表的哪种Python模式? - Which Python pattern to use for dynamically selecting and ordering a filtered list of functions? 是否有一个设计模式,我可以用它来应用一个函数列表来创建python中的机器学习功能? - Is there a design pattern that I can use for applying a list of functions to create machine learning features in python? 如何在Python中调试动态定义的函数? - How to debug dynamically defined functions in Python? 在python中生成没有闭包的函数 - Generate functions without closures in python 具有两个内部函数的闭包 python - closures with two inner functions python 在Python 3.6中实现不同功能“捆绑”的设计模式 - Design pattern for implementing different “bundles” of functions in Python 3.6 是否可以使用 python 的 shell 中定义的函数? - Is it possible to use functions defined in the shell from python? Python - 从闭包中返回多个函数 - Python - Returning multiple functions from closures Python:将本地定义的函数传递到动态加载的类中,这是否有成语? - Python: Passing locally defined functions into dynamically loaded classes, is there an idiom for this?
 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM