我正在开发一个用于处理一些科学数据的Python包。其他模块和包中有多个经常使用的类和函数,包括numpy,我几乎在包的任何模块中定义的每个函数中都需要它。
Pythonic处理它们的方法是什么?我考虑了多种变体,但每种变体都有其自身的缺点。
使用from foreignmodule import Class1, Class2, function1, function2
在模块级别导入类
然后可以从每个函数轻松访问导入的函数和类。另一方面,它们污染了模块名称空间,使dir(package.module)
和help(package.module)
混乱了导入的函数
使用from foreignmodule import Class1, Class2, function1, function2
来导入功能级别的类
函数和类很容易访问,并且不会污染模块,但是在每个函数中从多达十几个模块导入的内容看起来都是重复的代码。
使用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的解决方案是什么?我忽略了另一个好的解决方案吗?
答案 0 :(得分:19)
继续执行您的常规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()
并尝试将导入与模块中实际定义的内容区分开来。
答案 1 :(得分:9)
整体导入模块:import foreignmodule
。您声称的缺点实际上是一个好处。也就是说,在模块名称之前添加代码可以使代码更易于维护,并使其更加自我记录。
从现在起六个月,当您查看一行代码foo = Bar(baz)
时,您可能会问自己哪个模块Bar
来自哪个模块,但foo = cleverlib.Bar
这个模块更不是一个谜。
当然,您拥有的进口量越少,问题就越少。对于依赖性很少的小程序来说,这并不重要。
当你发现自己在问这样的问题时,问问自己是什么让代码更容易理解,而不是让代码更易于编写。你写了一次,但你读了很多。
答案 2 :(得分:9)
我见过的一种技术,包括在标准库中,使用import module as _module
或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
答案 3 :(得分:3)
对于这种情况,我会使用包含所有
的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 *
导入}有关详细信息,请参阅Making python imports more structured?。
答案 4 :(得分:1)
我会妥协,只为外国模块选择一个简短的别名:
import foreignmodule as fm
它可以完全避免污染(可能是更大的问题),至少可以减少前期负担。
答案 5 :(得分:0)
我知道这是一个老问题。可能不是“ Pythonic”,但是我发现,仅导出某些模块定义的最简洁的方法是,如您所知,确实是将模块全局包装在函数中。但是,您无需将其返回以导出名称,而只需对其进行全球化(因此global实际上本质上成为一种“ 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
但是,请记住,它们的 public 定义当然会重叠。