简体   繁体   English

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

[英]isinstance fails for a type imported via package and from the same module directly

/Project
|-- main.py
|--/lib
|  |--__init__.py
|  |--foo.py
|  |--Types.py

/Project/lib has been added to the PYTHONPATH variables. /Project/lib已添加到PYTHONPATH变量中。

Types.py:类型.py:

class Custom(object):
    def __init__(self):
        a = 1
        b = 2

foo.py: foo.py:

from Types import Custom
def foo(o):
    assert isinstance(o, Custom)

Finally, from main.py :最后,从main.py

from lib.Types import Custom
from lib.foo import foo
a = Custom()
foo(a)

The problem now is, that a is of type lib.foo.Custom , while the isinstance call will check if it equals foo.Custom , which obviously returns false.现在的问题是, alib.foo.Custom类型,而 isinstance 调用将检查它是否等于foo.Custom ,这显然返回 false。

How can I avoid this problem, without having to change anything in the library (lib)?如何避免这个问题,而不必更改库(lib)中的任何内容?

You should not both make lib a package and add it to PYTHONPATH .您不应该同时将lib设为包并将其添加到PYTHONPATH This makes it possible to import its modules both as lib.这使得可以将其模块作为lib.导入lib. and directly, setting yourself up for failure.并直接为失败做好准备。

As you can see,如你所见,

lib.Types.Custom != Types.Custom

because of the way Python imports work .因为Python 导入的工作方式

Python searches the import path and parses an appropriate entry that it finds. Python 搜索导入路径并解析它找到的适当条目。

  • When you import lib.Types , it imports the lib directory as a package, then lib/Types.py as a submodule inside it, creating module objects lib and lib.Types in sys.modules .当您导入lib.Types ,它会将lib目录作为包导入,然后将lib/Types.py作为其中的子模块lib.Typessys.modules创建模块对象liblib.Types
  • When you import Types , it imports Types.py as a standalone module, creating a module object Types in sys.modules .导入Types ,它Types.py作为独立模块导入,在sys.modules创建模块对象Types

So, Types and lib.Types end up as two different module objects.因此, Typeslib.Types最终成为两个不同的模块对象。 Python doesn't check if they are the same file to keep things simple and to avoid second-guessing you. Python 不会检查它们是否是同一个文件,以保持简单并避免猜测你。

(This is actually listed in the Traps for the Unwary in Python's Import System article as the "double import trap".) (这实际上在 Python 的导入系统文章中的Traps for the Unwary 中列为“双重导入陷阱”。)


If you remove lib from PYTHONPATH , the import in lib/foo.py would need to become a relative import:如果您从PYTHONPATH删除lib ,则lib/foo.py的导入将需要成为相对导入:

from .Types import Custom

or an absolute import:或绝对导入:

from lib.Types import Custom

When a module is imported thru two different path in the same process - like here with import Types in foo.py and import lib.Types in main.py , it is really imported twice, yielding two distinct module objects, each with it's own distinct functions and class instances (you can check by yourself using id(obj_or_class) ), effectively breaking is and isinstance tests.当一个模块在同一进程中通过两个不同的路径import Types ——就像这里在foo.py import Types和在foo.py import lib.Types main.py ,它实际上被导入了两次,产生了两个不同的模块对象,每个对象都有自己独特的函数和类实例(您可以使用id(obj_or_class)自己检查),有效地打破isisinstance测试。

The solution here would be to add Project (not Project/lib ) to your pythonpath (fwiw that's what should have been done anyway - pythonpath/sys.path should be a list of directories containing packages and modules, not the packages directories themselves) and use from lib.Type import Custom everywhere, so you only have one single instance of the module.这里的解决方案是将Project (而不是Project/lib )添加到您的pythonpath(fwiw无论如何都应该这样做 - pythonpath/sys.path 应该是包含包和模块的目录列表,而不是包目录本身)和在任何地方使用from lib.Type import Custom ,因此您只有一个模块实例。

# For generating a class UUID: uuidgen -n "<MODULE_UUID>" -N <Python class name> -s
# Example: uuidgen -n "dec9b2e9-07c0-4f59-af97-92f171e6fe33" -N Args -s
MODULE_UUID = "dec9b2e9-07c0-4f59-af97-92f171e6fe33"

def get_class_uuid(obj_or_cls):
    if isinstance(obj_or_cls, type):
        # it's a class
        return getattr(obj_or_cls, "CLASS_UUID", None)
    # it's an object
    return getattr(obj_or_cls.__class__, "CLASS_UUID", None)

def same_type(obj, cls):
    return get_class_uuid(obj) == get_class_uuid(cls)

class Foo:
    CLASS_UUID = "340637d8-5cb7-53b1-975e-d3f30bb825cd"

    @staticmethod
    def check_type(obj, accept_none=True):
        if obj is None:
            return accept_none 
        return same_type(obj, Foo)
...

assert Foo.check_type(obj)

声明:本站的技术帖子网页,遵循CC BY-SA 4.0协议,如果您需要转载,请注明本站网址或者原文地址。任何问题请咨询:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM