如何将`lambda`对象转换为`function`对象以在Python中进行酸洗?

O.r*_*rka 6 python lambda function object functools

我一直在处理有关lambda函数及其无法成为pickled. 我经常在运行中使用lambda函数作为一次性使用函数,当我必须以函数形式单独重新创建简单的 lambda 函数以用于酸洗时,它会大大降低我的工作流程生产力。

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

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
Run Code Online (Sandbox Code Playgroud)

gil*_*lch 3

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

\n\n
>>> import pickle\n>>> def foo():\n...   def bar():\n...     pass\n...   return bar\n...\n>>> pickle.dumps(foo())\nTraceback (most recent call last):\n  File "<stdin>", line 1, in <module>\nAttributeError: Can\'t pickle local object \'foo.<locals>.bar\'\n
Run Code Online (Sandbox Code Playgroud)\n\n

你说你不使用的原因dill

\n\n
\n

我见过一个用例,其中 dill 可以作为 pickle 导入,这非常酷,但由于我使用 pickle 和 dill 进行不同类型的序列化,所以在代码中变得混乱。

\n
\n\n

pickle序列化格式可以扩展到您喜欢的任何内部格式 - json, gzip, 甚至dill。你可以鱼与熊掌兼得!

\n\n

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

\n\n
import pickle\nimport dill\n\xe2\x80\x8b\nclass DillPickle:\n    def __init__(self, e):\n        self.e = e\n    def __getstate__(self):\n        return dill.dumps(self.e)\n    def __setstate__(self, state):\n        self.e = dill.loads(state)\n
Run Code Online (Sandbox Code Playgroud)\n\n

Pickle 通常通过递归腌制对象的 来序列化自定义类实例的状态__dict__。在 unpickling 时,它可以创建一个未初始化的实例并__dict__使用保存的值更新它。如果它的所有内容都是可腌制的,那么这是有效的。但如果不这样做,(就像 lambda 的情况一样)酸洗就会失败。但该pickle模块提供了一个接口来覆盖此行为:__getstate__()用于序列化对象的状态,以及__setstate__()从该值恢复它。如果 的返回值__getstate__()是可挑选的(并且__setstate__()已实现),则此方法有效。在这种情况下,我们返回由dill.dumps().

\n\n

示范:

\n\n
>>> pickle.dumps(DillPickle(lambda x, y: x+y))\nb\'\\x80\\x03c__main__\\nDillPickle\\nq\\x00)\\x81q\\x01C\\xe7\\x80\\x03cdill._dill\\n_create_function\\nq\\x00(cdill._dill\\n_load_type\\nq\\x01X\\x08\\x00\\x00\\x00CodeTypeq\\x02\\x85q\\x03Rq\\x04(K\\x02K\\x00K\\x02K\\x02KCC\\x08|\\x00|\\x01\\x17\\x00S\\x00q\\x05N\\x85q\\x06)X\\x01\\x00\\x00\\x00xq\\x07X\\x01\\x00\\x00\\x00yq\\x08\\x86q\\tX\\x1f\\x00\\x00\\x00<ipython-input-18-48b9de2c6f55>q\\nX\\x08\\x00\\x00\\x00<lambda>q\\x0bK\\x01C\\x00q\\x0c))tq\\rRq\\x0ec__builtin__\\n__main__\\nh\\x0bNN}q\\x0fNtq\\x10Rq\\x11.q\\x02b.\'\n>>> pickle.loads(_).e(\'foo\', \'bar\')\n\'foobar\'\n
Run Code Online (Sandbox Code Playgroud)\n\n

请注意,我们加载的是pickle,而不是dill!并且 lambda没有名称。当然,dill必须可供运行时使用才能解封,但类的其余部分也是如此DillPickle,就像使用pickle. dill现在只是 内部使用的实现细节DillPickle。接口是pickle.

\n\n

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

\n\n
class DillPickleCallable(DillPickle):\n    def __call__(self, *args, **kwargs):\n        return self.e(*args, **kwargs)\n
Run Code Online (Sandbox Code Playgroud)\n