【问题标题】:Using module's own objects in __main__.py在 __main__.py 中使用模块自己的对象
【发布时间】:2011-03-25 14:06:42
【问题描述】:

我正在尝试从其__main__.py 内部访问模块的数据。

结构如下:

mymod/
    __init__.py
    __main__.py

现在,如果我像这样在__init__.py 中公开一个变量:

__all__ = ['foo']
foo = {'bar': 'baz'}

如何从__main__.py 访问foo

【问题讨论】:

  • 起来!我遇到过很多次这样的问题,看到如此明显的行为没有按照预期的方式工作,我感到非常失望。

标签: python main init


【解决方案1】:

您需要在sys.path 中已有该软件包,将包含mymod 的目录添加到__main__.py 中的sys.path,或使用-m 开关。

mymod 添加到路径看起来像这样(在__main__.py 中):

import sys
import os
path = os.path.dirname(sys.modules[__name__].__file__)
path = os.path.join(path, '..')
sys.path.insert(0, path)
from myprog import function_you_referenced_from_init_file

使用-m 开关希望:

python -m mymod

更多讨论请见this answer

【讨论】:

  • from mymod import foo 之前添加sys.path.append(os.getcwd()) 效果很好,谢谢
  • 我认为这依赖于从父目录执行包的人。在这种情况下,python mymod.
  • sys.path.append( os.path.dirname(__file__) ) 会将正在执行的文件的目录添加到sys.path中
  • 但是请注意,在调用 sys.path.insert() 之前,包含 __main__.py 的目录位于 sys.path 中,这使得与 __main__.py 相邻的所有模块成为顶级模块,而不是命名空间下__main__.py 的包裹。如果包包含与 stdlib 模块相同的模块名称,则包提供的模块将在 stdlib 模块之前被选择。改用sys.path[0] = path 可能会更好(覆盖默认路径,以便只有包的命名空间在 sys.path 中)。
【解决方案2】:

我遇到这类事情最多的问题是,我经常想将__init__.py 文件作为脚本运行以测试功能,但在加载包时不应运行这些文件。对于python <package>/__init__.pypython -m <package> 之间的不同执行路径,有一个有用的解决方法。

  • $ python -m <module> 执行 <package>/__main__.py__init__.py 未加载。
  • $ python <package>/__init__.py 只是像普通脚本一样执行脚本__init__.py

问题

当我们希望__init__.py 有一个if __name__ == '__main__': ... 子句使用来自__main__.py 的内容时。我们不能导入__main__.py,因为它总是会从解释器的路径中导入__main__.pyc。 (除非……我们使用绝对路径导入技巧,这会导致很多其他混乱)。


解决方案 一个解决方案:)

为模块的__main__ 使用两个脚本文件:

<package>/
         __init__.py
         __main__.py
         main.py

# __init__.py

# ...
# some code, including module methods and __all__ definitions

__all__ = ['foo', 'bar']
bar = {'key': 'value'}
def foo():
    return bar
# ...
if __name__ == '__main__':
    from main import main
    main.main()

# __main__.py

# some code...such as:
import sys
if (len(sys.argv) > 1 and sys.argv[1].lower() == 'option1'):
    from main import main()
    main('option1')
elif (len(sys.argv) > 1 and sys.argv[1].lower() == 'option2'):
    from main import main()
    main('option2')
else:
    # do something else?
    print 'invalid option. please use "python -m <package> option1|option2"'

# main.py

def main(opt = None):
    if opt == 'option1':
        from __init__ import foo
        print foo()
    elif opt == 'option2':
        from __init__ import bar
        print bar.keys()
    elif opt is None:
        print 'called from __init__'

main.py 中的导入在我们从 __init__.py 运行的情况下可能并不理想,因为我们正在将它们重新加载到另一个模块的本地范围中,尽管已经在 __init__.py 中加载了它们,但是显式装载应避免循环装载。如果你确实在你的main.py 中再次加载整个__init__ 模块,它不会被加载为__main__,所以就循环加载而言应该是安全的。

【讨论】:

    【解决方案3】:

    a package__init__ 模块就像包本身的成员一样,所以直接从mymod 导入对象:

    from mymod import foo
    

    或者

    from . import foo
    

    如果您喜欢简洁,请阅读relative imports。例如,您需要像往常一样确保不以mymod/__main__.py 调用模块,因为这会阻止Python 将mymod 检测为包。你不妨看看distutils

    【讨论】:

    • 我想让模块目录可执行。
    • 是的,我已经尝试了这些建议,但它们对我不起作用。
    • 当我使用from . import foo 时,我得到一个ValueError: Attempted relative import in non-package,否则ImportError: No module named mymod
    【解决方案4】:

    如果您使用python -m mymod 运行模块,则__main__.py 中的代码将能够从模块的其余部分导入,而无需将模块添加到sys.path

    【讨论】:

      【解决方案5】:

      我发现第一个答案很有用(即破解 sys.path),但在 Python 3.4 中添加了 pathlib,我发现以下代码更加简单和 Pythonic:

      import sys
      from pathlib import Path
      
      # You don't need to .insert(), just append
      sys.path.append(str(Path(__file__).parent.parent))
      

      【讨论】:

        【解决方案6】:

        模块目录结构如下:

        py/
           __init__.py
           __main__.py
        

        __init__.py

        #!/usr/bin/python3
        #
        # __init__.py
        #
        
        __all__ = ['foo']
        foo = {'bar': 'baz'}
        info = { "package": __package__,
                 "name": __name__,
                 "locals": [x for x in locals().copy()] }
        print(info)
        

        __main__.py

        #!/usr/bin/python3
        #
        # __main__.py
        #
        
        info = { "package": __package__,
                 "name": __name__,
                 "locals": [x for x in locals().copy()] }
        print(info)
        from . import info as pyinfo
        print({"pyinfo: ": pyinfo})
        

        使用-m 标志将模块作为脚本执行

        $ python -m py

        # the printout from the 'print(info)' command in __init__.py
        {'name': 'py', 'locals': ['__all__', '__builtins__', '__file__', '__package__', '__path__', '__name__', 'foo', '__doc__'], 'package': None}
        # the printout from the 'print(info)' command in __main__.py
        {'name': '__main__', 'locals': ['__builtins__', '__name__', '__file__', '__loader__', '__doc__', '__package__'], 'package': 'py'}
        # the printout from the 'print(pyinfo)' command in __main__.py
        {'pyinfo: ': {'name': 'py', 'locals': ['__all__', '__builtins__', '__file__', '__package__', '__path__', '__name__', 'foo', '__doc__'], 'package': None}}
        

        【讨论】:

        • 这是你的答案?来点上下文或描述来配合你正在做的事情怎么样?
        • 这是一个巨大而丑陋的黑客。通过本地人复制变量?哈哈哈对不起,但这真的很有趣。
        猜你喜欢
        • 1970-01-01
        • 1970-01-01
        • 1970-01-01
        • 2021-03-11
        • 2012-10-16
        • 2012-03-02
        • 2017-07-21
        • 2021-12-24
        • 2013-09-03
        相关资源
        最近更新 更多