(可以直接跳到问题,再往下,跳过介绍。)
从用户定义的类中提取 Python 对象有一个常见的困难:
# This is program dumper.py
import pickle
class C(object):
pass
with open('obj.pickle', 'wb') as f:
pickle.dump(C(), f)
事实上,试图从另一个程序中取回对象loader.py
与
# This is program loader.py
with open('obj.pickle', 'rb') as f:
obj = pickle.load(f)
结果
AttributeError: 'module' object has no attribute 'C'
事实上,该类是按名称(“C”) pickle 的,loader.py
程序对 C
一无所知.一个常见的解决方案是导入
from dumper import C # Objects of class C can be imported
with open('obj.pickle', 'rb') as f:
obj = pickle.load(f)
但是,这个解决方案有一些缺点,包括必须导入 pickled 对象引用的所有类(可能有很多);此外,本地 namespace 会被来自 dumper.py
的名称污染程序。
现在,解决方案包括在 pickle 之前完全限定对象:
# New dumper.py program:
import pickle
import dumper # This is this very program!
class C(object):
pass
with open('obj.pickle', 'wb') as f:
pickle.dump(dumper.C(), f) # Fully qualified class
用原来的 Unpickling loader.py
上面的程序现在可以直接运行(无需执行 from dumper import C
)。
问题:现在,来自 dumper.py
的其他类似乎在 pickle 时自动完全合格,我很想知道这是如何工作的,以及这是否是可靠的、有记录的行为:
import pickle
import dumper # This is this very program!
class D(object): # New class!
pass
class C(object):
def __init__(self):
self.d = D() # *NOT* fully qualified
with open('obj.pickle', 'wb') as f:
pickle.dump(dumper.C(), f) # Fully qualified pickle class
现在,用原来的 loader.py
解 pickle 程序也可以工作(不需要做 from dumper import C
); print obj.d
给出了一个完全合格的类,这让我感到惊讶:
<dumper.D object at 0x122e130>
这种行为非常方便,因为只有顶部的 pickled 对象必须使用模块名称 ( dumper.C()
) 进行完全限定。但这种行为是否可靠并有记录?为什么类是按名称 ("D") pickle 的,但解腌决定 pickle self.d
属性属于 dumper.D
类(而不是一些本地 D
类)?
PS:提炼的问题:我刚刚注意到一些有趣的细节,可能指向这个问题的答案:
pickle 程序中dumper.py
, print self.d
版画 <__main__.D object at 0x2af450>
, 第一个dumper.py
程序(没有 import dumper
的程序)。另一方面,做 import dumper
并使用 dumper.C()
创建对象在 dumper.py
使 print self.d
打印 <dumper.D object at 0x2af450>
: self.d
属性由 Python 自动限定!所以,看起来 pickle
模块在上述良好的 unpickling 行为中没有任何作用。
问题是:为什么 Python 转换 D()
进入完全合格dumper.D
,在第二种情况下?这在某处记录了吗?
最佳答案
当你的类在你的主模块中定义时,pickle 期望在它们被解封时找到它们。在您的第一种情况下,类是在主模块中定义的,因此当加载器运行时,loader 是主模块,pickle 找不到类。如果您查看 obj.pickle
的内容,您会看到 name __main__
导出为 C 和 D 类的命名空间。
在您的第二种情况下,dumper.py 导入自身。现在您实际上定义了两组独立的 C 类和 D 类:一组在 __main__
命名空间中,一组在 dumper
命名空间中。您在 dumper
命名空间中序列化一个(查看 obj.pickle
以验证)。
如果找不到命名空间,pickle 将尝试动态导入命名空间,因此当 loader.py 运行时 pickle 本身 导入 dumper.py 以及 dumper.C 和 dumper.D 类。
因为你有两个独立的脚本,dumper.py 和 loader.py,所以只有定义它们在公共(public)导入模块中共享的类才有意义:
通用.py
class D(object):
pass
class C(object):
def __init__(self):
self.d = D()
加载程序.py
import pickle
with open('obj.pickle','rb') as f:
obj = pickle.load(f)
print obj
转储器.py
import pickle
from common import C
with open('obj.pickle','wb') as f:
pickle.dump(C(),f)
请注意,即使 dumper.py 转储 C()
在这种情况下 pickle 知道它是一个 common.C
对象(参见 obj.pickle
)。 loader.py运行时,会动态导入common.py,成功加载对象。
关于python - 在 Python 中,类名的自动完全限定是如何工作的? [与对象 pickle 相关],我们在Stack Overflow上找到一个类似的问题: https://stackoverflow.com/questions/6009169/