使用“导入模块”还是“从模块导入”?


410

我试图找到一个综合指南,以决定是否最好使用import modulefrom module import?我刚开始使用Python,并且正在尝试着眼于最佳实践。

基本上,我希望有人能分享他们的经验,其他开发人员有什么喜好,以及避免遇到麻烦的最佳方法是什么?


5
我只是想让您知道,所选答案是错误的。它指出,差异是主观的,而有差异。这可能导致难以发现错误。参见Michael Ray Lovetts的答案。
Mayou36


2
在导入特定的命名标识符'from module import X,Y,Z与之间有一个区别'from module import *。后者会污染您的名称空间,并且可能会根据模块中发生的事情给出不可预测的结果。更糟糕的是from module import *使用多个模块。
smci

Answers:


474

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但是很难确定。


66
+1表示不鼓励使用“ from module import *”,它只会使名称空间混乱。
Christian Witts

22
命名空间混乱不是 “ import *”最具问题的部分,而是可读性的降低:任何名称冲突都将在(单元)测试中显示出来。但是您从导入模块中使用的所有名称都将是裸露的,没有任何暗示它们来自的名称。我绝对讨厌“进口*”。
尔根·艾哈德

21
Python Zen难道说显式比隐式好吗?
安东尼·科赫,

8
from module import *如果用于以下用途,则可能会特别有用if(windows):\n\t from module_win import * \n else: \n\t from module_lin import *。然后,如果module_lin和module_win中的函数名称具有相同的名称,则您的父模块可能包含独立于OS的函数名称。就像有条件地继承任何一个类。
2013年

19
@anishsane。还有另一种方法。将module_win导入。然后始终使用something.method_name()
Vinay 2015年

163

这里还有另一个细节,未提及,与写入模块有关。当然,这可能不是很常见,但是我不时需要它。

由于引用和名称绑定在Python中的工作方式,如果您想从该模块外部更新模块中的某些符号(例如foo.bar),并且要更改其他导入代码“ see”,则必须导入foo a某种方式。例如:

模块foo:

bar = "apples"

模块a:

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”。


在最后一个示例中,您仍然可以调用'foo.bar =“ orange”'来更新'foo'中的'bar'吗?
velocirabbit 2015年

4
不,在最后一个示例中,名称“ foo”是未知的
Ghislain Leveque

31
该答案提供了以下问题的“真实”答案:两种进口型号之间有什么区别
Mayou36 '17

3
写了一些代码片段来证明这个答案是绝对正确的,但是这个背后的原理是什么呢?
huangbeidu

我认为您在说的是导入符号名称具有局部变量,但导入模块名称具有全局变量???
WinEunuuchs2Unix

79

尽管已经有很多人对importvs进行了解释import from,但我还是想尝试多解释一些关于幕后发生的事情以及它发生的所有变化的位置。


import foo

导入foo,并在当前名称空间中创建对该模块的引用。然后,您需要定义完整的模块路径,以从模块内部访问特定的属性或方法。

例如foo.bar但不是bar

from foo import bar

导入foo,并创建对列出的所有成员(bar)的引用。不设置变量foo

例如bar但不是bazfoo.baz

from foo import *

导入foo并创建对该模块在当前名称空间中定义的所有公共对象的引用(__all__如果__all__存在,则列出的所有对象,否则所有不以开头的对象_)。不设置变量foo

例如barbaz但不是_quxfoo._qux


现在让我们看看我们何时进行操作import X.Y

>>> import sys
>>> import os.path

检查sys.modules名称osos.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()命名空间字典:osos.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
>>>

os从locals()命名空间删除后,将无法访问它们osos.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.modulesos和检查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

OK,让我们检查一下它的外观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'
>>>

8
尽管这很冗长,但对于一个相当复杂的问题,这确实是列表中的最佳答案。它提供了实际的代码来帮助解释在此特定问题上“内幕”的精妙之处,而这些精妙之处比样式更重要。我希望我可以不止一次投票!
Mike Williamson

使用是否as SYMBOL根本改变了这个答案的工作方式?
Maximilian Burszley

40

两种方法都受支持是有原因的:有时候,一种方法比另一种更合适。

  • import module:当您使用模块中的许多位时很好。缺点是您需要使用模块名称来限定每个引用。

  • from module import ...:很高兴导入的项目无需模块名称前缀即可直接使用。缺点是您必须列出您使用的每件事,并且在代码中不清楚来源。

使用哪种方法取决于使代码清晰易读的方式,并且与个人喜好有关。我倾向于import module一般,因为在代码中很清楚对象或函数的来源。我在代码中大量from module import ...使用某些对象/函数时使用。


1
有没有一种方法可以使用from M import X并且仍然以某种方式获得使用限定符的好处?如果您M.X在导入后仍然可以做到,那么似乎可以同时兼顾两全其美。
节肢动物

@artgropod:Kinda。你可以的class m: from something.too.long import x, y, z。虽然不会真的推荐。
Lie Ryan

35

我个人总是使用

from package.subpackage.subsubpackage import module

然后以

module.function
module.modulevar

原因是同时有简短的调用,并且您清楚地定义了每个例程的模块名称空间,如果您必须在源代码中搜索给定模块的用法,这很有用。

不用说,不要使用import *,因为它会污染您的名称空间,并且不会告诉您给定函数的来源(来自哪个模块)

当然,如果两个不同软件包中的两个不同模块具有相同的模块名称,则可能会遇到麻烦。

from package1.subpackage import module
from package2.subpackage import module

在这种情况下,您当然会遇到麻烦,但是有一个强烈的暗示,即您的程序包布局存在缺陷,您必须重新考虑它。


10
在最后一种情况下,您始终可以使用:将pkgN.sub.module导入为modN,为每个模块提供不同的名称。您也可以使用“将模块名作为mod1导入”模式来缩短长名称,或在一次更改名称的情况下在相同API的实现之间切换(例如DB API模块)。
杰夫·香农(

15
import module

当您将使用模块中的许多功能时,最好。

from module import function

当您只想避免使用模块中的所有函数和类型污染全局名称空间时,这是最好的选择function


7
当然,如果您执行“导入模块”,则全局命名空间中唯一的东西就是“模块”?如果您执行'from .. import *',则只会污染名称空间。
John Fouhy

10

我刚刚发现了这两种方法之间的另一个细微差别。

如果模块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)导入了异常。当不再需要导入并将其删除时,损坏的模块将被破坏。


10

这是另一个未提及的区别。这是从http://docs.python.org/2/tutorial/modules.html逐字复制的

请注意,使用时

from package import item

该项目可以是包的子模块(或子包),也可以是包中定义的其他名称,例如函数,类或变量。import语句首先测试项目是否在包装中定义;如果不是,则假定它是一个模块并尝试加载它。如果找不到它,则会引发ImportError异常。

相反,当使用类似

import item.subitem.subsubitem

除最后一个项目外,每个项目都必须是一个包装;最后一项可以是模块或包,但不能是上一项中定义的类或函数或变量。


我注意到的另一件事是,如果item也是包中的子模块,则“从包导入项”起作用,但“导入包” package.item.subitem = ...不适用于包的空init .py,除非我们软件包的init文件中包含“导入项目” 。
Amitoz Dandiana '18

6

由于我还是初学者,因此我将尝试以一种简单的方式来解释这一点:在Python中,我们有三种类型的import语句,它们是:

1.通用进口:

import math

这种类型的导入是我个人的最爱,这种导入技术的唯一缺点是,如果需要使用任何模块的功能,则必须使用以下语法:

math.sqrt(4)

当然,它会增加打字的工作量,但是作为一个初学者,它将帮助您跟踪与之相关的模块和功能(一个好的文本编辑器将大大减少打字的工作量,建议使用)。

使用以下import语句可以进一步减少打字工作:

import math as m

现在,math.sqrt()可以使用代替使用m.sqrt()

2.函数导入:

from math import sqrt

如果您的代码只需要访问模块中的单个或几个函数,而要使用模块中的任何新项,则必须更新import语句,则这种类型的导入最适合。

3.普遍进口:

from math import * 

尽管它可以显着减少键入工作,但是不建议这样做,因为它将用模块中的各种函数填充代码,并且它们的名称可能与用户定义函数的名称冲突。 例:

如果您有自己的名为sqrt的函数并且导入了数学运算,则该函数是安全的:存在您的sqrt和Math.sqrt。但是,如果从数学导入*进行操作,则会遇到问题:即,两个具有相同名称的不同函数。资料来源:Codecademy


5
import package
import module

使用import,令牌必须是模块(包含Python命令的文件)或包(sys.path包含文件的文件夹__init__.py)。

有子包时:

import package1.package2.package
import package1.package2.module

为文件夹(封装)或文件(模块)的要求是相同的,但该文件夹或文件必须是内部package2必须是内部package1,并且两个package1package2必须包含__init__.py文件。https://docs.python.org/2/tutorial/modules.html

具有from导入样式:

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 

不被允许。


4

补充说一下from x import *:除了使人更难分辨名称的来源之外,还抛出了像Pylint这样的代码检查器。他们会将这些名称报告为未定义的变量。


3

我自己的答案主要取决于首先要使用的模块数量。如果我只使用一两个,那么我将经常使用from...,import因为它使文件其余部分的击键次数减少,但是如果我要使用许多不同的模块,我更喜欢import因为这意味着每个模块引用都是自记录的。我可以看到每个符号的来源,而不必四处寻找。

通常,我更喜欢纯文本导入的自我记录样式,仅当要输入的模块名称次数超过10到20时才更改为from .. import,即使只有一个模块被导入。


1

其中一个显著差异,我发现它的出奇,没有人一直在谈论的是使用纯进口,您可以访问private variableprivate functions从导入模块,这是不可能的从导入语句。

在此处输入图片说明

图片中的代码:

setting.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

0

导入模块-您无需付出额外的努力即可从模块中获取其他东西。它具有诸如冗余键入之类的缺点

从模块导入-减少键入操作,更多地控制可以访问模块的项目。要使用模块中的新项目,必须更新导入语句。


0

有一些内置模块主要包含裸函数(base64mathosshutilsystime等),将这些裸函数绑定到某些名称空间绝对是一个好习惯,从而提高了您的可读性码。考虑没有这些名称空间的情况下,理解这些功能的含义会更加困难:

copysign(foo, bar)
monotonic()
copystat(foo, bar)

而不是将它们绑定到某个模块时:

math.copysign(foo, bar)
time.monotonic()
shutil.copystat(foo, bar)

有时甚至需要命名空间来避免不同模块之间的冲突(json.loadpickle.load


另一方面,有些模块主要包含类(configparserdatetimetempfilezipfile,...),其中许多模块使类名变得不言而喻:

configparser.RawConfigParser()
datetime.DateTime()
email.message.EmailMessage()
tempfile.NamedTemporaryFile()
zipfile.ZipFile()

因此,在将这些类与代码中的其他模块名称空间一起使用时是否存在争议,是否会增加一些新信息还是仅仅是延长代码长度,就存在争议。


0

我想补充一点,在导入调用期间需要考虑一些事项:

我有以下结构:

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显示了不同之处:

  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

类似的问题...但是从x import y明显不同于与import import xy和y相同

例子4

qux/
   __init__.py
   a.py
   b.py
a.py:
import b 
b.py:
import a
>>> import qux.a
>>>

这个也可以


0

这是我当前目录的目录结构:

.  
└─a  
   └─b  
     └─c
  1. 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)>
  2. 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控制台。

0

正如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

在较大的项目中,这种“最佳实践”使我能够精确控制后续进口中公开的内容和未公开内容。这样可以使我的模块保持清洁,并以一定规模的项目回报。

By using our site, you acknowledge that you have read and understand our Cookie Policy and Privacy Policy.
Licensed under cc by-sa 3.0 with attribution required.