【问题标题】:Can't pickle <class 'a class'>: attribute lookup inner class on a class failed无法腌制 <class 'a class'>:类上的属性查找内部类失败
【发布时间】:2018-07-14 21:32:53
【问题描述】:

我正在使用 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

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

【问题讨论】:

  • Upovted,因为看到 MCVE 总是很高兴,但老实说,我不明白,到底为什么你会选择如此复杂的解决方案。尤其是使用 Spark。
  • MCVE 是什么意思 :) ?因为我在 jupyter notebook 中使用 PySpark,所以我一直使用TAB 给我一些提示,比如call_detal_info.bill_info.get()。我知道重写一些像__getattr__()这样的魔术方法可以解决我的问题,但是我只想知道在酸洗和解酸的情况下使用元类是否可行?
  • MCVE 是minimal reproducible example :) 如果您只想要. 语法,为什么不使用namedtuple?你真的想在运行时生成类,那么我会在外部范围内使用type 来完成。与namedtuple 相同。
  • namedtupletype 在模块范围内有同样的问题,除非我编写像 s = type("s", (object, ), {}) 这样的代码。这不是我真正关心的:)。

标签: python-3.x metaprogramming pickle metaclass


【解决方案1】:

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

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

虽然使用dill 可能会帮助其他人到达这里,但这不是你的情况,因为为了使用 dill,你必须对 PySpark 使用的底层 RPC 机制进行修补,以使用 dill 而不是 pickle ,对于生产用途来说,这可能不够简单也不够一致。

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

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

【讨论】:

  • 很好的答案!谢谢你:)
猜你喜欢
  • 1970-01-01
  • 2011-06-08
  • 1970-01-01
  • 1970-01-01
  • 2016-11-04
  • 2018-11-19
  • 1970-01-01
  • 1970-01-01
  • 2023-01-11
相关资源
最近更新 更多