【问题标题】:Python 3 isinstance unexpected behavior when importing class from different file?从不同文件导入类时,Python 3 isinstance 意外行为?
【发布时间】:2019-10-05 06:52:42
【问题描述】:

我正在尝试从一个文件中导入一个类并检查它是否是该类在定义它的文件中的一个实例。问题是它没有从isinstance() 函数返回True,而是返回False,因为它是在不同的文件中初始化的。

这是一个工作示例。

假设你有file1.py:

class Foo:
    def __init__(self, arg1):
        self.arg1 = arg1

def main(class_obj):
    # Prints false and is type <class 'file1.Foo'>
    print(type(class_obj))
    print(isinstance(class_obj, Foo))

if __name__ == '__main__':
    from file2 import get_class
    main(get_class())

还有file2.py:

from file1 import Foo

def get_class():
    foo = Foo("argument")
    return foo

它打印False,类型为&lt;class 'file1.Foo'&gt;。我发现有趣的是,如果你在定义 is 的 file1 中初始化 Foo 类,它会返回 True

# Add to main() function in file1
# Returns true and is type <class '__main__.Foo'>
foo_local = Foo("argument")  # Class initiated in __main__ seems to work
print(type(foo_local))
print(isinstance(foo_local, Foo))

我发现,如果您在定义它的文件之外启动一个类,它与您在定义它的文件内启动该类是不同的“类”。

# Different Classes?
<class 'file1.Foo'>  # From other file (`file2.py`)
<class '__main__.Foo'>  # From same file (`file1.py`)

所以我的问题是:

我该如何解决这个问题,以便即使在file1 之外启动的类也可以在isinstance() 函数上返回True?要改写它,我怎样才能使Foo 类与file1.pyfile2.py 中的“相同”?如果重要的话,我是 Python 3.6.7。

【问题讨论】:

  • 在询问与 python 相关的问题时始终使用 [python] 通用标签。自行决定使用特定于版本的标签。

标签: python python-3.x class python-import isinstance


【解决方案1】:

非常简单的答案是永远不要使用 if __name__=="__main__"。可以肯定,这是一个聪明的把戏,但它并没有做任何人认为它做的事情。它应该使文件成为一个模块一个脚本,但是(因为查找和运行模块和脚本的过程是如此不同)它实际上所做的是让文件成为一个模块脚本,单独。这个技巧包含一个关于这个缺点的提示:模块中的__name__ 应该是它在sys.modules 中的键,如果它是“__main__”,那根本就不是任何正常的模块。 (其实可以import __main__得到一个模块对象,其属性是脚本中的全局变量!)

在您的情况下,file1.py 被用作脚本一次,然后通过模块 file2 作为模块,它实际上被加载 两次。每次加载都会创建一个不相关(如果相似)的类Foo;使用每个类的在哪里并不重要,只需要使用哪一个即可。 (file1 甚至可以导入 自身 并获得“模块版本”。)请注意,类不必是“相同的”;相同的if 技巧可用于赋予它们不同的成员或基类,甚至可以控制执行哪个class Foo 语句。

如果你想使用python -m,出于安装原因,这是一个完全合理的愿望,损坏最少的方法是通过一个包中的__main__.py,否则通过@987654333使用@。 仍然可以导入它,这可能没有任何好处,但没有人会这样做(除了递归导入包中每个模块的幼稚代码)。

【讨论】:

  • 我不会说永远不要使用if __name__ == '__main__',尤其是因为存在multiprocessing 模块之类的情况,在这种情况下省略保护可能会完全破坏您的程序。也就是说,Python 的整个 __main__ 设计得很糟糕,这种与循环导入的特殊交互是更奇怪、更痛苦的部分之一。
  • @user2357112:也许我应该说不要在模块中使用它(尽管这是它的最初目的)。 multiprocessing 需要它(当不使用 fork 时)只是意味着从(当时)琐碎的驱动程序脚本中分离出一个模块是一个比平常更好的主意。
猜你喜欢
  • 1970-01-01
  • 2016-05-29
  • 1970-01-01
  • 2019-12-30
  • 2018-12-29
  • 1970-01-01
  • 2018-07-18
  • 1970-01-01
  • 2016-02-19
相关资源
最近更新 更多