我试图找到一份关于最好使用 import module
还是 from module import
的综合指南。我刚刚开始使用 Python,并且正在尝试从最佳实践入手。
基本上,我希望是否有人可以分享他们的经验,其他开发人员有什么偏好以及避免任何问题的最佳方法是什么?
from … import …
syntax in Python & 'import module' vs. 'from module import function'
'from module import X,Y,Z
与 'from module import *
之间存在天壤之别。后者会污染您的命名空间,并且可能会根据模块中发生的情况给出不可预测的结果。更糟糕的是使用多个模块执行 from module import *
。
import module
和 from module import foo
之间的区别主要是主观的。选择您最喜欢的一个,并在使用时保持一致。这里有一些要点可以帮助您做出决定。
import module
优点:对导入语句的维护更少。不需要添加任何额外的导入来开始使用模块中的另一个项目
减少对导入语句的维护。不需要添加任何额外的导入来开始使用模块中的另一个项目
缺点:在您的代码中键入 module.foo 可能是乏味和冗余的(可以通过使用 import module as mo 然后键入 mo.foo 来最小化乏味)
在您的代码中输入 module.foo 可能是乏味且多余的(可以通过使用 import module as mo 然后输入 mo.foo 来最小化乏味)
from module import foo
优点:使用 foo 的输入更少 更多地控制可以访问模块的哪些项目
减少打字以使用 foo
更好地控制可以访问模块的哪些项目
缺点:要使用模块中的新项目,您必须更新导入语句您丢失了有关 foo 的上下文。例如,与 math.ceil() 相比,ceil() 的作用不太清楚
要使用模块中的新项目,您必须更新导入语句
你失去了关于 foo 的上下文。例如,与 math.ceil() 相比,ceil() 的作用不太清楚
任何一种方法都可以接受,但不要使用 from module import *
。
对于任何合理的大型代码集,如果您import *
,您可能会将其固定到模块中,无法删除。这是因为很难确定代码中使用的哪些项目来自“模块”,因此很容易达到您认为不再使用 import
但很难确定的地步.
这里还有一个未提及的细节,与写入模块有关。当然,这可能不是很常见,但我不时需要它。
由于引用和名称绑定在 Python 中的工作方式,如果您想从该模块外部更新模块中的某些符号,例如 foo.bar,并让其他导入代码“看到”该更改,则必须导入 foo a某种方式。例如:
模块 foo:
bar = "apples"
模块一:
import foo
foo.bar = "oranges" # update bar inside foo module object
模块 b:
import foo
print foo.bar # if executed after a's "foo.bar" assignment, will print "oranges"
但是,如果您导入符号名称而不是模块名称,这将不起作用。
例如,如果我在模块 a 中执行此操作:
from foo import bar
bar = "oranges"
a
之外的任何代码都不会将 bar
视为“橙子”,因为我对 bar
的设置仅影响了模块 a
内的名称“bar”,它没有“触及”foo
模块对象,并且更新其 bar
。
global
的函数有同样的问题。
尽管很多人已经解释了 import
与 import from
,但我想尝试更多地解释一下幕后发生的事情,以及它改变的所有地方。
导入 foo:
导入 foo
,并在当前命名空间中创建对该模块的引用。然后,您需要定义完整的模块路径以从模块内部访问特定的属性或方法。
例如 foo.bar
但不是 bar
从 foo 导入栏:
导入 foo
,并创建对所有列出的成员 (bar
) 的引用。不设置变量 foo
。
例如 bar
但不是 baz
或 foo.baz
从 foo 导入 *:
导入 foo
,并创建对该模块在当前命名空间中定义的所有公共对象的引用(如果 __all__
存在,则在 __all__
中列出的所有对象,否则不以 _
开头的所有对象)。不设置变量 foo
。
例如 bar
和 baz
但不是 _qux
或 foo._qux
。
现在让我们看看我们何时执行 import X.Y
:
>>> import sys
>>> import os.path
使用名称 os
和 os.path
检查 sys.modules
:
>>> sys.modules['os']
<module 'os' from '/System/Library/Frameworks/Python.framework/Versions/2.7/lib/python2.7/os.pyc'>
>>> sys.modules['os.path']
<module 'posixpath' from '/System/Library/Frameworks/Python.framework/Versions/2.7/lib/python2.7/posixpath.pyc'>
使用 os
和 os.path
检查 globals()
和 locals()
命名空间字典:
>>> globals()['os']
<module 'os' from '/System/Library/Frameworks/Python.framework/Versions/2.7/lib/python2.7/os.pyc'>
>>> locals()['os']
<module 'os' from '/System/Library/Frameworks/Python.framework/Versions/2.7/lib/python2.7/os.pyc'>
>>> globals()['os.path']
Traceback (most recent call last):
File "<stdin>", line 1, in <module>
KeyError: 'os.path'
>>>
从上面的例子中我们发现在本地和全局命名空间中只插入了 os
。所以,我们应该可以使用:
>>> os
<module 'os' from
'/System/Library/Frameworks/Python.framework/Versions/2.7/lib/python2.7/os.pyc'>
>>> os.path
<module 'posixpath' from
'/System/Library/Frameworks/Python.framework/Versions/2.7/lib/python2.7/posixpath.pyc'>
>>>
但不是path
。
>>> path
Traceback (most recent call last):
File "<stdin>", line 1, in <module>
NameError: name 'path' is not defined
>>>
从 locals() 命名空间中删除 os
后,您将无法访问 os
和 os.path
,即使它们存在于 sys.modules 中:
>>> del locals()['os']
>>> os
Traceback (most recent call last):
File "<stdin>", line 1, in <module>
NameError: name 'os' is not defined
>>> os.path
Traceback (most recent call last):
File "<stdin>", line 1, in <module>
NameError: name 'os' is not defined
>>>
现在让我们谈谈从以下位置导入:
从:
>>> import sys
>>> from os import path
使用 os 和 os.path 检查 sys.modules:
>>> sys.modules['os']
<module 'os' from '/System/Library/Frameworks/Python.framework/Versions/2.7/lib/python2.7/os.pyc'>
>>> sys.modules['os.path']
<module 'posixpath' from '/System/Library/Frameworks/Python.framework/Versions/2.7/lib/python2.7/posixpath.pyc'>
我们发现在 sys.modules
中我们发现与使用 import name
之前所做的相同
好的,让我们看看它在 locals()
和 globals()
命名空间字典中的样子:
>>> globals()['path']
<module 'posixpath' from '/System/Library/Frameworks/Python.framework/Versions/2.7/lib/python2.7/posixpath.pyc'>
>>> locals()['path']
<module 'posixpath' from '/System/Library/Frameworks/Python.framework/Versions/2.7/lib/python2.7/posixpath.pyc'>
>>> globals()['os']
Traceback (most recent call last):
File "<stdin>", line 1, in <module>
KeyError: 'os'
>>>
您可以使用名称 path
而不是 os.path
访问:
>>> path
<module 'posixpath' from '/System/Library/Frameworks/Python.framework/Versions/2.7/lib/python2.7/posixpath.pyc'>
>>> os.path
Traceback (most recent call last):
File "<stdin>", line 1, in <module>
NameError: name 'os' is not defined
>>>
让我们从 locals()
中删除“路径”:
>>> del locals()['path']
>>> path
Traceback (most recent call last):
File "<stdin>", line 1, in <module>
NameError: name 'path' is not defined
>>>
最后一个使用别名的示例:
>>> from os import path as HELL_BOY
>>> locals()['HELL_BOY']
<module 'posixpath' from '/System/Library/Frameworks/Python.framework/Versions/2.7/lib/python2.7/posixpath.pyc'>
>>> globals()['HELL_BOY']
<module 'posixpath' from /System/Library/Frameworks/Python.framework/Versions/2.7/lib/python2.7/posixpath.pyc'>
>>>
并且没有定义路径:
>>> globals()['path']
Traceback (most recent call last):
File "<stdin>", line 1, in <module>
KeyError: 'path'
>>>
as SYMBOL
会改变这个答案的工作方式吗?
两种方式都得到支持是有原因的:有时一种方式比另一种更合适。
导入模块:当您使用模块中的许多位时很好。缺点是您需要使用模块名称限定每个引用。
from module import ...:很高兴导入的项目无需模块名称前缀即可直接使用。缺点是你必须列出你使用的每一个东西,而且在代码中并不清楚某些东西是从哪里来的。
使用哪个取决于哪个使代码清晰易读,并且与个人喜好有很大关系。我倾向于 import module
通常是因为在代码中非常清楚对象或函数的来源。当我在代码中使用一些对象/函数时,我会使用 from module import ...
。
from M import X
并且仍然以某种方式获得使用限定符的好处?如果您在导入之后M.X
仍能做到这一点,那么您似乎可以两全其美。
class m: from something.too.long import x, y, z
。不过真的不建议这样做。
我个人总是使用
from package.subpackage.subsubpackage import module
然后访问所有内容
module.function
module.modulevar
等等。原因是同时您的调用时间很短,并且您清楚地定义了每个例程的模块名称空间,如果您必须在源代码中搜索给定模块的用法,这将非常有用。
不用说,不要使用 import *,因为它会污染你的命名空间,并且它不会告诉你给定函数来自哪里(来自哪个模块)
当然,如果两个不同包中的两个不同模块的模块名称相同,则可能会遇到麻烦,例如
from package1.subpackage import module
from package2.subpackage import module
在这种情况下,你当然会遇到麻烦,但是强烈暗示你的包布局有缺陷,你必须重新考虑它。
pathlib
时,你总是写成 pathlib.Path
吗?
import module
当您将使用模块中的许多功能时最好。
from module import function
当您只需要 function
时,最好避免使用模块中的所有函数和类型污染全局命名空间。
我刚刚发现了这两种方法之间的另一个细微差别。
如果模块 foo
使用以下导入:
from itertools import count
然后模块 bar
可能会错误地使用 count
,就好像它是在 foo
中定义的,而不是在 itertools
中定义的:
import foo
foo.count()
如果 foo
使用:
import itertools
错误仍然是可能的,但不太可能发生。 bar
需要:
import foo
foo.itertools.count()
这给我带来了一些麻烦。我有一个模块错误地从一个没有定义它的模块导入了一个异常,只从其他模块导入了它(使用 from module import SomeException
)。当不再需要并删除导入时,有问题的模块被破坏了。
这是另一个没有提到的区别。这是从 http://docs.python.org/2/tutorial/modules.html 逐字复制的
请注意,使用时
from package import item
item 可以是包的子模块(或子包),也可以是包中定义的其他名称,如函数、类或变量。 import 语句首先测试该项目是否在包中定义;如果不是,它假定它是一个模块并尝试加载它。如果找不到它,则会引发 ImportError 异常。
相反,当使用类似的语法时
import item.subitem.subsubitem
除最后一项外,每一项都必须是一个包裹;最后一项可以是模块或包,但不能是前一项中定义的类或函数或变量。
由于我也是初学者,所以我将尝试以一种简单的方式来解释这一点:在 Python 中,我们有三种类型的 import
语句,它们是:
1. 通用进口:
import math
这种类型的导入是我个人最喜欢的,这种导入技术的唯一缺点是,如果您需要使用任何模块的功能,您必须使用以下语法:
math.sqrt(4)
当然,它会增加打字工作量,但作为初学者,它将帮助您跟踪与之相关的模块和功能,(一个好的文本编辑器会显着减少打字工作量,推荐)。
使用以下 import 语句可以进一步减少打字工作:
import math as m
现在,您可以使用 m.sqrt()
,而不是使用 math.sqrt()
。
2.函数导入:
from math import sqrt
如果您的代码只需要访问模块中的单个或几个函数,则这种类型的导入最适合,但是对于使用模块中的任何新项目,您必须更新导入语句。
3.通用进口:
from math import *
虽然它显着减少了输入工作,但不推荐使用,因为它会使用模块中的各种函数填充您的代码,并且它们的名称可能与用户定义函数的名称冲突。例子:
如果你有一个名为 sqrt 的函数并导入 math,你的函数是安全的:有你的 sqrt 和 math.sqrt。但是,如果您使用 from math import *,则会出现问题:即,两个不同的函数具有完全相同的名称。资料来源:Codecademy
import package
import module
对于 import
,令牌必须是模块(包含 Python 命令的文件)或包(sys.path
中包含文件 __init__.py
的文件夹。)
当有子包时:
import package1.package2.package
import package1.package2.module
文件夹(包)或文件(模块)的要求相同,但文件夹或文件必须在 package2
内,必须在 package1
内,并且 package1
和 package2
都必须包含 __init__.py
文件. https://docs.python.org/2/tutorial/modules.html
使用 from
样式的导入:
from package1.package2 import package
from package1.package2 import module
包或模块以 module
(或 package
)而不是 package1.package2.module
的形式进入包含 import
语句的文件的命名空间。您始终可以绑定到更方便的名称:
a = big_package_name.subpackage.even_longer_subpackage_name.function
只有 from
样式的导入允许您命名特定的函数或变量:
from package3.module import some_function
是允许的,但是
import package3.module.some_function
不被允许。
补充一下人们对 from x import *
的看法:除了让人们更难分辨名称的来源之外,这还会让 Pylint 等代码检查器失效。他们会将这些名称报告为未定义的变量。
这是我当前目录的目录结构:
. └─a └─b └─c
import 语句会记住所有中间名。这些名称必须经过限定: In[1]: import abc In[2]: a Out[2]:
注意:当然,我在第 1 步和第 2 步之间重新启动了 Python 控制台。
我自己对此的回答主要取决于首先,我将使用多少个不同的模块。如果我只打算使用一两个,我会经常使用 from
... import
,因为它可以减少其余部分的击键次数文件,但如果我要使用许多不同的模块,我更喜欢 import
因为这意味着每个模块引用都是自记录的。我可以看到每个符号的来源,而无需四处寻找。
通常我更喜欢普通导入的自我记录风格,并且只有在我必须输入模块名称的次数超过 10 到 20 时才更改为 from.. import,即使只有一个模块被导入。
我想补充一点。如果遇到循环导入,了解 Python 如何将导入的模块作为属性处理会很有用。
我有以下结构:
mod/
__init__.py
main.py
a.py
b.py
c.py
d.py
从 main.py 我将使用不同的导入方法导入其他模块
主要.py:
import mod.a
import mod.b as b
from mod import c
import d
dis.dis 显示了差异(注意模块名称,abcd):
1 0 LOAD_CONST 0 (-1)
3 LOAD_CONST 1 (None)
6 IMPORT_NAME 0 (mod.a)
9 STORE_NAME 1 (mod)
2 12 LOAD_CONST 0 (-1)
15 LOAD_CONST 1 (None)
18 IMPORT_NAME 2 (b)
21 STORE_NAME 2 (b)
3 24 LOAD_CONST 0 (-1)
27 LOAD_CONST 2 (('c',))
30 IMPORT_NAME 1 (mod)
33 IMPORT_FROM 3 (c)
36 STORE_NAME 3 (c)
39 POP_TOP
4 40 LOAD_CONST 0 (-1)
43 LOAD_CONST 1 (None)
46 IMPORT_NAME 4 (mod.d)
49 LOAD_ATTR 5 (d)
52 STORE_NAME 5 (d)
55 LOAD_CONST 1 (None)
最后它们看起来是一样的(STORE_NAME 是每个示例中的结果),但是如果您需要考虑以下四个循环导入,请注意这一点:
示例1
foo/
__init__.py
a.py
b.py
a.py:
import foo.b
b.py:
import foo.a
>>> import foo.a
>>>
这有效
示例2
bar/
__init__.py
a.py
b.py
a.py:
import bar.b as b
b.py:
import bar.a as a
>>> import bar.a
Traceback (most recent call last):
File "<stdin>", line 1, in <module>
File "bar\a.py", line 1, in <module>
import bar.b as b
File "bar\b.py", line 1, in <module>
import bar.a as a
AttributeError: 'module' object has no attribute 'a'
没有骰子
示例3
baz/
__init__.py
a.py
b.py
a.py:
from baz import b
b.py:
from baz import a
>>> import baz.a
Traceback (most recent call last):
File "<stdin>", line 1, in <module>
File "baz\a.py", line 1, in <module>
from baz import b
File "baz\b.py", line 1, in <module>
from baz import a
ImportError: cannot import name a
类似的问题......但显然 from x import y 与 import import xy as y 不同
例子4
qux/
__init__.py
a.py
b.py
a.py:
import b
b.py:
import a
>>> import qux.a
>>>
这个也有效
我发现令人惊讶地没有人谈论的显着差异之一是,使用普通 import 您可以从导入的模块访问 private variable
和 private functions
, from-import 语句无法做到这一点。
https://i.stack.imgur.com/1fqOd.png
图片中的代码:
设置.py
public_variable = 42
_private_variable = 141
def public_function():
print("I'm a public function! yay!")
def _private_function():
print("Ain't nobody accessing me from another module...usually")
plain_importer.py
import settings
print (settings._private_variable)
print (settings.public_variable)
settings.public_function()
settings._private_function()
# Prints:
# 141
# 42
# I'm a public function! yay!
# Ain't nobody accessing me from another module...usually
from_importer.py
from settings import *
#print (_private_variable) #doesn't work
print (public_variable)
public_function()
#_private_function() #doesn't work
正如 Jan Wrobel 所提到的,不同进口的一个方面是进口的披露方式。
模块之谜
from math import gcd
...
神话的使用:
import mymath
mymath.gcd(30, 42) # will work though maybe not expected
如果我导入 gcd
只是为了内部使用,而不是向 mymath
的用户透露,这可能会很不方便。我经常遇到这种情况,在大多数情况下,我想“保持我的模块干净”。
除了 Jan Wrobel 建议使用 import math
来掩盖这一点之外,我还开始通过使用前导下划线来隐藏导入:
# for instance...
from math import gcd as _gcd
# or...
import math as _math
在较大的项目中,这种“最佳实践”允许我准确控制向后续导入披露的内容和不披露的内容。这使我的模块保持清洁并在一定规模的项目中得到回报。
因为很多人在这里回答,但我只是尽力而为:)
当您不知道必须从模块导入哪个项目时,导入模块是最好的。这样在出现问题时可能很难调试,因为您不知道哪个项目有问题。当您知道需要导入哪个项目并且根据您的需要使用导入特定项目进行更多控制时,form module import
有很多答案,但没有一个提到测试(使用 unittest
或 pytest
)。
tl;博士
将 import foo
用于外部模块以简化测试。
艰难的道路
从模块中单独导入类/函数 (from 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
,我需要更新我的补丁以引用 tools
模块。本质上,由于我的测试对实现了解太多,因此需要进行比预期更多的更改才能进行此重构。
更好的方法
使用 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')
你的测试知道的实现细节越少越好。这样,如果您想出更好的解决方案(使用类而不是函数,使用附加文件来分隔想法等),则需要在测试中进行较少更改以适应重构。
导入模块 - 您不需要额外的努力来从模块中获取另一个东西。它有诸如冗余打字等缺点
模块导入自 - 更少输入和更多控制可以访问模块的哪些项目。要使用模块中的新项目,您必须更新导入语句。
有一些内置模块主要包含裸函数(base64、math、os、shutil、sys、time、...),拥有这些裸函数绝对是一个好习惯函数绑定到某个命名空间,从而提高代码的可读性。考虑一下在没有命名空间的情况下理解这些函数的含义是多么困难:
copysign(foo, bar)
monotonic()
copystat(foo, bar)
比它们绑定到某个模块时:
math.copysign(foo, bar)
time.monotonic()
shutil.copystat(foo, bar)
有时您甚至需要命名空间来避免不同模块之间的冲突(json.load 与 pickle.load)
配置解析器
约会时间
临时文件
压缩文件
configparser.RawConfigParser()
datetime.DateTime()
email.message.EmailMessage()
tempfile.NamedTemporaryFile()
zipfile.ZipFile()
因此,在代码中使用这些带有附加模块命名空间的类是否会添加一些新信息或只是延长代码可能会存在争议。
我正在回答一个类似的问题帖子,但在我发布之前,海报将其删除。这里有一个例子来说明差异。
Python 库可能有一个或多个文件(模块)。例如,
package1
|-- __init__.py
或者
package2
|-- __init__.py
|-- module1.py
|-- module2.py
我们可以在任何基于设计要求的文件中定义 python 函数或类。
让我们定义
mylibrary1 下的 __init__.py 中的 func1() 和 mylibrary2 下的 module2.py 中的 foo()。
我们可以使用其中一种方法访问 func1()
import package1
package1.func1()
或者
import package1 as my
my.func1()
或者
from package1 import func1
func1()
或者
from package1 import *
func1()
我们可以使用以下方法之一访问 foo()
:
import package2.module2
package2.module2.foo()
或者
import package2.module2 as mod2
mod2.foo()
或者
from package2 import module2
module2.foo()
或者
from package2 import module2 as mod2
mod2.foo()
或者
from package2.module2 import *
foo()
简而言之,这完全是为了方便程序员。在核心级别,它们只是导入模块的所有功能。
import module
:当您使用 import module
时,要使用此模块的方法,您必须编写 module.method()
。每次使用任何方法或属性时,都必须引用该模块。
from module import all
:当您使用 from module import all
而不是使用此模块的方法时,您只需编写 method()
而无需引用该模块。
不定期副业成功案例分享
from module import *
可能特别有用,如果将其用作:if(windows):\n\t from module_win import * \n else: \n\t from module_lin import *
。那么你的父模块可能包含独立于操作系统的函数名,如果 module_lin & module_win 具有相同的名称。这就像有条件地继承任一类。