【问题标题】:Importing modules in Python - best practice在 Python 中导入模块 - 最佳实践
【发布时间】:2012-03-28 22:58:43
【问题描述】:

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

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

import pandas、from pandas import * 和 from pandas import DataFrame

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

更新

我发现了这个excellent guide。它解释了一切。

【问题讨论】:

  • import pandas 和 from pandas import DataFrame 都可以。第二种形式 from pandas import * 通常不推荐使用,因为它只会将所有内容都拉入全局命名空间。

标签: python coding-style workflow python-import


【解决方案1】:

每种形式的缺点

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

import modulewithaverylongname 会使代码更加混乱 使用长模块名称(例如 concurrent.futures 或 django.contrib.auth.backends)并降低这些地方的可读性。

from module import * 让我没有机会从语法上看到, 例如,classA 和 classB 来自同一个模块并且 彼此有很多关系。 它使阅读代码困难。 (从这样的进口名称 可能从较早的导入中隐藏名称是该问题的最小部分。)

from module import classA, classB, functionC, constantD, functionE 名字太多让我的短期记忆超负荷 我在精神上需要分配给module,以便 连贯地理解代码。

import modulewithaverylongname as mwvln 有时不够 我的助记符。

适当的妥协

基于以上观察,我开发了以下 我自己的代码中的样式:

import module 是模块名称较短时的首选样式 例如标准库中的大多数包。 如果我需要使用模块中的名称,这也是首选样式 我自己的模块只有两三个地方; 清晰胜过简洁 ("Readability counts")。

import longername as ln 几乎是每个人的首选风格 其他情况。 例如,我可能会import django.contrib.auth.backends as djcab。 根据上述标准 1 的定义,将使用缩写 经常,因此很容易记住。

根据 "Explicit is better than implicit." 规则。

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

【讨论】:

  • 可爱的作品,卢茨。 +1 简介 “永恒的原则”动机 - 在我们的系统所依据的知识逐渐侵蚀和移动沙子的时代,这是真实且非常重要的。
  • 我同意这是一个合适的折衷方案。我只在导入另一个文件中定义的我自己的类时才使用from module import xx。
  • 不知道给定对象来自哪个包真是太无聊了。如果我们想要构建“可共享”和可读的代码,那么所谓的合适的折衷显然是要走的路。顺便说一句,它还可以防止名称冲突。 +1
  • 和你一样,我更喜欢导入模块,但是当模块位于一个包中时,我有时会使用from <package> import <module>,尤其是当模块有多个子包时。我宁愿输入file_generators.Random() 而不是libraries.file_utils.generators.file_generators.Random(),所以我只会使用from libraries.file_utils.generators import file_generators。
【解决方案2】:

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

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

from pandas import DataFrame 与上述相同,但仅将 DataFrame(而不是所有内容)导入到您当前的命名空间中。

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

【讨论】:

    【解决方案3】:

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

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

    【讨论】:

      【解决方案4】:

      以下是来自PEP8 Style Guide.的一些建议

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

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

        不过没关系

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

        • 应按以下顺序对导入进行分组:
          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 *),因为它们会使命名空间中存在哪些名称变得不清楚,从而混淆阅读器和许多自动化工具。


      来自python speed performance tips.的关于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
      

      【讨论】:

        【解决方案5】:
        from A import B
        

        基本上等于下面三个语句

        import A
        B = A.B
        del A
        

        就是这样,就是这样。

        【讨论】:

        • 这并不完全正确。 from A import B 会将A 导入全局命名空间,B 也会导入。你仍然可以使用A.C(如果你愿意,甚至可以使用A.B),但你也可以使用Bfrom itertools import imap >>> itertools <module 'itertools' (built-in)> >>> imap itertools.imap >>> itertools.starmap itertools.starmap
        • 因此没有像del A这样的东西
        【解决方案6】:

        它们都适用于不同的环境(这就是它们都可用的原因)。除了围绕清晰度、可维护性和简单性的通用母性声明之外,没有深刻的指导原则。我自己的代码中的一些示例:

        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。如果我只想要几个符号,我可以将每个符号作为逗号列表导入,但我通常不会费心弄清楚我需要哪些符号。 * 很方便,如果有点懒的话。
        猜你喜欢
        • 1970-01-01
        • 1970-01-01
        • 1970-01-01
        • 2015-02-15
        • 1970-01-01
        • 1970-01-01
        • 1970-01-01
        • 2019-09-01
        • 2018-05-10
        相关资源
        最近更新 更多