python - 在 Python 中,类名的自动完全限定是如何工作的? [与对象 pickle 相关]

标签 python class import pickle fully-qualified-naming

(可以直接跳到问题,再往下,跳过介绍。)

从用户定义的类中提取 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/

相关文章:

ruby - 类对象的案例陈述

python - 从单独的文件导入类

java - Eclipse如何添加文件夹库?

web-applications - 如何分发我的 Jython war 中使用的库?

python - python中有三元运算符吗

python - 调用数组时仅使用第一个元素?

python - 具有不同长度的列表列表的元素明智连接

python - 在 Python 中获取最后一个 '/' 或 '\\' 字符

c++ - C++ 中具有 int、函数、虚函数的 sizeof 类?

C++ 类和对象创建