使用‘导入模块’还是‘从模块导入’?
- 2024-11-22 08:47:00
- admin 原创
- 162
问题描述:
我试图找到一个全面的指南,以确定是否最好使用import module
或from module import
。我刚刚开始使用 Python,并试图从最佳实践开始。
基本上,我希望有人可以分享他们的经验,其他开发人员有什么偏好,以及避免将来出现任何问题的最佳方法是什么?
解决方案 1:
import module
和之间的区别from module import foo
主要是主观的。选择你最喜欢的那个,并坚持使用它。以下几点可以帮助你做出决定。
import module
优点:
减少语句维护
import
。无需添加任何额外导入即可开始使用模块中的另一个项目
缺点:
输入
module.foo
代码可能很繁琐和多余(可以使用import module as mo
然后输入来最大限度地减少繁琐mo.foo
)
from module import foo
优点:
减少输入
foo
更好地控制可以访问模块的哪些项目
缺点:
要使用模块中的新项目,您必须更新您的
import
声明你失去了关于 的背景。例如,与 相比 的含义
foo
不太清楚ceil()
`math.ceil()`
两种方法都可以接受,但不要使用from module import *
。
对于任何相当大一组代码,如果您import *
将其固定到模块中,则无法将其移除。这是因为很难确定代码中使用的哪些项目来自“模块”,这很容易让您认为不再使用它们,import
但很难确定。
解决方案 2:
这里还有一个细节没有提到,与写入模块有关。虽然这可能不是很常见,但我有时需要它。
由于 Python 中引用和名称绑定的工作方式,如果您想从模块外部更新模块中的某个符号(例如 foo.bar),并让其他导入代码“看到”该更改,则必须以某种方式导入 foo。例如:
模块 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
仅仅影响了模块内部的名称“bar” a
,它并没有“深入”foo
模块对象并更新其bar
。
解决方案 3:
尽管很多人已经解释了import
vs import from
,但我想尝试更多地解释一下内部发生的事情,以及所有发生变化的地方在哪里。
import foo
:
导入foo
,并在当前命名空间中创建对该模块的引用。然后您需要定义完整的模块路径,以便从模块内部访问特定属性或方法。
例如foo.bar
但不是bar
from foo import bar
:
导入foo
并创建对所列所有成员的引用(bar
)。不设置变量foo
。
例如bar
但不是baz
或foo.baz
from foo import *
:
导入foo
,并创建对当前命名空间中该模块定义的所有公共对象的引用(__all__
如果__all__
存在,则引用 中列出的所有内容,否则引用不以 开头的所有内容_
)。不设置变量foo
。
例如bar
和baz
但不是_qux
或foo._qux
。
现在让我们看看什么时候做import X.Y
:
>>> import sys
>>> import os.path
检查sys.modules
姓名os
和os.path
:
>>> 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'>
globals()
使用和检查命名空间locals()
字典os
:os.path
>>> 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 from
:
from
:
>>> import sys
>>> from os import path
sys.modules
使用os
和进行检查os.path
:
>>> 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'
>>>
解决方案 4:
支持这两种方式的原因在于:有时候一种方式比另一种方式更合适。
import module
:当您使用模块中的许多位时,这很好。缺点是您需要用模块名称限定每个引用。from module import ...
:导入的项目可以直接使用,无需模块名称前缀,这一点很好。缺点是您必须列出您使用的每个东西,而且代码中不清楚某些东西来自哪里。
使用哪种方法取决于哪种方法可以让代码更清晰、更易读,这在很大程度上取决于个人喜好。我倾向于使用import module
“一般”方法,因为在代码中,对象或函数的来源非常清晰。当我在代码中大量from module import ...
使用某个对象/函数时,我会使用它。
解决方案 5:
我个人总是使用
from package.subpackage.subsubpackage import module
然后访问所有内容
module.function
module.modulevar
等等。原因是,同时您有简短的调用,并且您清楚地定义了每个例程的模块命名空间,如果您必须在源中搜索给定模块的使用情况,那么这非常有用。
不用说,不要使用 import *,因为它会污染你的命名空间,并且它不会告诉你给定函数来自哪里(来自哪个模块)
当然,如果两个不同的包中的两个不同的模块有相同的模块名,那么你可能会遇到麻烦,例如
from package1.subpackage import module
from package2.subpackage import module
在这种情况下,你当然会遇到麻烦,但随后就有强烈的暗示表明你的包装布局存在缺陷,你必须重新考虑。
解决方案 6:
import module
当您需要使用模块中的多项功能时,这是最好的选择。
from module import function
当您只需要时,最好避免使用模块中的所有函数和类型来污染全局命名空间function
。
解决方案 7:
我刚刚发现这两种方法之间还有一个细微的差别。
如果模块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
)导入了它。当不再需要导入并将其删除时,有问题的模块就坏了。
解决方案 8:
这里还有另一个未提及的区别。这是从http://docs.python.org/2/tutorial/modules.html逐字复制的。
使用时请注意
from package import item
该项目可以是包的子模块(或子包),也可以是包中定义的其他名称,如函数、类或变量。 import 语句首先测试该项目是否在包中定义;如果没有,则假定它是一个模块并尝试加载它。如果找不到它,则会引发 ImportError 异常。
相反,当使用如下语法时
import item.subitem.subsubitem
除最后一项之外的每一项都必须是一个包;最后一项可以是模块或包,但不能是前一项中定义的类或函数或变量。
解决方案 9:
由于我也是一名初学者,我将尝试用一种简单的方式来解释这一点:在 Python 中,我们有三种类型的import
语句:
1. 仿制药进口:
import math
这种导入类型是我个人最喜欢的,这种导入技术的唯一缺点是,如果您需要使用任何模块的功能,则必须使用以下语法:
math.sqrt(4)
当然,它增加了打字的工作量,但作为初学者,它将帮助您跟踪与其相关的模块和功能(一个好的文本编辑器将大大减少打字的工作量,值得推荐)。
使用此导入语句可以进一步减少打字工作量:
import math as m
math.sqrt()
现在,您可以使用 ,而不是 使用m.sqrt()
。
2.函数导入:
from math import sqrt
如果您的代码只需要访问模块中的单个或几个函数,则这种导入类型最适合,但要使用模块中的任何新项目,您必须更新导入语句。
3.通用进口:
from math import *
虽然它大大减少了打字工作量,但不建议这样做,因为它会用模块中的各种函数填充您的代码,并且它们的名称可能会与用户定义函数的名称冲突。
例如:
如果您有一个名为 sqrt 的函数,并且您导入了 math,那么您的函数是安全的:有您的 sqrt 和 math.sqrt。但是,如果您从 math import * 导入,就会出现问题:即两个不同的函数具有完全相同的名称。来源:Codecademy
解决方案 10:
我想补充一点。如果你遇到循环导入,了解 Python 如何将导入的模块作为属性处理会很有用。
我有以下结构:
mod/
__init__.py
main.py
a.py
b.py
c.py
d.py
我将使用不同的导入方法从 main.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 "bara.py", line 1, in <module>
import bar.b as b
File "bar.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 "baza.py", line 1, in <module>
from baz import b
File "baz.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
>>>
这个也有效
解决方案 11:
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
import 的风格:
from package1.package2 import package
from package1.package2 import module
import
包或模块以module
(或package
)而不是 的形式进入包含语句的文件的命名空间package1.package2.module
。你始终可以绑定到更方便的名称:
a = big_package_name.subpackage.even_longer_subpackage_name.function
只有from
导入样式允许您命名特定的函数或变量:
from package3.module import some_function
是允许的,但是
import package3.module.some_function
是不允许的。
解决方案 12:
这是我当前目录的目录结构:
. └─a └─b └─c
该
import
语句会记住所有中间名称。
这些名称必须符合以下条件:
In[1]: import a.b.c
In[2]: a
Out[2]: <module 'a' (namespace)>
In[3]: a.b
Out[3]: <module 'a.b' (namespace)>
In[4]: a.b.c
Out[4]: <module 'a.b.c' (namespace)>
该
from ... import ...
语句仅记住导入的名称。
此名称不得限定:
In[1]: from a.b import c
In[2]: a
NameError: name 'a' is not defined
In[2]: a.b
NameError: name 'a' is not defined
In[3]: a.b.c
NameError: name 'a' is not defined
In[4]: c
Out[4]: <module 'a.b.c' (namespace)>
注意:当然,我在步骤 1 和步骤 2 之间重新启动了我的 Python 控制台。
解决方案 13:
除了人们所说的内容外from x import *
,这还会使人们更难判断名称的来源,还会使 Pylint 等代码检查器失效。它们会将这些名称报告为未定义的变量。
解决方案 14:
我自己对此的回答主要取决于首先我将使用多少个不同的模块。如果我只使用一个或两个,我通常会使用from
... import
,因为它可以减少文件其余部分的击键次数,但如果我要使用许多不同的模块,我更喜欢仅仅import
因为这意味着每个模块引用都是自文档化的。我可以看到每个符号来自哪里,而不必到处寻找。
通常我更喜欢简单导入的自文档样式,并且只有当我必须输入模块名称的次数增加到 10 到 20 次以上时才更改为 from.. import,即使只有一个模块被导入。
解决方案 15:
有很多答案,但没有一个提到测试(使用unittest
或pytest
)。
总结
用于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')
测试知道的实现细节越少越好。这样,如果你想出一个更好的解决方案(使用类而不是函数,使用额外的文件来分离想法等),那么在测试中需要做的更改就更少,以适应重构。
解决方案 16:
我发现的一个显著区别是,令人惊讶的是没有人谈论过,那就是使用普通的导入就可以访问导入的模块,private variable
而使用from-importprivate functions
语句则无法做到这一点。
图片中的代码:
设置.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")
纯导入器.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
来自导入器.py
from settings import *
#print (_private_variable) #doesn't work
print (public_variable)
public_function()
#_private_function() #doesn't work
解决方案 17:
正如Jan Wrobel所提到的,不同进口的一个方面在于进口的披露方式。
mymath模块
from math import gcd
...
mymath的用途:
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
在大型项目中,这种“最佳实践”允许我精确控制哪些信息可以向后续导入公开,哪些信息不公开。这可以保持模块整洁,并在项目达到一定规模时获得回报。
解决方案 18:
因为很多人在这里回答,但我只是尽力而为:)
import module
当你不知道要从哪个项目导入时,这是最好的选择module
。这样,当出现问题时,调试可能会很困难,因为你不知道哪个项目有问题。form module import <foo>
当您知道需要导入哪个项目时,这种方法是最好的,而且也有助于更好地控制根据需要导入特定项目。使用这种方法调试可能很容易,因为您知道导入了哪个项目。
解决方案 19:
导入模块 - 您不需要额外的努力来从模块中获取另一个东西。它有冗余输入等缺点
模块导入自 - 更少的输入和更多的控制可以访问模块的哪些项目。要使用模块中的新项目,您必须更新导入语句。
解决方案 20:
有一些内置模块主要包含裸函数(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、datetime、tempfile、zipfile等),其中许多模块的类名已经足够不言自明了:
configparser.RawConfigParser()
datetime.DateTime()
email.message.EmailMessage()
tempfile.NamedTemporaryFile()
zipfile.ZipFile()
因此,可能会存在争议,在代码中使用这些类和附加模块命名空间是否会添加一些新信息或只是延长代码。
解决方案 21:
我之前回答过一个类似的问题,但发帖者在我发帖之前就删除了它。下面是一个例子来说明差异。
Python 库可能有一个或多个文件(模块)。例如,
package1
|-- __init__.py
或者
package2
|-- __init__.py
|-- module1.py
|-- module2.py
我们可以根据设计要求在任何文件中定义 python 函数或类。
让我们定义
func1()
在__init__.py
以下mylibrary1
,并且foo()
在module2.py
之下mylibrary2
。
我们可以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()
解决方案 22:
简单来说,这都是为了程序员的方便。在核心层面,他们只需导入模块的所有功能即可。
import module
:当您使用import module
then 来使用此模块的方法时,您必须编写module.method()
。每次使用任何方法或属性时,您都必须引用该模块。
from module import all
:当您使用from module import all
这个模块的方法时,您只需编写method()
而无需引用该模块。
解决方案 23:
@ahfx 已经提到了这些导入的一个关键方面,即模块加载过程的内部结构。如果您的系统需要使用循环导入(例如,您想在某些流行的 http 框架中使用依赖项注入),就会出现这种情况。在这种情况下,from {module} import {function}
对加载过程如何进行的要求似乎更加激进。让我们举个例子:
#m1.py:
print('--start-m1--')
from m2 import * # form does not matter; just need to force import of m2
print('--mid-m1--')
def do1(x):
print(x)
print('--end-m1--')
输入
#m2.py
print('--start-m2--')
# from m1 import * # A
# from m1 import do1 # B
# import m1 # C
# D -- no import of "do1" at all
print('--mid-m2--')
def do2(x):
m1.do1(x)
print('--end-m2--')
通过运行
#main.py:
from m1 import do1
do1('ok')
m2.py
在(A,B,C,D)中的所有导入可能性中,只有这from {module} import {function}
实际上会导致加载过程崩溃,从而导致臭名昭著的 (CPython 3.10.6)
ImportError: cannot import name 'do1' from partially initialized module 'm1'
(most likely due to a circular import)
虽然我无法说出为什么会发生这种情况,但看起来该from ... import ...
声明对相关模块在初始化过程中“进展如何”提出了更严格的要求。