对于通过包导入的类型以及直接从同一模块导入的类型,isinstance失败

net*_*tik 9 python python-3.x python-3.4

/Project
|-- main.py
|--/lib
|  |--__init__.py
|  |--foo.py
|  |--Types.py
Run Code Online (Sandbox Code Playgroud)

/Project/lib已被添加到PYTHONPATH变量中.

Types.py:

class Custom(object):
    def __init__(self):
        a = 1
        b = 2
Run Code Online (Sandbox Code Playgroud)

foo.py:

from Types import Custom
def foo(o):
    assert isinstance(o, Custom)
Run Code Online (Sandbox Code Playgroud)

最后,来自main.py:

from lib.Types import Custom
from lib.foo import foo
a = Custom()
foo(a)
Run Code Online (Sandbox Code Playgroud)

现在的问题是,这a是类型lib.foo.Custom,而isinstance调用将检查它是否等于foo.Custom,显然返回false.

如何在不更改库(lib)中的任何内容的情况下避免此问题?

iva*_*eev 11

你不应该同时创建lib一个包并将其添加到PYTHONPATH.这使得可以直接导入其模块lib.,从而使自己失败.

如你看到的,

lib.Types.Custom != Types.Custom
Run Code Online (Sandbox Code Playgroud)

因为Python导入工作的方式.

Python搜索导入路径并解析它找到的相应条目.

  • 导入时lib.Types,它将lib目录作为包导入,然后lib/Types.py作为其中的子模块导入,创建模块对象liblib.Types在其中sys.modules.
  • 当您导入Types,它导入Types.py作为一个独立的模块,创建模块对象Typessys.modules.

所以,Typeslib.Types最终成为两个不同的模块对象.Python不会检查它们是否是同一个文件,以保持简单并避免再次猜测.

(这实际上是在Python的导入系统文章中将"粗心的陷阱"列为"双重导入陷阱".)


如果lib从中删除PYTHONPATH,则导入lib/foo.py将需要成为相对导入:

from .Types import Custom
Run Code Online (Sandbox Code Playgroud)

或绝对导入:

from lib.Types import Custom
Run Code Online (Sandbox Code Playgroud)


bru*_*ers 5

当在同一过程中通过两条不同的路径导入模块时(例如在此处使用import Typesin foo.pyimport lib.Typesin)main.py,它实际上被导入了两次,产生了两个不同的模块对象,每个对象都有自己的不同函数和类实例(您可以自己使用进行检查id(obj_or_class)) ,有效突破isisinstance测试。

这里的解决方案是在您的pythonpath中添加Project(而不是Project/lib)(无论如何,应该这样做-pythonpath / sys.path应该是包含软件包和模块的目录列表,而不是软件包目录本身),并from lib.Type import Custom在各处使用,因此您只有模块的一个实例。