簡體   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