【问题标题】:Why import when you need to use the full name?为什么要在需要使用全名时导入?
【发布时间】:2018-04-06 11:08:45
【问题描述】:

在 python 中,如果您需要来自不同包的模块,则必须导入它。来自 Java 背景,这是有道理的。

import foo.bar

但没有意义的是,为什么我想使用 bar 时需要使用全名?如果我想使用全名,为什么需要导入?使用全名不会立即描述我正在处理的模块吗?

from foo import bar 似乎有点多余,而 import foo.bar 应该这样做。也有点模糊,为什么我要使用全名时必须导入。

【问题讨论】:

  • “需要导入”是什么意思?您是否希望 foo.bar 无需先导入就可以工作?那你怎么知道 foo 是一个模块而不是一个普通的变量
  • @mb14,在 Java 中,您可以直接使用另一个包中的类,而无需通过在代码中显式使用完全限定的类名和包名来导入。例如,您可以在源代码中将 Java 的 List 接口称为“java.util.List”,或者先导入它,然后将其简称为“List”。 Python 这样做的方式,对于来自 Java 的人来说,感觉就像您正在导入 java.util.List,但您仍然需要在代码中写出“java.util.List”而不是“List”。跨度>

标签: python


【解决方案1】:

问题是,尽管 Python 的 import 语句被设计为看起来类似于 Java 的,但它们在底层做的事情却完全不同。如您所知,在 Java 中,import 语句实际上只是对编译器的提示。它基本上为完全限定的类名设置了一个别名。例如,当你写

import java.util.Set;

它告诉编译器在整个文件中,当你写Set 时,你的意思是java.util.Set。如果你写s.add(o),其中sSet 类型的对象,编译器(或者更确切地说,链接器)会出去并在Set.class 中找到add 方法并引用它。

但在 Python 中,

import util.set

(顺便说一下,这是一个组成的模块)做一些完全不同的事情。请看,在 Python 中,包和模块不仅仅是 名称,它们是实际的 对象,当您在代码中编写 util.set 时,它会指示 Python 访问名为util 的对象并在其上查找名为set 的属性。 Python 的import 语句的工作是创建该对象和属性。它的工作方式是解释器查找名为util/__init__.py 的文件,使用其中的代码定义对象的属性,并将该对象绑定到名称util。类似地,util/set.py 中的代码用于初始化绑定到util.set 的对象。有一个名为__import__ 的函数负责处理所有这些,实际上import util.set 语句基本上等同于

util = __import__('util.set')

重点是,当你导入一个 Python 模块时,你得到的是一个对应于顶级包的对象,util。为了访问util.set,您需要经历这些,这就是为什么您似乎需要在 Python 中使用完全限定名称。

当然,有办法解决这个问题。由于所有这些东西都是对象,一种简单的方法是将util.set 绑定到一个更简单的名称,即在import 语句之后,您可以拥有

set = util.set

从那时起,您可以使用set,否则您将使用util.set。 (当然这会掩盖内置的set 类,所以我不建议实际使用名称set。)或者,正如至少在另一个答案中提到的那样,您可以编写

from util import set

import util.set as set

这仍然导入包util,其中包含模块set,但不是在当前范围内创建变量util,而是创建一个引用util.set的变量set。在幕后,这有点像

_util = __import__('util', fromlist='set')
set = _util.set
del _util

在前一种情况下,或

_util = __import__('util.set')
set = _util.set
del _util

在后者中(尽管两种方式基本上做同样的事情)。这种形式在语义上更像 Java 的 import 语句所做的:它为通常只能通过完全限定名称 (util.set) 访问的东西定义别名 (set)。

【讨论】:

    【解决方案2】:

    如果您愿意,可以缩短它:

    import foo.bar as whateveriwant
    

    使用全名可以防止两个具有同名子模块的包相互破坏。

    【讨论】:

    • +1,这是 OP 正在寻找的正确答案。也许编辑说“酒吧”而不是“whateveriwant”?这可能更清楚地表明它提供了广受欢迎的功能。
    • 它接近于正确答案,但没有解释为什么 Python 表现出这种行为。
    • 而且它也比from foo import bar 稍微冗长一些。 "import foo.bar as bar" == 21 个字符,"from foo import bar" == 19 个字符。
    • @JAB 但您可以执行“import foo.bar as mybar”来匹配您已有的内部命名
    • @Martin Beckett:仅当bar 是一个模块时。当 b 是方法或变量时,import a.b as c 语法不起作用。事实上,无论如何您都不能使用点表示法导入方法或变量。您必须使用from ... import ... 语法,或者通过包含的模块名称引用方法或变量。
    【解决方案3】:

    标准库中有一个模块叫io

    In [84]: import io
    
    In [85]: io
    Out[85]: <module 'io' from '/usr/lib/python2.6/io.pyc'>
    

    scipy 中还有一个模块叫io

    In [95]: import scipy.io
    
    In [96]: scipy.io
    Out[96]: <module 'scipy.io' from '/usr/lib/python2.6/dist-packages/scipy/io/__init__.pyc'>
    

    如果您想在同一个脚本中使用这两个模块,那么命名空间是区分两者的便捷方式。

    In [97]: import this
    The Zen of Python, by Tim Peters
    ...
    Namespaces are one honking great idea -- let's do more of those!
    

    【讨论】:

      【解决方案4】:

      在 Python 中,导入不仅仅表明您可能会使用某些东西。导入实际上在模块级别执行代码。您可以将导入视为“解释”和创建函数的时刻。任何在 _____init_____.py 级别或不在函数或类定义中的代码都会发生。

      导入还制作了整个模块名称空间的廉价副本,并将其放在文件/模块/任何导入位置的名称空间中。然后,IDE 会列出您可能开始键入以完成命令的函数列表。

      【讨论】:

      • 请注意,当一个模块或包被多次导入时,模块或包的__init__.py文件中的任何可执行代码只会在第一次导入时执行。这很好。
      【解决方案5】:

      Python 哲学的一部分是explicit is better than implicit。当您第一次尝试从包中访问某些内容时,Python 可以自动导入,但这并不明确。

      我还猜测,如果导入是自动的,那么包初始化会更加困难,因为它不会在代码中始终如一地完成。

      【讨论】:

        【解决方案6】:

        您对 Python 导入的工作方式有些困惑。 (我刚开始的时候也是。)在 Python 中,你不能简单地用全名来引用模块中的某些东西,这与 Java 不同。无论您打算如何引用导入的项目,您都必须先导入模块。尝试在解释器中输入math.sqrt(5),而不先导入mathmath.sqrt,看看会发生什么。

        无论如何...import foo.bar 要求您使用foo.bar 而不仅仅是bar 的原因是为了防止意外的命名空间冲突。比如,如果你先import foo.bar,然后import baz.bar呢?

        当然,您可以选择使用import foo.bar as bar(即别名),但如果您这样做,您也可以使用from foo import bar。 (编辑:除非你想导入方法和变量。那么你必须使用from ... import ... 语法。这包括你想要导入一个方法或变量而不使用别名的实例,即你不能简单地做import foo.bar if bar 是一个方法或变量。)

        【讨论】:

        • import foo.bar as bar 似乎比 from foo import bar 更受欢迎,因为我找不到更多的技术原因。
        • @Philipp:如果是这样,那对我来说就是新闻。
        • @Philipp: docs.python.org/py3k/howto/… 没有提及任何偏好。事实上,你不能在所有情况下都使用前者,因为当你尝试导入方法或数据成员时它会失败。
        • 有一些关于以特殊方式修改成员的事情。当使用from 并在导入的模块中进行某些修改时,AFAIR 不会将此更改转移到导入该模块的其他模块。但可能我在编造这个并且混淆了一些东西,我一直无法找到来源。
        • @Philipp:你没有错。查看我在之前的评论中链接到的页面。但是你不应该首先使用from 来导入模块,因为它的主要目的是用于方法和变量/常量,不能使用通常的import 语法直接导入。
        【解决方案7】:

        除了在 Java 中,在 Python 中 import foo.bar 声明您将使用 foo.bar 所指的东西。

        这符合 Python 的显式优于隐式的哲学。有更多的编程语言使模块间的依赖关系比 Java 更明确,例如 Ada。

        使用全名可以消除来自不同模块的同名定义的歧义。

        【讨论】:

          【解决方案8】:

          您不必使用全名。尝试其中一种

          from foo import bar
          
          import foo.bar as bar
          
          import foo.bar
          bar = foo.bar
          
          from foo import *
          

          显式导入很好的几个原因:

          • 它们有助于向人类和工具表明您的模块所依赖的包。
          • 它们避免了在运行时动态确定必须加载(并可能编译)哪些包的开销。
          • 它们(连同 sys.path)明确区分来自不同命名空间的名称冲突的符号。
          • 它们让程序员可以控制进入他正在工作的命名空间的内容。

          【讨论】:

            猜你喜欢
            • 1970-01-01
            • 1970-01-01
            • 1970-01-01
            • 2010-11-04
            • 1970-01-01
            • 2015-10-08
            • 2012-12-03
            • 1970-01-01
            • 2016-12-23
            相关资源
            最近更新 更多