为何将namedtuple设为常量后无法pickle?名称为何自动小写?
问题解析与解决
1. 为什么命名为常量后pickle失败?
collections.namedtuple()的作用是动态生成一个新的类,第一个参数'dir_names'是这个类的正式名称,你赋值的变量名DIR_NAMES只是这个类对象的一个引用。
pickle序列化类对象时,默认会尝试在当前模块的全局命名空间中,根据类的__name__属性(也就是这里的dir_names)查找对应的类。当你把类赋值给DIR_NAMES时,模块里并没有名为dir_names的变量,pickle找不到这个类的定义,因此抛出attribute lookup dir_names on __main__ failed错误。
而第一个例子中,变量名dir_names和类的正式名称完全一致,pickle能在全局命名空间中找到对应的类,所以可以正常序列化。
2. 为什么报错显示__main__.dir_names?
因为namedtuple的第一个参数直接决定了生成类的__name__属性,这个属性是类的真实名称,和你赋值的变量名无关。不管你把这个类赋值给DIR_NAMES还是其他变量,类本身的名称始终是dir_names,所以pickle在查找时会用这个名称,报错信息里自然显示的是__main__.dir_names。
3. 为什么defaultdict设为常量可以正常pickle?
你混淆了类的实例和动态生成的类的序列化逻辑:
DD = collections.defaultdict(int)创建的是defaultdict类的一个实例,pickle序列化实例时,只需要找到它的类(即collections.defaultdict),这个类是Python标准库中已存在的全局类,不需要依赖你定义的变量名。- 而你序列化的
DIR_NAMES是namedtuple动态生成的类对象,这类动态生成的类需要在当前模块的命名空间中能通过类名找到定义,否则pickle无法完成序列化。
解决方法
方法一:让类名与变量名一致
把namedtuple的第一个参数改成和变量名相同的名称,这样pickle就能在全局命名空间中找到对应的类:
DIR_NAMES = collections.namedtuple('DIR_NAMES', ['mark', 'category']) pickle.dump(DIR_NAMES, open('tmp.bin', 'wb'))
方法二:序列化namedtuple实例而非类本身
如果你实际需要序列化的是具体的数据(而不是类本身),直接创建实例并序列化即可,此时不需要关心类名和变量名是否一致:
DIR_NAMES = collections.namedtuple('dir_names', ['mark', 'category']) obj = DIR_NAMES(mark='test', category='demo') pickle.dump(obj, open('tmp.bin', 'wb'))
内容的提问来源于stack exchange,提问作者king Carrey
相关产品推荐
相关产品推荐

