在Python包中,我有文件结构
package/
__init__.py
import_me.py
Run Code Online (Sandbox Code Playgroud)
该文件import_me.py被认为提供了功能片段:
import re
import sys
def hello():
pass
Run Code Online (Sandbox Code Playgroud)
这样就package.import_me.hello可以通过动态导入import.不幸的是,这也让进口re和sys为package.import_me.re和package.import_me.sys分别.
有没有办法防止导入的模块import_me.py再次重新导出?优选地,这应该超出名称修改或下划线前缀导入的模块,因为在我的情况下,它可能在某些情况下造成安全问题.
Eth*_*man 13
没有简单的方法可以禁止从模块导入全局名称; Python根本就不是这样构建的.
如果您编写自己的__import__函数并隐藏内置函数,则可能实现令人生畏的目标,但我怀疑时间和测试的成本是否值得,也不完全有效.
您可以做的是使用前导下划线导入依赖模块,这是用于传达" 实现细节,使用风险自负 " 的标准Python习惯用法:
import re as _re
import sys as _sys
def hello():
pass
Run Code Online (Sandbox Code Playgroud)
注意
虽然删除导入的模块作为一种不允许导入它们的方式似乎可能有用,但它实际上并没有:
import re
import sys
def hello():
sys
print('hello')
del re
del sys
Run Code Online (Sandbox Code Playgroud)
然后导入和使用hello:
>>> import del_mod
>>> del_mod.hello()
Traceback (most recent call last):
File "<stdin>", line 1, in <module>
File "del_mod.py", line 5, in hello
sys
NameError: global name 'sys' is not defined
Run Code Online (Sandbox Code Playgroud)
另一种方法可能是将定义包装到初始化函数中。
## --- exporttest.py ---
def _init():
import os # effectively hidden
global get_ext # effectively exports it
def get_ext(filename):
return _pointless_subfunc(filename)
# underscore optional, but good
def _pointless_subfunc(filename): # for the sake of documentation
return os.path.splitext(filename)[1]
if __name__ == '__main__': # for interactive debugging (or doctest)
globals().update(locals()) # we want all definitions accessible
import doctest
doctest.testmod()
_init()
print('is ``get_ext`` accessible? ', 'get_ext' in globals())
print('is ``_pointless_subfunc`` accessible?', '_pointless_subfunc' in globals())
print('is ``os`` accessible? ', 'os' in globals())
Run Code Online (Sandbox Code Playgroud)
比较:
>>> python3 -m exporttest
is ``get_ext`` accessible? True
is ``_pointless_subfunc`` accessible? True
is ``os`` accessible? True
>>> python3 -c "import exporttest"
is ``get_ext`` accessible? True
is ``_pointless_subfunc`` accessible? False
is ``os`` accessible? False
Run Code Online (Sandbox Code Playgroud)
dir(exporttest)它没有杂乱。可悲的是,与import MODULE as _MODULE模式不同,它不能很好地与 pylint 配合使用。
C: 4, 4: Invalid constant name "get_ext" (invalid-name)
W: 4, 4: Using global for 'get_ext' but no assignment is done (global-variable-not-assigned)
W: 5, 4: Unused variable 'get_ext' (unused-variable)
Run Code Online (Sandbox Code Playgroud)
__all__在进一步的阅读中,我发现了Python的方式做到这一点是依靠__all__。它不仅控制导出的内容from MODULE import *,还控制显示的内容help(MODULE),根据“我们都是成年人”的口头禅,如果用户使用未公开记录的任何内容,则是用户自己的错。
工装在这种方法(例如,通过编辑器支持autoimports经历了最好的支持importmagic库)。
就我个人而言,我觉得整个“我们都是成年人”的口头禅相当天真;当在时间压力下工作而在交付更改之前没有机会完全理解代码库时,我们可以利用我们所能获得的任何帮助来防止“射中自己的脚”的情况。此外,即使是许多流行的软件包也没有真正遵循最佳实践,例如提供有用的交互式文档字符串或定义__all__. 但这是pythonic的方式。
没有简单的方法可以禁止从模块中导入全局名称。但实际上,您不需要。Python允许使用本地导入而不是全局导入:
def foo():
import sys
print(sys.copyright)
sys.copyright # Throws NameError
Run Code Online (Sandbox Code Playgroud)
简洁明了。
实际上,我认为使用本地进口应该是一个好习惯,而全球进口仅仅是对C或对C的继承的敬意。
UPD:明显的缺点是,import sys每次调用此函数都会执行该语句,这可能负担不起。但是您可以改为创建一个可调用对象:
class Callable(object):
import sys as _sys
def __call__(self):
print(self._sys.copyright)
foo = Callable()
foo()
Run Code Online (Sandbox Code Playgroud)
尽管我个人不喜欢这种方法,但对于通用类可能会更好。