我试图找到一个全面的指南,是否最好使用导入模块或从模块导入。我刚刚开始学习Python,我正试图从最佳实践开始。
基本上,我希望任何人都能分享他们的经验,其他开发者有什么偏好,以及避免任何陷阱的最佳方法是什么?
我试图找到一个全面的指南,是否最好使用导入模块或从模块导入。我刚刚开始学习Python,我正试图从最佳实践开始。
基本上,我希望任何人都能分享他们的经验,其他开发者有什么偏好,以及避免任何陷阱的最佳方法是什么?
当前回答
有很多答案,但没有一个提到测试(使用unittest或pytest)。
博士tl;
对外部模块使用import foo来简化测试。
艰难的方式
从模块中单独导入类/函数(从foo import bar中)会使红绿重构周期变得冗长乏味。例如,如果我的文件看起来像
# my_module.py
from foo import bar
class Thing:
def do_thing(self):
bar('do a thing')
我的测试是
# test_my_module.py
from unittest.mock import patch
import my_module
patch.object(my_module, 'bar')
def test_do_thing(mock_bar):
my_module.Thing().do_thing()
mock_bar.assert_called_with('do a thing')
乍一看,这似乎很棒。但是如果我想在不同的文件中实现Thing类会发生什么呢?我的结构将不得不像这样改变……
# my_module.py
from tools import Thing
def do_thing():
Thing().do_thing()
# tools.py
from foo import bar
class Thing:
def do_thing(self):
bar('do a thing')
# test_my_module.py
from unittest.mock import patch
import my_module
import tools # Had to import implementation file...
patch.object(tools, 'bar') # Changed patch
def test_do_thing(mock_bar):
my_module.do_thing() # Changed test (expected)
mock_bar.assert_called_with('do a thing')
不幸的是,由于我使用from foo import bar,我需要更新我的补丁来引用工具模块。从本质上讲,由于我的测试对实现了解太多,因此要进行这个重构,需要更改的内容要比预期的多得多。
更好的方法
使用import foo,我的测试可以忽略模块是如何实现的,而只是对整个模块打补丁。
# my_module.py
from tools import Thing
def do_thing():
Thing().do_thing()
# tools.py
import foo
class Thing:
def do_thing(self):
foo.bar('do a thing') # Specify 'bar' is from 'foo' module
# test_my_module.py
from unittest.mock import patch
import my_module
patch('foo') # Patch entire foo module
def test_do_thing(mock_foo):
my_module.do_thing() # Changed test (expected)
mock_foo.bar.assert_called_with('do a thing')
测试知道的实现细节越少越好。这样,如果您提出了更好的解决方案(使用类而不是函数,使用额外的文件来分离思想,等等),那么在您的测试中需要更改的内容就会更少,以适应重构。
其他回答
这里还有一个没有提到的区别。这是从http://docs.python.org/2/tutorial/modules.html逐字复制的
注意,当使用
from package import item
项目可以是包的子模块(或子包),也可以是包中定义的其他名称,如函数、类或变量。import语句首先测试项目是否在包中定义;如果不是,则假定它是一个模块,并尝试加载它。如果找不到,则引发ImportError异常。
相反,当使用语法像
import item.subitem.subsubitem
除最后一项外,每一项都必须是一个包裹;最后一项可以是模块或包,但不能是前一项中定义的类、函数或变量。
import module和from module import foo之间的区别主要是主观的。选择一个你最喜欢的,并始终如一地使用它。这里有几点可以帮助你做决定。
导入模块
优点: 更少地维护您的导入语句。不需要添加任何额外的导入就可以开始使用模块中的另一个项 缺点: 输入模块。Foo在你的代码中可能是乏味和冗余的(单调可以通过使用import module作为mo然后输入mo. Foo来最小化)
从模块导入foo
优点: 使用foo时输入更少 对可以访问模块的哪些项有更多的控制 缺点: 要使用模块中的新项,必须更新import语句 你失去了关于foo的上下文。例如,与math.ceil()相比,ceil()的作用就不太清楚了。
这两种方法都可以,但不要使用from module import *。
对于任何合理的大型代码集,如果您导入*,则可能会将其固化到模块中,无法删除。这是因为很难确定代码中使用的哪些项来自'module',这很容易让你认为你不再使用导入,但很难确定。
正如Jan Wrobel提到的,不同导入的一个方面是导入的披露方式。
模块mymath
from math import gcd
...
使用mymath:
import mymath
mymath.gcd(30, 42) # will work though maybe not expected
如果我导入gcd只是为了内部使用,而不向mymath的用户公开它,这可能会很不方便。我经常遇到这种情况,在大多数情况下,我想“保持我的模块干净”。
除了Jan Wrobel提出的通过使用导入数学来掩盖这一点之外,我已经开始通过使用前导下划线来隐藏导入,以避免公开:
# for instance...
from math import gcd as _gcd
# or...
import math as _math
在较大的项目中,这种“最佳实践”允许我准确地控制向后续导入公开的内容和不公开的内容。这使我的模块保持干净,并在一定规模的项目中回报。
我自己的答案主要取决于首先,我将使用多少不同的模块。如果我只打算用一两个,我会经常用from…import,因为它在文件的其余部分减少了击键,但如果我要使用许多不同的模块,我更喜欢只使用import,因为这意味着每个模块引用都是自文档化的。我可以看到每个符号的来源,而不需要到处寻找。
通常我更喜欢简单导入的自文档风格,只更改from..当我必须输入模块名的次数增加到10到20次时,即使只有一个模块被导入。
我个人总是用
from package.subpackage.subsubpackage import module
然后访问所有
module.function
module.modulevar
等。原因是,与此同时,您可以进行简短的调用,并且可以清楚地定义每个例程的模块名称空间,如果您必须在源代码中搜索给定模块的使用情况,这是非常有用的。
不用说,不要使用import *,因为它会污染您的命名空间,并且它不会告诉您给定函数来自何处(来自哪个模块)
当然,如果在两个不同的包中对两个不同的模块使用相同的模块名,您可能会遇到麻烦,例如
from package1.subpackage import module
from package2.subpackage import module
在这种情况下,您当然会遇到麻烦,但是强烈地暗示您的包布局有缺陷,您必须重新考虑它。