“导入模块”与“从模块导入功能”


143

我一直在使用这种方法:

from sys import argv

argv仅使用argv。但是使用此约定:

import sys

并通过使用argv sys.argv

第二种方法使代码自成体系,并且我(确实)坚持执行。但是我之所以偏爱第一种方法,是因为它速度很快,因为我们仅导入所需的功能,而不是导入整个模块(该模块包含更多无用的功能,而python将浪费这些时间来导入它们)。请注意,我只需要argv,而sys中的所有其他功能对我来说都是无用的。

所以我的问题是。第一种方法真的可以使脚本快速运行吗?哪种方法最合适?为什么?



Answers:


175

导入模块不会浪费任何东西 ; 该模块始终完全导入(导入到sys.modules映射中),因此无论您使用import sys还是from sys import argv不成问题。

这两个语句之间的唯一区别是绑定的名称。import sys将名称绑定sys到模块(so- sys> sys.modules['sys']),同时from sys import argv绑定不同的名称,argv直接指向模块内部包含的属性(so- argv> sys.modules['sys'].argv)。sys无论您是否使用模块中的任何其他模块,模块的其余部分仍然存在。

两种方法之间也没有性能差异。是的,sys.argv必须查找两件事;它必须sys在全局名称空间中查找(找到模块),然后查找attribute argv。是的,通过使用,from sys import argv您可以跳过属性查找,因为您已经直接引用了该属性。但是该import语句仍然必须执行该工作,在导入时它会查找相同的属性,并且您只需要使用argv 一次即可。如果您必须argv在循环中使用数千次,则可能会有所不同,但是在这种特定情况下,实际上没有。

那么,在一个或另一个之间的选择应该基于编码样式

大型模块中,我肯定会使用import sys; 代码文档很重要,sys.argv在大型模块中的某处使用将使您所指的内容比argv以往更加清晰。

如果您使用的唯一位置argv是在'__main__'块中调用main()函数,from sys import argv则一定要使用,如果您对此感到更高兴:

if __name__ == '__main__':
    from sys import argv
    main(argv)

我仍然会在import sys那里自己使用。所有事物都是平等的(就性能用于编写它的字符数而言,它们都是准确的),对我来说,这更容易些。

如果您要完全导入其他内容,那么性能可能会发挥作用。但是仅当您在某个模块中多次使用特定名称时(例如在关键循环中)。但是,然后(在函数内)创建本地名称仍然会更快:

 import somemodule

 def somefunction():
      localname = somemodule.somefunctionorother
      while test:
          # huge, critical loop
          foo = localname(bar)

1
还有一种情况,您有一个带有子包或模块的包,该包暴露了顶层包中这些子包/模块之一的属性。使用from...import允许您执行package.attribute而不是package.subpackage_or_module.attribute,如果您在软件包中包含逻辑或概念分组,但又想使软件包的用户使用起来更方便,这将很有用。(numpy做这样的事情,我相信。)
JAB

在django中,您可以找到很多类似的from django.core.management.base import BaseCommand地方,而其他方面(尤其是import django)都会导致无法读取的代码变得更好。因此,尽管我喜欢这个答案,但我认为在某些库(尤其是某些框架)中,约定违反了裸导入。一如既往,根据您的判断在给定情况下的最佳选择。但是在显式方面犯了错误(换句话说,我大部分时间都同意)。
Neuronet

1
@JAB:您仍然可以使用import ... as其他名称查找软件包:import package.subpackage_or_module as shortnamefrom parent import sub本质上是一样的。
马丁·彼得斯

43

有两个原因赞成使用import module而不是from module import function

首先是名称空间。将函数导入全局名称空间会有名称冲突的风险。

其次,与标准模块无关,但是对您自己的模块很重要,尤其是在开发过程中。这reload()是模块的选项。考虑一下:

from module import func
...
reload(module)
# func still points to the old code

另一方面

import module
...
reload(module)
# module.func points to the new code

至于速度...

我们仅导入所需的功能,而不是导入整个模块(该模块包含更多无用的功能,而python会浪费时间导入它们)

无论您是导入模块还是从模块导入函数,Python都会解析整个模块。两种方式都可以导入模块。“导入功能”无非就是将功能绑定到名称。实际上import module,翻译工作比翻译少from module import func


6
reload()是Python 2中的内置函数;Python 3不再是这种情况。
André17年

我认为循环导入依赖项也有影响吗?
ADP

18

from import每当提高可读性时,我都会使用s。例如,我更喜欢(分号只是为了节省空间):

from collections import defaultdict
from foomodule import FooBar, FooBaz
from twisted.internet.protocol import Factory
defaultdict(); FooBar(); FooBaz(); Factory()

代替:

import collections
import foomodule
import twisted.internet.protocol
collections.defaultdict(); foomodule.FooBar(); foomodule.FooBaz()
twisted.internet.protocol.Factory()

后者对我来说很难读(写),因为它包含了很多冗余信息。另外,提前知道我正在使用模块的哪些部分也很有用。

import如果我从模块中使用许多短名称,则我更喜欢常规s:

import sys
sys.argv; sys.stderr; sys.exit()

或者,如果名称是如此通用,以致在名称空间之外没有任何意义:

import json
json.loads(foo)

from json import loads
loads(foo)  # potentially confusing

这是我最喜欢的答案。“显式胜于隐式”有时会与可读性,简单性和DRY相冲突。特别是在使用像Django这样的框架时。
Neuronet

18

我认为使用常规import可以提高可读性。查看Python代码时,我喜欢看到给定的函数或类的使用来源。它使我免于滚动到模块顶部来获取该信息。

至于长模块名称,我只使用as关键字并给它们提供短别名:

import collections as col
import foomodule as foo
import twisted.internet.protocol as twip

my_dict = col.defaultdict()
foo.FooBar()
twip_fac = twip.Factory()

作为例外,from module import something在处理__future__模块时,我总是使用表示法。当您希望所有字符串在Python 2中默认为unicode时,您就无法采取其他方式,例如

from __future__ import unicode_literals
from __future__ import print_function

阿们!“ import as”是一个成功的组合:-)
paj28'3

4

虽然import sysfrom sys import agrv这两个导入整个sys模块,后者将通过名称绑定,只有argv模块的代码的其余部分进行访问。

对于某些人来说,这将是首选样式,因为它仅使您明确声明的功能可访问。

但是,它确实引入了潜在的名称冲突。如果您有另一个名为的模块argv怎么办?请注意,您还可以显式导入该函数并使用进行重命名from sys import argv as sys_argv,该约定满足显式导入,并且不太可能产生名称空间冲突。


2
那么有if sys_argv:什么比这更好的if sys.argv:呢?我知道第二种说法的意思,不知道第一种形式的意思是没有回溯到离奇的含义。
msw

1

我最近对自己问了这个问题。我为不同的方法计时。

请求库

def r():
    import requests
    return 'hello'
timeit r() # output: 1000000 loops, best of 3: 1.55 µs per loop

def rg():
    from requests import get
    return 'hello'
timeit rg() # output: 100000 loops, best of 3: 2.53 µs per loop

美丽汤图书馆

def bs():
    import bs4
    return 'hello' 
timeit bs() # output: 1000000 loops, best of 3: 1.53 µs per loop

def be():
    from bs4 import BeautifulSoup
    return 'hello'
timeit be() # output: 100000 loops, best of 3: 2.59 µs per loop

json库

def js():
    import json
    return 'hello'
timeit js() # output: 1000000 loops, best of 3: 1.53 µs per loop

def jl():
    from json import loads
    return 'hello'
timeit jl() # output: 100000 loops, best of 3: 2.56 µs per loop

sys库

def s():
    import sys
    return 'hello'
timeit s() # output: 1000000 loops, best of 3: 1.55 µs per loop

def ar():
    from sys import argv
    return 'hello'
timeit ar() # output: 100000 loops, best of 3: 2.87 µs per loop

这在我看来,在性能上略有差别。


您正在添加属性查找。为了比较import modulefrom module import name正确,加上该名称查找到的import module情况。例如,将行添加sys.argvar测试中,等等。仍然存在差异,因为完成的工作略有不同,因为生成了不同的字节码并且执行了不同的代码路径。
马丁·彼得斯

2
请注意,我在回答中直接解决了这一差异;会有使用之间的差异import sys,然后使用sys.argv数以千计的时间在一个循环中主场迎战from sys import argv然后只使用argv。但是你没有。对于只在模块全局级别执行一次的操作,您确实应该针对可读性进行优化,而不是对时序的微观差异进行优化。
马丁·彼得斯

1
啊!我以为我会做某事!:)我只浏览了您的答案。好像我跳上那把枪。谦卑感到很好。
tmthyjames 2015年

-1

查看发布的代码片段,导入整个模块并进行引用module.function几乎是标准的,至少对于标准模块而言。一个例外似乎是datetime

from datetime import datetime, timedelta

所以你可以说datetime.now()而不是datetime.datetime.now()

如果您担心表现,可以随时说(例如)

argv = sys.argv

然后执行关键性能代码,因为模块查找已完成。但是,尽管这可以与函数/方法一起使用,但是大多数IDE都会感到困惑,并且(例如)在将函数分配给变量时不会显示该函数的源链接/签名。


-2

我只想补充一点,如果你做类似的事情

from math import sin

(或任何其他内置库,如sysposix),然后sin将包含在您模块的文档中(例如,当您使用>>> help(mymodule)或时$ pydoc3 mymodule。为避免这种情况,请使用以下命令导入:

import math
from math import sin as _sin

PS:内置库是从C代码编译并包含在Python中的一个库。argparseos并且io不是内置包

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.