Python 使用默认值取消勾选和参数时出现问题

Python 使用默认值取消勾选和参数时出现问题,python,serialization,pickle,Python,Serialization,Pickle,假设我有一门课: class Character(): def __init__(self): self.race = "Ork" 我创建一个实例并对其进行pickle c = Character() import pickle with open(r'C:\tmp\state.bin', 'w+b') as f: pickle.dump(c, f) 当我试着解开它时,一切正常。 但如果我想给角色添加另一个属性呢? 我要说的是: class Charact

假设我有一门课:

class Character():

    def __init__(self):
        self.race = "Ork"
我创建一个实例并对其进行pickle

c = Character()

import pickle
with open(r'C:\tmp\state.bin', 'w+b') as f:
    pickle.dump(c, f)
当我试着解开它时,一切正常。 但如果我想给角色添加另一个属性呢? 我要说的是:

class Character():

    def __init__(self):
        self.race = "Ork"
        self.health = 100
假设我想取消勾选没有健康属性的旧版本。如果我只是从文件中取消勾选数据,对象将不具有health属性。为了以一种合适的方式实现它,按照有效的Python手册中所写的内容,我需要引入带有默认值的参数,并使用copyreg

因此,我这样做:

class Character

    def __init__(self, race = "Ork", health = 100):
        self.race = race
        self.health = health

import copyreg 

def pickle_character(state):
    kwargs = state.__dict__
    return unpickle_character, (kwargs, )

def unpickle_character(kwargs):
    return Character(**kwargs)

copyreg.pickle(Character, pickle_character)
现在,取消勾选应该可以正常工作:

with open(r'C:\tmp\state.bin', 'rb') as f:
    c = pickle.load(f)
这段代码运行得很好,但是,我仍然没有在c对象中看到新的health属性


问题很简单,为什么会这样?根据有效的Python,一切都应该正常工作。

取消勾选的标准行为直接分配属性-它不使用uuu init_uuuuu或uu new_uuuu。因此,默认参数不适用

当一个类实例被取消勾选时,它的_init__方法通常不会被调用

调用_init _可能会产生副作用,并且可能需要比属性更多、更少或其他参数。这使得它成为一个不安全的默认值。实际上,pickle使用object.\uuuuu new\uuuuu cls创建实例,然后更新其\uuuu dict\uuuu

如果愿意,您必须明确地告诉pickle使用_uinit

使用copyreg时,必须将其传递给。请注意,此签名与您的unpickle_字符的签名不同

否则,酸洗函数pickle_字符将静态定义用于取消酸洗的函数。由于没有为Character类注册构造函数,并且旧pickle不包含它,因此加载旧pickle不会调用构造函数

def pickle_character(state):
    kwargs = state.__dict__
    return unpickle_character, (kwargs, )
    #      ^ unpickler stored for *newly pickled instance*!
# no constuctor stored for *Character class* v
copyreg.pickle(Character, pickle_character)
在类上定义更容易。这直接接收到状态,甚至是来自旧的泡菜

class Character:
    def __init__(self, race, health):
        self.race = race
        self.health = health

    # load state with defaults for missing attributes
    def __setstate__(self, state):
        self.race = state.get('race', 'Ork')
        self. health = state.get('health', 100)
如果您知道uuu init uuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuu

class Character:
    # defaults for every initialisation
    def __init__(self, race='Ork', health=100):
        self.race = race
        self.health = health

    def __setstate__(self, state):
        # re-use __init__ for initialisation
        self.__init__(**state)

取消勾选的标准行为直接指定属性-它不使用uuu init_uuuu或uuu new_uuu。因此,默认参数不适用

当一个类实例被取消勾选时,它的_init__方法通常不会被调用

调用_init _可能会产生副作用,并且可能需要比属性更多、更少或其他参数。这使得它成为一个不安全的默认值。实际上,pickle使用object.\uuuuu new\uuuuu cls创建实例,然后更新其\uuuu dict\uuuu

如果愿意,您必须明确地告诉pickle使用_uinit

使用copyreg时,必须将其传递给。请注意,此签名与您的unpickle_字符的签名不同

否则,酸洗函数pickle_字符将静态定义用于取消酸洗的函数。由于没有为Character类注册构造函数,并且旧pickle不包含它,因此加载旧pickle不会调用构造函数

def pickle_character(state):
    kwargs = state.__dict__
    return unpickle_character, (kwargs, )
    #      ^ unpickler stored for *newly pickled instance*!
# no constuctor stored for *Character class* v
copyreg.pickle(Character, pickle_character)
在类上定义更容易。这直接接收到状态,甚至是来自旧的泡菜

class Character:
    def __init__(self, race, health):
        self.race = race
        self.health = health

    # load state with defaults for missing attributes
    def __setstate__(self, state):
        self.race = state.get('race', 'Ork')
        self. health = state.get('health', 100)
如果您知道uuu init uuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuu

class Character:
    # defaults for every initialisation
    def __init__(self, race='Ork', health=100):
        self.race = race
        self.health = health

    def __setstate__(self, state):
        # re-use __init__ for initialisation
        self.__init__(**state)

因此pickle将调用uuu new_uuu?@C.Nivs,它直接构造对象,同时绕过类“uuu new_uu”。@MisterMiyagi-hmm。。。为什么签名不同?我认为有效Python的作者的意思是,我们引入了一个带有默认值的args形式的dict,因此我们可以传递**kwargs。@MisterMiyagi它仅在用作构造函数时无效,这是什么意思?请注意,我无法重现您的错误-您的意思是,在您的计算机上,我的代码取消了旧版本的pickle,并且您有一个具有新添加属性的对象吗?@MisterMiyagi如果愿意,您必须显式地告诉pickle使用init。我该怎么做?你能修改我的代码,使其依赖于这种解压方式吗?非常感谢你的帮助!因此pickle将调用uuu new_uuu?@C.Nivs,它直接构造对象,同时绕过类“uuu new_uu”。@MisterMiyagi-hmm。。。为什么签名不同?我认为有效Python的作者的意思是,我们引入了一个带有默认值的args形式的dict,因此我们可以传递**kwargs。@MisterMiyagi它仅在用作构造函数时无效,这是什么意思?请注意,我无法重现您的错误-您的意思是,在您的计算机上,我的代码取消了旧版本的pickle,并且您有一个具有新添加属性的对象吗?@MisterMiyagi如果愿意,您必须显式地告诉pickle使用init。我该怎么做?你能修改我的代码,使其依赖于这种解压方式吗?非常感谢你的帮助!