/Project
|-- main.py
|--/lib
| |--__init__.py
| |--foo.py
| |--Types.py
/Project/lib
已添加到PYTHONPATH
变量中。
Types.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)
现在的问题是,a
的类型为lib.foo.Custom
,而实例调用将检查它是否等于foo.Custom
,这显然会返回false。
如何在不更改库(lib)中的任何内容的情况下避免此问题?
答案 0 :(得分:8)
您不应同时将lib
打包并将其添加到PYTHONPATH
。这样就可以将其模块同时导入lib.
并直接导入失败。
如您所见,
lib.Types.Custom != Types.Custom
因为the way Python imports work。
Python搜索导入路径并解析它找到的相应条目。
lib.Types
时,它会将lib
目录作为包导入,然后lib/Types.py
作为其中的子模块导入,创建模块对象lib
和{{1} } lib.Types
。sys.modules
时,会导入Types
作为独立模块,在Types.py
中创建模块对象Types
。因此,sys.modules
和Types
最终成为两个不同的模块对象。 Python不会检查它们是否是同一个文件,以保持简单并避免再次猜测。
(这实际上在Traps for the Unwary in Python’s Import System文章中列为“双重导入陷阱”。)
如果您从lib.Types
移除lib
,PYTHONPATH
中的导入将需要成为相对导入:
lib/foo.py
或绝对导入:
from .Types import Custom
答案 1 :(得分:3)
当一个模块通过同一过程中的两个不同路径导入时 - 就像import Types
中的foo.py
和import lib.Types
中的main.py
一样,它实际导入了两次,产生两个不同的模块对象,每个对象都有自己独特的函数和类实例(您可以自己使用id(obj_or_class)
进行检查),有效地打破is
和isinstance
测试。
这里的解决方案是将Project
(不是Project/lib
)添加到你的pythonpath(fwiw,无论如何都应该这样做 - pythonpath / sys.path应该是一个目录列表包含包和模块,而不是包目录本身)并在任何地方使用from lib.Type import Custom
,因此您只有一个模块实例。
答案 2 :(得分:0)
# 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)