问题
请考虑以下布局:
package/
main.py
math_helpers/
mymath.py
__init__.py
mymath.py
包含:
import math
def foo():
pass
在main.py
中,我希望能够像这样使用mymath.py
中的代码:
import math_helpers
math_helpers.foo()
为了这样做,__init__.py
包含:
from .mymath import *
但是,在mymath.py
中导入的模块现在位于math_helpers
命名空间中,例如math_helpers.math
是可访问的。
当前方法
我在mymath.py
的末尾添加以下内容。
import types
__all__ = [name for name, thing in globals().items()
if not (name.startswith('_') or isinstance(thing, types.ModuleType))]
这似乎可行,但这是正确的方法吗?
__all__
是确定星型导入中出现的内容的推荐方法。您的方法是正确的,完成后可以进一步清理名称空间:
import types
__all__ = [name for name, thing in globals().items()
if not (name.startswith('_') or isinstance(thing, types.ModuleType))]
del types
虽然不建议使用,但您也可以直接在模块外清理元素,以使它们根本不显示。如果您需要在模块中定义的函数中使用它们,这将是一个问题,因为每个函数对象都有一个__globals__
引用,该引用绑定到其父模块的__dict__
。但是,如果仅导入math_helpers
来调用math_helpers.foo()
,并且不需要在模块的其他位置对其进行持久引用,则只需在最后将其取消链接即可:
del math_helpers
Long Version
模块导入在模块__dict__
的命名空间中运行模块的代码。该词典中存在任何通过类定义,函数定义,直接分配或其他方式在顶层绑定的名称。有时,像我建议使用types
一样,需要清理中间变量。假设您的模块如下所示:
test_module.py
import math import numpy as np def x(n): return math.sqrt(n) class A(np.ndarray): pass import types __all__ = [name for name, thing in globals().items() if not (name.startswith('_') or isinstance(thing, types.ModuleType))]
在这种情况下,__all__
将为['x', 'A']
。但是,模块本身将包含以下名称:'math', 'np', 'x', 'A', 'types', '__all__'
。如果最后运行
del types
,它将从名称空间中删除该名称。显然,这是安全的,因为一旦构建了types
,就不会在任何地方引用__all__
。类似地,如果您想通过添加
np
来删除del np
,那就可以了。在模块代码的末尾完全构造了类A
,因此它不需要全局名称np
即可引用其父类。
math
并非如此。如果要在模块代码的末尾执行del math
,则功能x
将不起作用。如果导入模块,则可以看到x.__globals__
是模块的__dict__
:
import test_module test_module.__dict__ is test_module.x.__globals__
如果从模块词典中删除math
并调用test_module.x
,则会得到
NameError: name 'math' is not defined
因此,在某些非常特殊的情况下,您可能可以清理mymath.py
的名称空间,但这不是推荐的方法,因为它仅适用于某些情况。最后,坚持使用
__all__
。一个类似的故事
一次,我有两个模块实现了相似的功能,但是适用于不同类型的最终用户。我想从模块a
中复制到模块b
中,有几个功能。问题是我希望这些功能像在模块b
中定义的那样工作。不幸的是,它们依赖于a
中定义的常数。b
定义了自己的常量版本。例如:a.py
value = 1 def x(): return value
b.py
from a import x value = 2
我希望b.x
访问b.value
而不是a.value
。我通过在b.py
中添加以下内容(基于https://stackoverflow.com/a/13503277/2988730)实现了这一目标:
import functools, types x = functools.update_wrapper(types.FunctionType(x.__code__, globals(), x.__name__, x.__defaults__, x.__closure__), x) x.__kwdefaults__ = x.__wrapped__.__kwdefaults__ x.__module__ = __name__ del functools, types
我为什么要告诉你这一切?好吧,您可以为模块创建一个版本,该版本在名称空间中没有任何杂乱的名称。但是,您将看不到函数中全局变量的更改。这只是使python超出其正常使用范围的一种做法。我强烈不建议这样做,但这是一个示例模块,可以有效冻结涉及功能的__dict__
。它具有与上述test_module
相同的成员,但全局名称空间中没有模块:
import math import numpy as np def x(n): return math.sqrt(n) class A(np.ndarray): pass import functools, types, sys def wrap(obj): """ Written this way to be able to handle classes """ for name in dir(obj): if name.startswith('_'): continue thing = getattr(obj, name) if isinstance(thing, FunctionType) and thing.__module__ == __name__: setattr(obj, name, functools.update_wrapper(types.FunctionType(thing.func_code, d, thing.__name__, thing.__defaults__, thing.__closure__), thing) getattt(obj, name).__kwdefaults__ = thing.__kwdefaults__ elif isinstance(thing, type) and thing.__module__ == __name__: wrap(thing) d = globals().copy() wrap(sys.modules[__name__]) del d, wrap, sys, math, np, functools, types
所以,请永远不要这样做!但是,如果这样做,请将其放在实用程序类中的某个位置。
• 从父目录导入脚本
• python在运行文件时不能导入模块,但是在交互式shell中可以导入模块
• 在哪里可以找到 Flask 项目中安装的所有第三方包或模块
• 对 JSON API 的 GET 请求偶尔会返回空白响应
• python sys.path ModuleNotFoundError:在 Docker 容器中导入时没有名为“file_name”的模块
• 当我运行代码时,它说“TypeError: unlink() got an unexpected keyword argument 'missing_ok'”
• 使用 DagBag 在 Cloud Composer 中测试 dags 时出现 ModuleNotFoundError
• Visual Studio 2019 中的 Runsettings 文件默认不包括所有内容
• AssertionError: SparkContext._active_spark_context 不是 None
• 为什么 pytest 模拟程序全局修补模块方法(取决于模块导入)?
• 在 pytest 测试中使用 runpy 时如何防止缓存模块/变量?