繁体   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已添加到PYTHONPATH变量中。

类型.py:

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

foo.py:

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

最后,从main.py

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

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

如何避免这个问题,而不必更改库(lib)中的任何内容?

您不应该同时将lib设为包并将其添加到PYTHONPATH 这使得可以将其模块作为lib.导入lib. 并直接为失败做好准备。

如你所见,

lib.Types.Custom != Types.Custom

因为Python 导入的工作方式

Python 搜索导入路径并解析它找到的适当条目。

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

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

(这实际上在 Python 的导入系统文章中的Traps for the Unwary 中列为“双重导入陷阱”。)


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

from .Types import Custom

或绝对导入:

from lib.Types import Custom

当一个模块在同一进程中通过两个不同的路径import Types ——就像这里在foo.py import Types和在foo.py import lib.Types main.py ,它实际上被导入了两次,产生了两个不同的模块对象,每个对象都有自己独特的函数和类实例(您可以使用id(obj_or_class)自己检查),有效地打破isisinstance测试。

这里的解决方案是将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