在 __main__.py 中使用模块自己的对象
Posted
技术标签:
【中文标题】在 __main__.py 中使用模块自己的对象【英文标题】:Using module's own objects in __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
?
【问题讨论】:
起来!我遇到过很多次这样的问题,看到如此明显的行为没有按照预期的方式工作,我感到非常失望。 【参考方案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__.py
和python -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
【讨论】:
这是你的答案?来点上下文或描述来配合你正在做的事情怎么样? 这是一个巨大而丑陋的黑客。通过本地人复制变量?哈哈哈对不起,但这真的很有趣。以上是关于在 __main__.py 中使用模块自己的对象的主要内容,如果未能解决你的问题,请参考以下文章
尽管有 __init__.py,Python 仍无法导入自定义模块