我应该使用`import os.path`还是`import os`?

162
根据官方文档os.path是一个模块。因此,导入它的首选方式是什么?
# Should I always import it explicitly?
import os.path

或者...
# Is importing os enough?
import os

请不要回答"importing os works for me"。我知道,它现在可以工作(截至Python 2.6)。我想知道的是关于这个问题是否有任何官方建议。因此,如果您回答这个问题,请附上您的参考资料。
6个回答

188
  • os.path的工作方式有点奇怪。看起来os应该是具有子模块path的包,但实际上os是一个普通的模块,通过使用sys.modules魔法注入了os.path。以下是发生的情况:

    • 当Python启动时,它会将一堆模块加载到sys.modules中。它们没有绑定到脚本中的任何名称,但在以某种方式导入它们时,您可以访问已创建的模块。

      • sys.modules是一个字典,其中缓存了模块。当您导入模块时,如果它已经在某个地方导入,它将获取存储在sys.modules中的实例。
    • os是Python启动时加载的模块之一。它将其path属性分配给特定于操作系统的路径模块。

    • 它注入sys.modules['os.path'] = path,以便您可以像子模块一样进行"import os.path"。

    我倾向于将os.path视为我要使用的模块,而不是os模块中的东西,因此即使它实际上不是名为os的包的子模块,我也会像一个一样导入并始终执行import os.path 。这与如何记录os.path一致。


  • 顺便说一句,我认为这种结构导致了许多Python程序员对模块、包和代码组织的早期困惑。原因有两个:

    1. 如果你把os看作一个包,并且知道你可以执行import os命令并访问子模块os.path,那么当你不能执行import twisted并自动访问twisted.spread时,可能会感到惊讶。

    2. os.name是一个普通的字符串,而os.path是一个模块,这很令人困惑。我总是用空的__init__.py文件来构建我的包,这样在同一级别上我总是有一种类型的东西:一个模块/包或其他东西。许多大型Python项目采取这种方法,这 tends to make more structured code.


    非常好的、富有信息量的回答!恭喜!尽管它并没有直接回答问题,但是它包含了许多有用的细节。但是,您能否详细说明一下“这与os.path的文档记录一致”是什么意思呢?就像Chris Hulan所说的那样,os.walk()的示例只导入了os而不是os.path。 - Denilson Sá Maia
    4
    @Denilson,它确实包含了一个直接的回答:我总是自己做import os.path并且认为那是更好的方式。当我说“这与os.path文档中所述的一致”时,我的意思是它在http://docs.python.org/library/os.path.html 中有它自己的页面。 - Mike Graham
    3
    哇,os.py确实注入到了sys.modules['os.path']中。这就是为什么from os.path import something实际上有效的原因。我很好奇这是什么时候引入的,然后查看了源代码。有趣的事实是:这来自1999年,最初包含在Python 1.5.2中。原始提交在此处 - Bluehorn
    太好了!当 os 真正是一个模块时,我对“import os.path”语法的包导入感到非常困惑。 - Ruperrrt

    31
    根据 Tim Peters 的 PEP-20,“显示优于隐式”,“可读性很重要”。如果你只需要使用 os.path 下的内容,则更显式并能让他人知道你真正关心的是什么,应使用import os.path
    同样地,PEP-20 还说“简单胜于复杂”,所以如果你需要使用更通用的 os 模块下的内容,则应选择import os

    2
    我不认为import os在任何有意义的方式上都与“简单”有关。 简单≠短。 - Mike Graham
    16
    如果你需要使用os.getcwd()os.path.isfile()函数,那么同时导入import osimport os.path是愚蠢的。我的初衷是指出这一点。 - Nick T
    虽然两个都导入是不必要的,但我认为“愚蠢”有点过了。 - chepner

    16

    明确的答案: import os 并使用 os.path。不要直接 import os.path

    来自模块文档本身的说明:

    >>> import os
    >>> help(os.path)
    ...
    Instead of importing this module directly, import os and refer to
    this module as os.path.  The "os.path" name is an alias for this
    module on Posix systems; on other systems (e.g. Mac, Windows),
    os.path provides the same operations in a manner specific to that
    platform, and is an alias to another module (e.g. macpath, ntpath).
    ...
    

    16
    请注意,这不是针对不存在的 os.path 模块的文档,而是针对 posixpath 的文档。 - wRAR
    23
    我认为这并不是文档字符串的本意,尽管它相当具有误导性。请记住句子“"Instead of importing this module directly, import os and refer to this module as os.path."” 在 posixpath.py(或 macpath.pyntpath.py 等)中。我相当确定他们的意思是不应该直接 import posixpath(虽然可以),而应通过 os 导入模块以实现更好的可移植性。我认为他们并不打算推荐使用 import os 还是 import os.path - flornquake
    1
    我同意 @flornquake 的大部分评论,但不同意最后一句话。posixpath.py 和 ntpath.py 都说“import os并将此模块称为 os.path”。他们没有说“import os.path并将此模块称为 os.path”。macpath.py 对此事没有任何说明。 - Pete Forman
    7
    这是一个很好的例子,展示了包含this指针的文档如何具有误导性 :D - Cyker
    1
    我想不出将 os.path 注入到 sys.modules 中的任何理由,除了允许使用 import os.path 导入。如果你不应该使用直接导入,为什么要在第一次添加 os.pathsys.modules 中呢? - chepner

    10

    有趣的是,导入os.path将会导入所有的os模块。在交互式提示符中尝试以下操作:

    import os.path
    dir(os)
    

    结果将与仅导入os相同。这是因为os.path将基于您使用的操作系统引用不同的模块,因此python将导入os以确定要加载哪个模块以供路径使用。

    参考

    对于某些模块,说import foo将不会公开foo.bar,因此我猜这实际上取决于特定模块的设计。


    通常情况下,只导入你需要的显式模块会稍微快一点。在我的电脑上: import os.path: 7.54285810068e-06import os: 9.21904878972e-06
    这些时间足够接近,可以认为是相当可忽略的。 你的程序可能需要现在或将来使用 os 的其他模块,因此通常最好牺牲两微秒并使用 import os 以避免以后出现错误。 我通常倾向于只导入整个 os,但我可以理解为什么有些人更喜欢 import os.path,因为它从技术上讲更有效,并且能向代码的读者传达只需要使用 os 模块的这一部分。 对我来说,这实际上归结为一个风格问题。

    3
    如果速度是问题,使用from os import path将使对路径的调用更快。 - Justin Peel
    作为Pythonic的编程风格,显式优于隐式,对吧? 实际上,我认为这取决于用户的判断,无论用户是只使用os.path还是在os中使用多个模块。也许一种方法比另一种更符合您的编程哲学? - Andrew Kou
    27
    计时这个东西是我见过的最为过早优化之一。这从未成为任何人的瓶颈,时间对于编码的方式并不重要。 - Mike Graham
    1
    这不仅适用于 os.path正如文档所述,任何非顶级模块导入都会将当前命名空间中顶级包的名称绑定为其别名。导入模块还需要(隐式)导入包含它的包。 - chepner

    6
    常识告诉我们:os是一个模块,os.path也是一个模块。因此,只需导入你想要使用的模块即可:
    • If you want to use functionalities in the os module, then import os.

    • If you want to use functionalities in the os.path module, then import os.path.

    • If you want to use functionalities in both modules, then import both modules:

      import os
      import os.path
      

    参考资料:


    1
    严格来说,os.path是一个特定于操作系统的模块的别名,该模块具有自己的名称。如果您已经导入了import os,那么import os.path就是多余的,因为os也有一个属性path,它指向相同的模块。(这并不是说它没有文档上的好处,只是不必要的。) - chepner
    1
    @chepner,您提供的描述非常有用。但是官方文档仅在谈到模块时提到了os.path,并未提及别名。技术细节是将名称注入sys.modules中。 - Cyker
    1
    没有一个名为 os.path 的单独模块。它总是别名,要么是 posixpath,要么是 ntpath - chepner
    同样的文档也只链接到这两个模块的文件。至少在CPython中,并没有提供一个名为“os.path”的单独或不同的模块。 - chepner

    5

    我没有找到任何权威参考资料,但是我看到os.walk的示例代码使用了os.path,但仅导入了os。


    不是(或不再是)真的;有一个例子使用os.path作为os模块属性的一个示例,但也有一个使用from os.path import join, getsize的示例。当然,这与from ... import ...语句不允许像from os import path.join as join, path.getsize as getsize这样的语句有很大关系。 - chepner

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