【问题标题】:Python - Undefined class name after dynamic importPython - 动态导入后未定义的类名
【发布时间】:2012-07-05 13:00:32
【问题描述】:

我已经使用 Python 一周了,但我遇到了动态导入的问题。 我有一个文件 Test.py ,其中定义了一个类。我想在从另一个文件动态导入 Test.py 后使用这个类。

我的最终目标更复杂,但我简化了它,但我仍然遇到同样的问题。

文件:Test.py

class Test :
    def __init__ ( self ) :
        print ( "instance" )

文件:Main.py

def allImports ( ) :
    __import__ ( "Test" )

我得到了什么:

>>> import Main
>>> Main.allImports()
>>> myInstance = Test ()
Traceback (most recent call last):
   File "<stdin>", line 1, in <module>
NameError: name 'Test' is not defined

我无法在 fromlist 中指定我必须导入 Test.py 中的哪个元素,因为我不应该知道它们。

我该怎么办?

【问题讨论】:

  • 你的代码不会只将模块“Test”导入到函数allImports()的本地范围内吗?
  • 实际上我发布的代码可以,但我希望能够在任何地方使用 Test 类而无需任何前缀。
  • 这可能有点繁琐。你能假设模块将包含一个与模块同名的类吗?
  • 没有@millimoose,有时模块会包含多个类

标签: python class dynamic import


【解决方案1】:

对于更接近您的意图的解决方案:

import importlib
def allImports(globals):
    mod = importlib.import_module('Test', globals['__name__'])

    try:
        keys = mod.__all__
    except AttributeError:
        keys = dir(mod)

    for key in keys:
        if not key.startswith('_'):
            globals[key] = getattr(mod, key)

# …

allImports(globals())
Test # should work, you can also look into dir(Test) to find the class.

如果您的模块没有__all__,上面的代码 /will/ 会严重破坏您的命名空间。要么确保你定义了__all__,要么修改allImports() 只导入你想要的东西。 (例如,仅类,或仅在模块中定义的类。这部分实际上取决于您的用例。)

【讨论】:

  • @millimose,你不知道如何在不给模块名称加前缀的情况下使该类可用吗?
  • @ibi0tux 我更改了代码来做一些可能类似于from Test import * 的事情。您可能应该小心,这样您才真正从使用它的模块中导入您想要的东西。
  • 泰。这似乎可行,但我不明白 all 有什么问题。
  • 如果您将任何模块导入Test 模块,但未指定__all__,则from Test import * 将重新导出这些模块。这意味着您将重新出口一大堆您可能不打算这样做的废话。使用您的模块的人不需要/不应该使用的辅助函数或类也是如此。 __all__ 的意思是“这些东西对使用这个模块的人来说很有趣”,定义它是一个最佳实践。
  • @millimose,我试过这个来导入包,但是当包中的文件导入另一个文件时失败。问题是不是由于相对路径不是基于包的根目录?
【解决方案2】:

使用__import__()加载模块时,必须在sys.modules中查找:

>>> import sys
>>> import Main
>>> Main.allImports()
>>> myInstance = sys.modules['Test'].Test()
instance
>>>

documentationhereherehere 中的更多信息。

【讨论】:

    【解决方案3】:

    此代码使__import__ ( "Test" ) 成为局部变量,因此您无法在函数外部访问它。

       def allImports ( ) :
            __import__ ( "Test" )
    

    试试:

    def allImports ( ) :
       test= __import__ ( "Test" )
       return test   #return the module
    
    >>> import Main
    >>> x=Main.allImports()  #store the returned module in x
    >>> myInstance = x.Test ()
    instance
    >>>myInstance
    <Test.Test instance at 0x011D7F80>
    

    【讨论】:

    • 谢谢,但是我应该如何使用这个返回的变量?
    • @user1504016 将其存储在某个变量中(x 这里),我编辑了我的解决方案。
    • 这种方式似乎更简单,但我想知道是否有任何方法可以在全局范围内导入类,以便可以在不使用范围名称前缀的情况下使用它。
    【解决方案4】:

    __import__ 不会神奇地修改全局或本地命名空间。

    模块和类是 Python 中的一等公民,也就是说,您可以将它们用作 Python 中的任何其他对象(绑定到名称、作为参数传递给函数、作为函数的值返回)。

    def allImports():
        return __import__("Test")
    
    Test_module = allImports()
    Test = Test_module.Test # class
    test_instance = Test()
    

    如果上述代码在函数内部,则将Test 放入全局命名空间:globals()['Test'] = Test。请注意,您很可能不需要它,并且有更好的方法可以在不修改函数内部的全局命名空间的情况下做任何您想做的事情。

    不鼓励使用__import__(),而是使用importlib.import_module()

    如果知道模块的名称和类,您可以在模块级别编写:

    from Test import Test
    

    【讨论】:

    • 好的。谢谢你。问题是模块的名称和类的名称都不知道。模块的名称是一个变量,我想导入所有的模块内容(类和函数)。
    • @ibi0tux:您可以使用inspect.getmembers()inspect.isclass()inspect.isfunction 谓词获取所有类和函数。如果您正在创建某种插件系统;考虑使用现有的,例如 yapsy
    • 是的,我正在研究一个插件系统,但它非常基础,所以我没有寻找现有系统。
    猜你喜欢
    • 1970-01-01
    • 2013-04-27
    • 1970-01-01
    • 2019-05-03
    • 2018-09-21
    • 1970-01-01
    • 1970-01-01
    • 2020-05-02
    • 1970-01-01
    相关资源
    最近更新 更多