如何在 Python 中将"lambda"对象转换为"函数"对象以进行酸洗?



我一直在处理有关lambda函数及其无法pickled的错误。 我经常将lambda函数作为一次性使用函数即时使用,当我必须以函数形式单独重新创建简单的 lambda 函数以用于酸洗时,它会大大降低我的工作流程效率。

有没有办法将lambda及其所有参数转换为Python 3.6.1中的function对象?

lambda_func = lambda x: x.split(" ")
def func(x):
return x.split(" ")
input_string = "Darth Plagueis was a Dark Lord of the Sith"
# Function Version
func(input_string)
# ['Darth', 'Plagueis', 'was', 'a', 'Dark', 'Lord', 'of', 'the', 'Sith']
lambda_func(input_string)
# ['Darth', 'Plagueis', 'was', 'a', 'Dark', 'Lord', 'of', 'the', 'Sith']
def lambda2func(lambda_func):
#...
return func_version

pickle不保存代码对象,只保存其名称。但是,为函数命名并不足以使其可挑剔,因为unpickle通过导入名称来反序列化它。当然,这意味着它必须可以通过脱洗运行时导入。由于这个原因,用名称def的函数仍然可能无法腌制。因此,即使您可以将lambda转换为命名函数,这仍然不起作用:

>>> import pickle
>>> def foo():
...   def bar():
...     pass
...   return bar
...
>>> pickle.dumps(foo())
Traceback (most recent call last):
File "<stdin>", line 1, in <module>
AttributeError: Can't pickle local object 'foo.<locals>.bar'

你说你不使用dill的理由是

我见过一个用例,其中莳萝可以作为泡菜导入,这很酷,但由于我将泡菜和莳萝用于不同类型的序列化,因此在代码中变得混乱。

但是pickle序列化格式可以扩展到您喜欢的任何内部格式 -jsongzip,甚至dill。你也可以吃你的蛋糕!

让我们尝试一下dill,因为这很容易:

import pickle
import dill
​
class DillPickle:
def __init__(self, e):
self.e = e
def __getstate__(self):
return dill.dumps(self.e)
def __setstate__(self, state):
self.e = dill.loads(state)

Pickle 通常通过递归挑取对象的__dict__来序列化自定义类实例的状态。在取消酸洗时,它可以创建一个未初始化的实例,并使用保存的值更新其__dict__。如果它的所有内容物都可以一直腌制,这就可以了。但如果不是,(就像lambdas的情况一样)腌制就会失败。但是pickle模块提供了一个接口来重写此行为:__getstate__()用于序列化对象的状态,__setstate__()用于从该值还原对象状态。如果__getstate__()的返回值是可选择的(并且实现了__setstate__()),则此方法有效。在这种情况下,我们返回dill.dumps()返回的可拾取字节串。

示范:

>>> pickle.dumps(DillPickle(lambda x, y: x+y))
b'x80x03c__main__nDillPicklenqx00)x81qx01Cxe7x80x03cdill._dilln_create_functionnqx00(cdill._dilln_load_typenqx01Xx08x00x00x00CodeTypeqx02x85qx03Rqx04(Kx02Kx00Kx02Kx02KCCx08|x00|x01x17x00Sx00qx05Nx85qx06)Xx01x00x00x00xqx07Xx01x00x00x00yqx08x86qtXx1fx00x00x00<ipython-input-18-48b9de2c6f55>qnXx08x00x00x00<lambda>qx0bKx01Cx00qx0c))tqrRqx0ec__builtin__n__main__nhx0bNN}qx0fNtqx10Rqx11.qx02b.'
>>> pickle.loads(_).e('foo', 'bar')
'foobar'

请注意,我们加载了pickle,而不是dill!而且 lambda没有名字。当然,dill必须可供运行时使用才能取消pickled,但DillPickle类的其余部分也是如此,就像使用pickle序列化的任何自定义类型一样。dill现在只是DillPickle内部使用的实现细节。界面pickle

您甚至可以将其设置为可调用对象,因此您无需自己添加.e

class DillPickleCallable(DillPickle):
def __call__(self, *args, **kwargs):
return self.e(*args, **kwargs)

相关内容

最新更新