Python 3.x Can';t pickle<;类别';一类'>;:类上的属性查找内部类失败

Python 3.x Can';t pickle<;类别';一类'>;:类上的属性查找内部类失败,python-3.x,metaprogramming,pickle,metaclass,Python 3.x,Metaprogramming,Pickle,Metaclass,我使用PySpark处理一些通话数据。如您所见,我通过使用元类向classGetInfoFromCalls动态添加了一些内部类。 以下代码位于包中,用于所有节点中存在的\u测试: class StatusField(object): """ some alias. """ failed = "failed" succeed = "succeed" status = "status" getNothingDefaultValue = "-99

我使用PySpark处理一些通话数据。如您所见,我通过使用元类向class
GetInfoFromCalls
动态添加了一些内部类。 以下代码位于包
中,用于所有节点中存在的\u测试

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
当我跑的时候

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"))
它引发了如下错误:

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
回溯(最近一次呼叫最后一次):
文件“”,第1行,在
pickle.dumps(GetInfoFromCalls(“foo”))
PicklingError:无法pickle:属性查找用户\u mobile on \uuuuu main\uuuu失败
似乎我不能pickle内部类,因为它们是由代码动态添加的。当类被酸洗时,内部类不存在,对吗?

我真的不想写这些彼此几乎相同的类。有人有好的方法来避免这个问题吗

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

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
一个人可以尝试做的一件直截了当的事就是尝试用泡菜代替泡菜。它是一个类似于“pickle”的第三方软件包,但具有实际序列化任意动态类的扩展

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

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
如果问题真的是动态创建的类不可访问,那么您可以做的是为动态类本身创建额外的元类,而不是使用“type”,并在这些元类上创建适当的
\uu getstate\uuuuuuu
\uuuuuu setstate\uuuuuuuuu
(或其他现有的辅助方法)-这可能使这些类能够通过普通Pickle进行Pickle。也就是说,在代码中使用一个单独的元类,其中包含Pickler-helper方法,而不是
type(…,(object,),…)

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

但是,“unpickable object”并不是您得到的错误-它是一个属性查找错误,这表明您正在构建的结构不够好,Pickle无法对其进行反思并从您的一个实例中获取所有成员-它与类对象的unpickable性(尚未)无关。由于您的动态类作为类的属性存在(类本身不是pickle的),而不是实例的属性,因此pickle很可能不关心它。查看上面pickle上的文档,也许你所需要的就是在你的类上pickle合适的助手方法,元类上没有什么不同,所有这些都可以正常工作。

Upovted,因为看到MCVE总是好的,但老实说,我不明白,到底为什么你会选择这样一个复杂的解决方案。特别是与Spark合作。你说的MCVE:)是什么意思?因为我在jupyter笔记本中使用PySpark,所以我总是使用
TAB
给我一些提示,比如
调用\u detal\u info.bill\u info.get()
。我知道重写一些神奇的方法,如uu getattr uuu()可以解决我的问题,但我只想知道在酸洗和取消酸洗的情况下使用元类是否可行?MCVE是:)如果只想
语法,为什么不使用
namedtuple
?如果您真的想在运行时生成类,那么我会在外部范围内使用
type
。与
namedtuple
相同。模块范围中的
namedtuple
type
也有相同的问题,除非我编写类似
s=type(“s”,(object,),{})的代码。这不是我真正关心的:)。回答得很好!谢谢:)
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