在Python中导入模块 - 最佳实践

101

我是新手,想通过学习Python来扩展我在R中学到的技能。 在R中,我倾向于加载一堆库,有时会导致函数名称冲突。

在Python中的最佳实践是什么。我看到了一些具体的变化,但我没有看出它们之间的区别。

import pandasfrom pandas import *from pandas import DataFrame之间有什么区别,我应该只导入需要的内容吗? 此外,对于那些处理数据和计算简单统计量的小程序,最糟糕的后果将是什么。

更新

我找到了这个优秀的指南,里面解释了所有问题。


2
import pandasfrom pandas import DataFrame 都可以。但是第二种形式 from pandas import * 通常不推荐使用,因为它会将所有内容都拉入全局命名空间。 - Niklas B.
可能是 Python中正确导入模块的方法 的重复问题。 - Wooble
6个回答

83

各种导入方式的缺点

在阅读其他人的代码时(这些人使用非常不同的导入风格),我注意到每种风格都存在以下问题:

import modulewithaverylongname会在后面的代码中加上很长的模块名称(例如concurrent.futuresdjango.contrib.auth.backends),降低了那些地方的可读性。

from module import *没有让我有机会从语法上看出,例如classAclassB来自同一个模块,并且彼此之间有很多关联。这使得阅读代码变得困难。(这种导入可能会遮盖先前导入的名称只是该问题的最小部分。)

from module import classA, classB, functionC, constantD, functionE会用太多的名称超载我的短期记忆,我需要在脑海中将它们赋值给module,以便能够有条理地理解代码。

import modulewithaverylongname as mwvln有时对我来说不太容易记忆。

一个适当的折中方案

基于以上观察,我在自己的代码中开发了以下风格:

如果模块名很短,例如大多数标准库中的包,则import module是首选样式。 如果我只需要在自己的模块中使用模块中的名称两到三次,则也是首选样式; 在这种情况下,清晰度胜过简洁(“可读性至关重要”)。

在几乎所有其他情况下,import longername as ln是首选样式。 例如,我可能会import django.contrib.auth.backends as djcab。 根据上述标准的定义,缩写将经常使用,因此足够容易记忆。

根据"显式优于隐式"规则,只有这两种风格是完全符合Python的。

from module import xx在我的代码中仍然有时出现。 我在需要的情况下使用它,即使as格式看起来有些夸张, 最著名的例子是from datetime import datetime (但如果我需要更多元素,我会使用import datetime as dt)。


3
可爱的作品,Lutz。给个赞👍,“永恒原则”动机-它是真实的,在知识逐渐侵蚀和我们系统基础不断变化的时代非常重要。 - user3666197
我同意这是一个合适的妥协。只有在导入另一个文件中定义的我的类时,我才使用 from module import xx - Nicolas Garnier
1
不知道一个对象来自哪个包,真是太无聊了。如果我们想要构建“可共享”和可读的代码,那么所谓的“合适妥协”显然是正确的选择。顺便说一句,它还可以防止名称冲突。+1 - keepAlive
和你一样,我更喜欢导入模块,但当模块位于包内时,我有时会使用 from <package> import <module>,特别是当模块在多个子包中时。我宁愿输入 file_generators.Random() 而不是 libraries.file_utils.generators.file_generators.Random(),所以我只需使用 from libraries.file_utils.generators import file_generators - Troy Hoffman

56

import pandas 导入pandas模块并将其放在pandas命名空间下,因此您需要使用 pandas.foo 来调用pandas中的对象。

from pandas import * 将pandas模块中的所有对象导入到当前命名空间中,因此您只需使用 foo 来调用pandas中的对象。请注意,如果当前命名空间和pandas命名空间之间存在任何名称冲突,则可能会产生意外后果。

from pandas import DataFrame 与上述相同,但仅将 DataFrame 导入到当前命名空间中(而不是全部)。

在我看来,第一种方法通常是最佳实践,因为它可以在代码中将不同的模块保持得很好。


30

以下是PEP8风格指南的一些建议。

  1. Imports should usually be on separate lines, e.g.:

    Yes: import os
         import sys
    
    No:  import sys, os
    

    but it is okay to

    from subprocess import Popen, PIPE
    
  2. Imports are always put at the top of the file, just after any module comments and docstrings, and before module globals and constants.

    • Imports should be grouped in the following order:
      1. standard library imports
      2. related third party imports
      3. local application/library specific imports
    • You should put a blank line between each group of imports.
  3. Absolute imports are recommended
    They are more readable and make debugging easier by giving better error messages in case you mess up import system.

    import mypkg.sibling
    from mypkg import sibling
    from mypkg.sibling import example
    

    or explicit relative imports

    from . import sibling
    from .sibling import example
    
  4. Implicit relative imports should never be used and is removed in Python 3.

    No:  from ..grand_parent_package import uncle_package
    
  5. Wildcard imports ( from <module> import * ) should be avoided, as they make it unclear which names are present in the namespace, confusing both readers and many automated tools.


以下是关于Python中的“惰性导入”建议,来自Python速度性能技巧

导入语句开销

导入语句可以在任何地方执行。将它们放在函数内部以限制其可见性和/或减少初始启动时间通常很有用。虽然Python解释器经过优化,不会多次导入同一模块,但在某些情况下,重复执行导入语句可能会严重影响性能。

以下是页面上解释的一个场景:
>>> def doit1():
... import string
... string.lower('Python')
...
>>> import string
>>> def doit2():
... string.lower('Python')
...
>>> import timeit
>>> t = timeit.Timer(setup='from __main__ import doit1', stmt='doit1()')
>>> t.timeit()
11.479144930839539
>>> t = timeit.Timer(setup='from __main__ import doit2', stmt='doit2()')
>>> t.timeit()
4.6661689281463623

隐式和显式相对输入有什么区别? - thanos.a

26

一般来说,最好进行显式导入。

例如:

import pandas
frame = pandas.DataFrame()

或者:

from pandas import DataFrame
frame = DataFrame()

在Python中,当你有冲突的名称时,另一种选择是使用import x as y:

from pandas import DataFrame as PDataFrame
from bears import DataFrame as BDataFrame
frame1 = PDataFrame()
frame2 = BDataFrame()

8
from A import B

本质上等同于以下三个语句:

import A
B = A.B
del A

那就是它,所有的内容都在这里了。

4
这并不完全正确。from A import B 会将 AB 都导入到全局命名空间中。你仍然可以使用 A.C(甚至是 A.B 如果你想的话),但现在你也可以直接使用 B。另外一个例子是,当你输入 from itertools import imap 后,itertools 被导入到了全局命名空间中,而 imap 则成为了 itertools 模块下的一个属性,同样地,itertools.starmap 也是如此。 - Dan Oberlam
1
因此,没有类似于del A的东西。 - Dan Oberlam
@DanOberlam 可能已经过时了。在Python 3.8中,当我尝试访问你的示例中的itertools时,会出现NameError。 - Ian Goldby
@DanOberlam 这可能已经过时了。在Python 3.8中,当我尝试访问你的示例中的itertools时,会出现NameError。 - Ian Goldby

2
他们在不同的情境下都很适用(这也是它们都可用的原因)。没有深层的指导原则,只有关于清晰性、可维护性和简单性的通用母亲陈述。以下是我自己代码中的一些示例:
  1. import sys, os, re, itertools 避免名称冲突并提供了一种非常简洁的方式来导入一堆标准模块。
  2. from math import * 让我在数学密集型代码中编写 sin(x) 而不是 math.sin(x)。当我还导入numpy时,这会变得有点棘手,因为它们有些重复,但这并不过分担心我,因为它们通常是相同的函数。此外,我倾向于遵循numpy文档 - import numpy as np - 这样可以完全避开这个问题。
  3. 我喜欢使用 from PIL import Image, ImageDraw,只是因为PIL文档展示了这种方式的示例。

如果你想使用 sin(x) 而不是 math.sin(x),为什么不使用 from math import sin 呢? - Chiggs
@Chiggs:在面向数学的代码中,我很少只需要 sin。如果我只需要一小部分符号,我可以将每个符号作为逗号列表导入,但我通常不想费心去弄清楚我需要哪些符号。* 很方便,虽然有点懒。 - Marcelo Cantos
1
这些示例都很容易编写,但不一定易于阅读,并且违反最佳实践。 - rjh

网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接