繁体   English   中英

如何在不污染其命名空间的情况下在python模块中执行导入?

[英]How should I perform imports in a python module without polluting its namespace?

我正在开发一个用于处理一些科学数据的Python包。 其他模块和包中有多个经常使用的类和函数,包括numpy,我几乎在包的任何模块中定义的每个函数中都需要这些类和函数。

什么是Pythonic处理它们的方法? 我考虑了多种变体,但每种变体都有其自身的缺点。

  • 使用from foreignmodule import Class1, Class2, function1, function2模块级别的类from foreignmodule import Class1, Class2, function1, function2
    然后可以从每个函数轻松访问导入的函数和类。 另一方面,它们污染了模块命名空间,使得dir(package.module)help(package.module)与导入的函数混杂在一起

  • 使用from foreignmodule import Class1, Class2, function1, function2级别导入类
    函数和类很容易访问,并且不会污染模块,但是在每个函数中从多达十几个模块的导入看起来像很多重复的代码。

  • 使用import foreignmodule在模块级别import foreignmodule
    通过将模块名称添加到每个函数或类调用的需要来补偿不太多的污染。

  • 使用一些人为的解决方法,比如使用函数体进行所有这些操作,并仅返回要导出的对象......就像这样

     def _export(): from foreignmodule import Class1, Class2, function1, function2 def myfunc(x): return function1(x, function2(x)) return myfunc myfunc = _export() del _export 

    这设法解决了问题,模块名称空间污染和功能的易用性......但它似乎根本不是Pythonic。

那么最具Pythonic的解决方案是什么? 我忽略了另一个好的解决方案吗?

继续from W import X, Y, Z ,然后使用__all__特殊符号来定义您希望人们从模块导入的实际符号:

__all__ = ('MyClass1', 'MyClass2', 'myvar1', …)

这定义了如果从模块import * ,将导入到用户模块中的符号。

一般来说,Python程序员应该使用dir()来弄清楚如何使用你的模块,如果他们这样做,它可能表明在其他地方有问题。 他们应该阅读您的文档或键入help(yourmodule)以了解如何使用您的库。 或者他们可以自己浏览源代码,在这种情况下(a)你导入的东西和你定义的东西之间的区别很明显,(b)他们会看到__all__声明并知道他们应该玩哪些玩具。

如果你尝试在这样的情况下支持dir()用于没有设计它的任务,你将不得不对你自己的代码设置恼人的限制,因为我希望从这里的其他答案中可以清楚地看到。 我的建议:不要这样做! 看一下标准库的指导:它from … import …只要代码清晰和简洁需要它,并提供(1)信息性文档字符串,(2)完整文档,和(3)可读代码,以便没有人必须在模块上运行dir()并尝试将导入与模块中实际定义的内容区分开来。

我见过的一种技术,包括在标准库中,使用import module as _modulefrom module import var as _varfrom module import var as _var导入的模块/变量分配from module import var as _var下划线开头的名称。

其结果是,遵循通常的Python约定的其他代码将这些成员视为私有。 这甚至适用于不查看__all__代码,例如IPython的自动完成功能。

Python 3.3 random模块的一个例子:

from warnings import warn as _warn
from types import MethodType as _MethodType, BuiltinMethodType as _BuiltinMethodType
from math import log as _log, exp as _exp, pi as _pi, e as _e, ceil as _ceil
from math import sqrt as _sqrt, acos as _acos, cos as _cos, sin as _sin
from os import urandom as _urandom
from collections.abc import Set as _Set, Sequence as _Sequence
from hashlib import sha512 as _sha512

另一种技术是在函数范围内执行导入,以便它们成为局部变量:

"""Some module"""
# imports conventionally go here
def some_function(arg):
    "Do something with arg."
    import re  # Regular expressions solve everything
    ...

这样做的主要原因是它实际上是懒惰的,延迟了模块依赖项的导入,直到实际使用它们为止。 假设模块中的一个函数依赖于特定的大型库。 导入文件顶部的库意味着导入模块将加载整个库。 这样,导入模块可以很快,只有实际调用该函数的客户端代码会产生加载库的成本。 此外,如果依赖库不可用,则不需要依赖功能的客户端代码仍可以导入模块并调用其他功能。 缺点是使用函数级导入会掩盖代码的依赖关系。

Python 3.3的os.py

def get_exec_path(env=None):
    """[...]"""
    # Use a local import instead of a global import to limit the number of
    # modules loaded at startup: the os module is always loaded at startup by
    # Python. It may also avoid a bootstrap issue.
    import warnings

整个导入模块: import foreignmodule 您声称的缺点实际上是一个好处。 也就是说,在模块名称之前添加代码可以使代码更易于维护,并使其更加自我记录。

从现在起六个月,当你看到像foo = Bar(baz)这样的代码时,你可能会问自己哪个模块Bar来自哪个,但是foo = cleverlib.Bar它不是一个谜。

当然,你拥有的进口越少,问题就越少。 对于依赖性很少的小程序来说,这并不重要。

当你发现自己在问这样的问题时,问问自己是什么让代码更容易理解,而不是让代码更容易编写。 你写了一次,但你读了很多。

对于这种情况,我会使用all_imports.py文件,其中包含所有文件

from foreignmodule import .....
from another module import .....

然后在你的工作模块中

import all_imports as fgn # or whatever you want to prepend
...
something = fgn.Class1()

另一件需要注意的事情

__all__ = ['func1', 'func2', 'this', 'that']

现在,模块中但不在模块的__all__任何函数/类/变量/等都不会显示在help() ,也不会from mymodule import *请参阅使python导入更具结构化? 了解更多信息。

我会妥协,只为外国模块选择一个简短的别名:

import foreignmodule as fm

它可以完全避免污染(可能是更大的问题),至少可以减少前期负担。

我知道这是一个老问题。 它可能不是'Pythonic',但是我发现只出口某些模块定义的最干净的方法就像你发现的那样,全局地将模块包装在一个函数中。 但是,不是返回它们,而是导出名称,你可以简单地全局化它们(全局因此本质上变成了一种'export'关键字):

def module():
    global MyPublicClass,ExportedModule

    import somemodule as ExportedModule
    import anothermodule as PrivateModule

    class MyPublicClass:
        def __init__(self):
            pass

    class MyPrivateClass:
        def __init__(self):
            pass

module()
del module

我知道这与你原来的结论没什么不同,但坦率地说,这似乎是最干净的选择。 另一个优点是,您可以将以这种方式编写的任意数量的模块组合到一个文件中,并且它们的私有条款不会重叠:

def module():
    global A

    i,j,k = 1,2,3

    class A:
        pass

module()
del module

def module():
    global B

    i,j,k = 7,8,9 # doesn't overwrite previous declarations

    class B:
        pass

module()
del module

但是,请记住,他们的公开定义当然会重叠。

暂无
暂无

声明:本站的技术帖子网页,遵循CC BY-SA 4.0协议,如果您需要转载,请注明本站网址或者原文地址。任何问题请咨询:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM