Python中pickle和copy_reg模块如何为缺失的属性提供默认值?

我电脑的 python 是2.7.10,在看picklecopy_reg为缺失的属性提供默认值的时候遇到了一个问题,具体如下:

  1. 先把仅有两个属性的GameState创建对象,并做序列化操作,保存到文件里。
  2. 然后GameState类新增加了属性point,这里用copy_reg来注册相应的行为,再进行反序列化操作

问题:在进行反序列操作后,新的属性没有被构造。这里如果序列化操作不保存到文件,整个操作又能正常,而不管是从文件里读还是strpickle实际调用的函数是一样的。找不到具体原因。求大神指点。

# 对应第一步
class GameState(object):
    def __init__(self):
        self.level = 0
        self.lives = 4

state = GameState() state.level += 1 state.lives -= 1 print state.dict ‘’’ {‘lives’: 3, ‘level’: 1} ‘’’

import pickle state_path = ‘./game_state.pickle’ with open(state_path, ‘wb’) as f: pickle.dump(state, f)

# 对应第二步
class GameState(object):
    def __init__(self, level=0, lives=4, point=0):
        self.level = level
        self.lives = lives
        self.point = point

def pickle_obj(obj):
    kwargs = obj.__dict__
    return unpickle_obj, (kwargs,)

def unpickle_obj(kwargs):
    return GameState(**kwargs)

import copy_reg
copy_reg.pickle(GameState, pickle_obj)

import pickle
state_path = './game_state.pickle'
with open(state_path, 'rb') as f:
    state_after = pickle.load(f)
print state_after.__dict__
'''
{'lives': 3, 'level': 1}
'''

Python中pickle和copy_reg模块如何为缺失的属性提供默认值?

3 回复

在Python中,pickle模块用于序列化对象,而copy_reg(Python 3中更名为copyreg)模块允许你自定义序列化和反序列化过程。当反序列化一个对象时,如果类定义发生了变化(比如删除了某个属性),直接pickle.load会失败。为了避免这种情况,我们可以通过copyreg模块为缺失的属性提供默认值。

具体做法是:使用copyreg.pickle注册一个自定义的序列化/反序列化函数。在反序列化时,如果发现对象缺少某些属性,就给它赋予默认值。

下面是一个完整的示例:

import pickle
import copyreg

class Person:
    def __init__(self, name, age, email=None):
        self.name = name
        self.age = age
        self.email = email  # 新增的属性

def pickle_person(person):
    # 序列化时保存所有属性
    return unpickle_person, (person.name, person.age, getattr(person, 'email', None))

def unpickle_person(name, age, email=None):
    # 反序列化时,如果email为None,则赋予默认值
    obj = Person(name, age)
    if email is not None:
        obj.email = email
    else:
        obj.email = "default@example.com"  # 为缺失的属性提供默认值
    return obj

# 注册自定义序列化/反序列化函数
copyreg.pickle(Person, pickle_person)

# 创建一个对象并序列化
person = Person("Alice", 30, "alice@example.com")
data = pickle.dumps(person)

# 模拟类定义变化:移除email属性
class Person:
    def __init__(self, name, age):
        self.name = name
        self.age = age

# 反序列化对象
loaded_person = pickle.loads(data)
print(f"Name: {loaded_person.name}, Age: {loaded_person.age}, Email: {loaded_person.email}")

在这个例子中,即使Person类后来移除了email属性,反序列化时unpickle_person函数也会检测并提供默认值。这种方法可以确保向后兼容性,避免因类定义变化导致的AttributeError

总结:用copyreg注册自定义函数来安全地处理缺失属性。


昨天在 Python3.6 下试了下也有一样的问题。

找到原因了。正确的使用方式应该是第一次创建类的时候,就进行注册。
~python
class GameState(object):
def init(self):
self.level = 0
self.lives = 4

def pickle_obj(obj):
kwargs = obj.dict
return unpickle_obj, (kwargs,)

def unpickle_obj(kwargs):
return GameState(**kwargs)

import copy_reg
copy_reg.pickle(GameState, pickle_obj)

state = GameState()
state.level += 2
state.lives -= 3
print state.dict
’’’
{‘lives’: 1, ‘level’: 2}
’’‘

import pickle
state_path = ‘./game_state_v2.pickle’

with open(state_path, ‘wb’) as f:
pickle.dump(state, f)

class GameState(object):
def init(self, level=0, lives=4, point=0):
self.level = level
self.lives = lives
self.point = point

with open(state_path, ‘rb’) as f:
state_after = pickle.load(f)
print state_after.dict
’’’
{‘point’: 0, ‘lives’: 1, ‘level’: 2}
’’’
~

回到顶部