python-3.x - 无法 pickle <class 'a class' > : attribute lookup inner class on a class failed

标签 python-3.x metaprogramming pickle metaclass

我正在使用 PySpark 处理一些通话数据。如您所见,我使用元类动态地向类 GetInfoFromCalls 添加了一些内部类。 以下代码位于包 for_test 中,存在于所有节点中:

class StatusField(object):
    """
    some alias.
    """
    failed = "failed"
    succeed = "succeed"
    status = "status"
    getNothingDefaultValue = "-999999"


class Result(object):
    """
    Result that store result and some info about it.
    """

    def __init__(self, result, status, message=None):
        self.result = result
        self.status = status
        self.message = message

structureList = [
    ("user_mobile", str, None),
    ("real_name", str, None),
    ("channel_attr", str, None),
    ("channel_src", str, None),
    ("task_data", dict, None),
    ("bill_info", list, "task_data"),
    ("account_info", list, "task_data"),
    ("payment_info", list, "task_data"),
    ("call_info", list, "task_data")
]

def inner_get(self, defaultValue=StatusField.getNothingDefaultValue):
    try:
        return self.holder.get(self)
    except Exception as e:
        return Result(defaultValue, StatusField.failed)
        print(e)

class call_meta(type):
    def __init__(cls, name, bases, attrs):

        for name_str, type_class, pLevel_str in structureList:
            setattr(cls, name_str, type(
                name_str,
                (object,),
                {})
                )

class GetInfoFromCalls(object, metaclass = call_meta):
    def __init__(self, call_deatails):
        for name_str, type_class, pLevel_str in structureList:
            inn = getattr(self.__class__, name_str)()
            object_dict = {
                "name": name_str,
                "type": type_class,
                "pLevel": None if pLevel_str is None else getattr(self, pLevel_str),
                "context": None,
                "get": inner_get,
                "holder": self,
            }
            for attr_str, real_attr in object_dict.items():
                setattr(inn, attr_str, real_attr)
            setattr(self, name_str, inn)

        self.call_details = call_deatails

当我跑的时候

import pickle

pickle.dumps(GetInfoFromCalls("foo"))

它引发了这样的错误:

Traceback (most recent call last):
  File "<ipython-input-11-b2d409e35eb4>", line 1, in <module>
    pickle.dumps(GetInfoFromCalls("foo"))
PicklingError: Can't pickle <class '__main__.user_mobile'>: attribute lookup user_mobile on __main__ failed

我似乎无法 pickle 内部类,因为它们是通过代码动态添加的。当类被 pickle 时,内部类不存在,对吗?
真的,我不想编写这些彼此几乎相同的类。有人有避免这个问题的好方法吗?

最佳答案

Python 的 pickle 实际上不序列化类:它序列化实例,并在序列化中放入对每个实例类的引用 - 并且该引用基于绑定(bind)到定义良好的模块中的名称的类。因此,没有模块名称但作为其他类中的属性或列表和字典中的数据存在的类的实例通常不起作用。

可以尝试做的一件直接的事情就是尝试使用 dill而不是 pickle 。它是一个第三方包,工作方式类似于“pickle”,但具有实际序列化任意动态类的扩展。

虽然使用 dill 可能会帮助其他人到达这里,但这不是你的情况,因为为了使用 dill,你必须猴子修补 PySpark 正在使用的底层 RPC 机制以利用莳萝而不是 pickle ,这对于生产使用来说可能不够微不足道或不够一致。

如果问题真的是动态创建的类是不可拾取的,你可以做的是为动态类本身创建额外的元类,而不是使用“类型”,并在这些元类上创建适当的 __getstate____setstate__ (或其他辅助方法 on pickle documentation )——这可能使这些类能够被普通的 Pickle pickle 。也就是说,在您的代码中使用带有 Pickler 辅助方法的单独元类代替 type(..., (object, ), ...)

但是,“unpickable object”不是您得到的错误 - 它是一个属性查找错误,这表明您正在构建的结构不足以让 Pickle 对其进行内省(introspection)并从您的其中一个获取所有成员instances - 它与类对象的 unpickleability 无关(还)。由于您的动态类作为类的属性存在(它本身不是 pickle 的)而不是实例的属性,因此 pickle 很可能不关心它。检查上面关于 pickle 的文档,也许你只需要有适当的辅助方法来对你的类进行 pickle,元类上没有什么不同,因为你在那里可以正常工作。

关于python-3.x - 无法 pickle <class 'a class' > : attribute lookup inner class on a class failed,我们在Stack Overflow上找到一个类似的问题: https://stackoverflow.com/questions/48615601/

相关文章:

python - collection.abc.* 模块中的抽象类是如何实现的?

ruby - 创建带有参数的动态方法

python - 我有一个 cPickle 错误,不知道他的意思,也许有人知道这是什么意思?

python - 为什么 python 模块 dill 不能 pickle 生成器函数?

python - 如何动态创建 Luigi 任务

python - 如何创建与 python 中现有目录内容相同的目录的 "in memory"副本

python - 如何获取多个JSON对象的值?

python-3.x - Pandas 移位日期时间索引运行时间太长

c++ - 使用 C++ 模板有条件地插入方法的主体

ruby-on-rails - 将 block 传递给动态创建的方法