使用Python导入模块-最佳做法


72

我是Python的新手,因为我想扩展使用R学习的技能。在RI中,往往会加载一堆库,有时会导致函数名冲突。

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

import pandasfrom pandas import *from pandas import DataFrame

前两者之间有什么区别,我应该只导入我需要的东西吗?同样,对于制作小型程序来处理数据和计算简单统计信息的人来说,最糟糕的后果是什么。

更新

我找到了这个出色的指南。它解释了一切。


1
import pandas而且from pandas import DataFrame都还可以。from pandas import *通常不建议使用第二种形式,因为它只会将所有内容提取到全局名称空间中。
Niklas B.

Answers:


49

import pandas 在pandas名称空间下导入pandas模块,因此您需要使用以下命令在pandas中调用对象 pandas.foo

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

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

在我看来,第一个通常是最佳实践,因为它可以在代码中很好地划分不同的模块。


44

每种形式的劣势

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

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。通过上面的标准1的定义,该缩写将被频繁使用,因此足够容易记住。

按照“显式优于隐式”的说法,只有这两种样式是完全Python的 规则。

from module import xx有时仍会在我的代码中发生。我使用它的时候甚至是as格式都被夸大了,最著名的例子是from datetime import datetime (但是如果我需要更多元素,我会import datetime as dt)。


2
卢兹(Lotz),这是一件可爱的作品。+1代表“永恒的原则”动机-在我们不断发展的知识不断侵蚀和移动的沙尘时代,这是真实且非常重要的。
user3666197 '16

我同意这是一个适当的妥协。我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

24

通常,最好进行显式导入。如:

import pandas
frame = pandas.DataFrame()

要么:

from pandas import DataFrame
frame = DataFrame()

当名称冲突时,Python中的另一个选项是将x导入为y:

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

18

以下是《PEP8样式指南》中的一些建议

  1. 导入通常应放在单独的行上,例如:

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

    但是可以

    from subprocess import Popen, PIPE
    
  2. 导入总是放在文件的顶部,紧随任何模块注释和文档字符串之后,以及模块全局变量和常量之前。

    • 导入应按以下顺序分组:
      1. 标准库导入
      2. 相关第三方进口
      3. 本地应用程序/特定于库的导入
    • 您应该在每组导入之间放置一个空白行。
  3. 推荐绝对导入
    。如果您弄乱了导入系统,它们会提供更好的错误消息,从而更具可读性,并使调试更容易。

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

    明确的相对进口

    from . import sibling
    from .sibling import example
    
  4. 绝对不要使用隐式相对导入,并且在Python 3中将其删除。

    No:  from ..grand_parent_package import uncle_package
    
  5. from <module> import *应避免使用通配符导入(),因为通配符不能弄清楚名称空间中存在哪些名称,这会混淆阅读器和许多自动化工具。


关于一些建议lazy imports蟒速度性能的技巧。

导入报表费用

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

下面给出的是页面上说明的场景,

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

8
from A import B

本质上等于以下三个陈述

import A
B = A.B
del A

就是这样,仅此而已。


4
这并不完全正确。from A import B也将导入A到全局名称空间中B。您仍然可以做A.C(或即使A.B您愿意),但是您也可以做B from itertools import imap >>> itertools <module 'itertools' (built-in)> >>> imap itertools.imap >>> itertools.starmap itertools.starmap
Dannnno 2014年

1
因此,没有类似的东西del A
Dannnno 2014年

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:在面向数学的代码中,我很少想要sin。如果我只需要几个符号,则可以将每个符号导入为逗号列表,但通常不会打扰我确定需要哪些符号。*很方便,如果有点懒。
Marcelo Cantos 2013年
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.