我试图找到一个关于它是否最好使用import module
或from module import
. 我刚刚开始使用 Python,并且正在尝试从最佳实践入手。
基本上,我希望是否有人可以分享他们的经验,其他开发人员有什么偏好以及避免任何问题的最佳方法是什么?
我试图找到一个关于它是否最好使用import module
或from module import
. 我刚刚开始使用 Python,并且正在尝试从最佳实践入手。
基本上,我希望是否有人可以分享他们的经验,其他开发人员有什么偏好以及避免任何问题的最佳方法是什么?
import module
和之间的区别from module import foo
主要是主观的。选择您最喜欢的一个,并在使用时保持一致。这里有一些要点可以帮助您做出决定。
import module
import
报表的维护。不需要添加任何额外的导入来开始使用模块中的另一个项目module.foo
代码可能很乏味和冗余(可以通过使用import module as mo
then typing来最小化乏味mo.foo
)from module import foo
foo
import
声明foo
. 例如,ceil()
与math.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
仅影响了模块内部的名称“bar” a
,它没有“进入”foo
模块对象并更新其bar
.
尽管很多人已经解释了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
andbaz
但不是_qux
or 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'>
使用and来检查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'
>>>
您可以使用 namepath
而不是 by访问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'
>>>
两种方式都得到支持是有原因的:有时一种方式比另一种更合适。
import module
:当您使用模块中的许多位时很好。缺点是您需要使用模块名称限定每个引用。
from module import ...
:很好,导入的项目无需模块名称前缀即可直接使用。缺点是你必须列出你使用的每一个东西,而且在代码中并不清楚某些东西是从哪里来的。
使用哪个取决于哪个使代码清晰易读,并且与个人喜好有很大关系。我倾向于import module
一般,因为在代码中非常清楚对象或函数的来源。当我在代码中大量from module import ...
使用某些对象/函数时,我会使用它。
我个人总是使用
from package.subpackage.subsubpackage import module
然后访问所有内容
module.function
module.modulevar
等等。原因是同时您的调用时间很短,并且您清楚地定义了每个例程的模块名称空间,如果您必须在源代码中搜索给定模块的用法,这将非常有用。
不用说,不要使用 import *,因为它会污染你的命名空间,并且它不会告诉你给定函数来自哪里(来自哪个模块)
当然,如果两个不同包中的两个不同模块的模块名称相同,则可能会遇到麻烦,例如
from package1.subpackage import module
from package2.subpackage import module
在这种情况下,你当然会遇到麻烦,但是强烈暗示你的包布局有缺陷,你必须重新考虑它。
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
现在,math.sqrt()
您可以使用m.sqrt()
.
2.函数导入:
from math import sqrt
如果您的代码只需要访问模块中的单个或几个函数,则这种类型的导入最适合,但是对于使用模块中的任何新项目,您必须更新导入语句。
3.通用进口:
from math import *
虽然它显着减少了输入工作,但不推荐使用,因为它会使用模块中的各种函数填充您的代码,并且它们的名称可能与用户定义函数的名称冲突。 例子:
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
包或模块进入包含import
语句的文件的命名空间 as module
(or 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
不允许。
补充一下人们所说的from x import *
:除了让人们更难以分辨名字的来源之外,这还会引发像 Pylint 这样的代码检查器。他们会将这些名称报告为未定义的变量。
这是我当前目录的目录结构:
. └─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)>
我自己对此的回答主要取决于首先,我将使用多少个不同的模块。如果我只打算使用一两个,我会经常使用from
...import
因为它可以减少文件其余部分的击键次数,但如果我要使用许多不同的模块,我更喜欢import
因为这意味着每个模块引用都是自记录的。我可以看到每个符号的来源,而无需四处寻找。
通常我更喜欢普通导入的自我记录风格,并且只有在我必须输入模块名称的次数超过 10 到 20 时才更改为 from.. import,即使只有一个模块被导入。
我发现的显着差异之一令人惊讶的是没有人谈论过,使用普通导入可以访问private variable
和从导入的模块,而使用from-import语句private functions
是不可能的。
图片中的代码:
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")
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 settings import *
#print (_private_variable) #doesn't work
print (public_variable)
public_function()
#_private_function() #doesn't work
我想补充一点。如果遇到循环导入,了解 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 是每个示例中的结果),但是如果您需要考虑以下四个循环导入,请注意这一点:
foo/
__init__.py
a.py
b.py
a.py:
import foo.b
b.py:
import foo.a
>>> import foo.a
>>>
这有效
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'
没有骰子
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 不同
qux/
__init__.py
a.py
b.py
a.py:
import b
b.py:
import a
>>> import qux.a
>>>
这个也有效
正如Jan Wrobel所提到的,不同进口的一个方面是进口的披露方式。
模块之谜
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
在较大的项目中,这种“最佳实践”允许我准确控制向后续导入披露的内容和不披露的内容。这使我的模块保持清洁并在一定规模的项目中得到回报。
因为很多人在这里回答,但我只是尽力而为:)
import module
当您不知道必须从哪个项目导入时最好module
。这样在出现问题时可能很难调试,因为您不知道哪个项目有问题。
form module import <foo>
当您知道需要导入哪个项目并且根据您的需要使用导入特定项目来帮助进行更多控制时,这是最好的选择。使用这种方式调试可能很容易,因为您知道您导入了哪个项目。
导入模块 - 您不需要额外的努力来从模块中获取另一个东西。它有诸如冗余打字等缺点
模块导入自 - 更少输入和更多控制可以访问模块的哪些项目。要使用模块中的新项目,您必须更新导入语句。
有一些内置模块主要包含裸函数(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 函数或类。
让我们定义
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()
有很多答案,但没有一个提到测试(使用unittest
or 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')
你的测试知道的实现细节越少越好。这样,如果你想出一个更好的解决方案(使用类而不是函数,使用额外的文件来分离想法等),你的测试中需要更改的内容更少以适应重构。