【问题标题】:Python: importing through function to main namespacePython:通过函数导入到主命名空间
【发布时间】:2010-12-12 17:41:46
【问题描述】:

(重要:请参阅下面的更新。)

我正在尝试编写一个函数,import_something,它将对某些模块很重要。 (对于这个问题,哪个无关紧要。)问题是,我希望在调用函数的级别导入这些模块。例如:

import_something() # Let's say this imports my_module
my_module.do_stuff() #

这可能吗?

更新:

抱歉,我最初的措辞和示例具有误导性。我将尝试解释我的整个问题。我拥有的是一个包,里面有一些模块和包。在其__init__.py 中,我想导入所有模块和包。因此,在程序的其他地方,我导入了整个包,并遍历它已导入的模块/包。

(为什么?这个包叫crunchers,里面定义了各种各样的crunchers,比如CruncherThreadCruncherProcess,将来可能是MicroThreadCruncher。我想让crunchers包自动将所有的 cruncher 都放在其中,所以稍后在程序中使用 crunchers 时,我知道它可以准确地分辨出我定义了哪些 cruncher。)

我知道如果我完全避免使用函数,并且使用 for 循环等在主级别上执行所有导入,我可以解决这个问题。但是很丑,我想看看能不能避免。

如果还有什么不清楚的,请在cmets中提问。

【问题讨论】:

    标签: python import


    【解决方案1】:

    函数能够将某些内容返回到调用它们的位置。它称为它们的返回值:p

    def import_something():
        # decide what to import
        # ...
        mod = __import__( something )
        return mod
    my_module = import_something()
    my_module.do_stuff()
    

    风格不错,没有麻烦。

    关于您的更新,我认为向您添加这样的内容 __init__.py 可以满足您的需求:

    import os
    
    # make a list of all .py files in the same dir that dont start with _
    __all__ = installed = [ name for (name,ext) in ( os.path.splitext(fn) for fn in os.listdir(os.path.dirname(__file__))) if ext=='.py' and not name.startswith('_') ]
    for name in installed:
        # import them all
        __import__( name, globals(), locals())
    

    其他地方:

    import crunchers
    crunchers.installed # all names
    crunchers.cruncherA # actual module object, but you can't use it since you don't know the name when you write the code
    # turns out the be pretty much the same as the first solution :p
    mycruncher = getattr(crunchers, crunchers.installed[0])  
    

    【讨论】:

    • 为什么要绕过解释器的导入逻辑?
    • @THC4k:我使用自定义函数导入的原因之一是我不知道要导入的模块的名称,我希望将其导入到它的自己的名字。
    • 例如,如果您想决定在运行时使用哪个模板引擎(mako/cheetah/etc),您需要__import__
    • @cool-RR 你想要import Aimport B 但保留他们的名字?导入后的代码应该是什么样子? A 或 B 将是未定义的 ...
    • 我认为我在问题中的示例具有误导性,对不起。我并没有在执行import_something() 的文件中做任何事情,但我导入的是某处并迭代它已导入的模块。抱歉,我意识到现在还不清楚,我的错。
    【解决方案2】:

    根据__import__的帮助:

    __import__(name, globals={}, locals={}, fromlist=[], level=-1) -> module
    
    Import a module.  The globals are only used to determine the context;
    they are not modified. ...
    

    因此,您可以简单地获取父框架的全局变量并将其用于__import__ 调用。

    def import_something(s):
        return __import__(s, sys._getframe(1).f_globals)
    

    注意:在 2.6 之前,__import__ 的签名不同之处在于它只是具有可选参数而不是使用 kwargs。由于globals 在这两种情况下都是第二个参数,所以上面调用它的方式很好。如果您决定使用任何其他参数,请注意一些事项。

    【讨论】:

    • 你仍然只是返回模块,我认为 OP 想要一些比这更神奇的东西。 (否则你可以直接调用 import 而不是包装它)。
    【解决方案3】:

    您可以使用 CPython 中的父框架进行猴子操作,以将模块安装到该框架(并且仅该框架)的本地。缺点是 a) 这真的很 hackish 并且 b) sys._getframe() 不能保证存在于其他 python 实现中。

    def importer():
      f = sys._getframe(1) # Get the parent frame
      f.f_locals["some_name"] = __import__(module_name, f.f_globals, f.f_locals)
    

    您仍然需要将模块安装到 f_locals 中,因为 import 实际上不会为您执行此操作 - 您只需为适当的上下文提供父框架本地和全局。

    然后在你的调用函数中你可以有:

    def foo():
      importer() # Magically makes 'some_name' available to the calling function
      some_name.some_func()
    

    【讨论】:

      【解决方案4】:

      你在寻找这样的东西吗?

      def my_import(*names):
          for name in names:
              sys._getframe(1).f_locals[name] = __import__(name)
      

      那么你可以这样称呼它:

      my_import("os", "re")
      

      namelist = ["os", "re"]
      my_import(*namelist)
      

      【讨论】:

      • globals 会将其导入一级还是顶级?它应该只有一个级别,因此它的行为类似于import 语句。
      • 你说得对,我相应地更改了代码。好吧,看看其他解决方案,现在看起来就像 jamessan 和 Nick 不久前发布的那样。
      • (如何)我可以使用它来复制导入语句,例如:“from module import fun as F”和“import mod.submod as S”?
      猜你喜欢
      • 2017-01-25
      • 1970-01-01
      • 2010-12-19
      • 1970-01-01
      • 2021-10-03
      • 2016-08-29
      • 2011-09-09
      • 1970-01-01
      • 1970-01-01
      相关资源
      最近更新 更多