Python - 什么时候需要使用'import'?

5

mod1.py

import mod2

class Universe:
    def __init__(self):
        pass
    def answer(self):
        return 42

u = Universe()
mod2.show_answer(u)

mod2.py

#import mod1 -- not necessary
def show_answer(thing):
    print thing.answer()

从C++背景出发,我认为在show_answer函数起作用之前需要导入包含Universe类定义的模块。也就是说,在使用之前必须声明所有内容。
我想知道这样做是否必要?这是鸭子类型吗?因此,如果不需要导入即可查看类的方法,则至少需要导入模块的类定义和顶层函数?
在我编写的一个脚本中,我甚至写了一个基类来声明具有一组方法的接口,然后派生具体类以继承该接口,但我现在明白了——在Python中,这种做法是错误的,对象是否具有特定方法是在运行时检查的,而不是在调用点处。
我意识到Python比C++更加动态,花了我一段时间才看出实际上你只需要写很少的代码!
我想我知道这个问题的答案,但我只是想得到澄清并确保我走在正确的轨道上。
更新:感谢所有的回答,我想我现在应该澄清我的问题:
mod2.show_answer()需要导入(任何描述)才能知道thing具有名为answer()的方法,还是在运行时动态确定?

回答你的第二个问题:不,它是动态的。它会查找 thing.dict 中的 "answer" 条目。在 http://docs.python.org/reference/datamodel.html#the-standard-type-hierarchy 上搜索 "Class instances"。 - Nicolas Dumazet
7个回答

7
在这种情况下,你是正确的:show_answer()被赋予了一个对象,其中它调用"answer"方法。只要传递给show_answer()的对象拥有这样的方法,它来自哪里就无关紧要。
然而,如果你想在mod2内创建Universe的实例,你需要导入mod1,因为即使在mod1导入mod2之后,Universe也不在mod2命名空间中。

的确,虽然有一些解决方法——mod1 类可以将 Universe 对象传递给 mod2,如果需要的话,mod2 可以创建更多实例。 - ilya n.

4
import 关注的是名称 -- 大多数情况下是在某个模块中绑定在顶层(也称为全局级别、模块级别名称)的“裸名称”。例如,在你执行 import mod2 后,你可以将 mod2 命名空间作为一个可用名称(如果你正在执行 import 本身作为顶层,这是最常见的;但是在函数内部进行本地 import 将使 mod2 成为该函数的本地变量等),因此你可以使用 mod2.foobar 来访问在 mod2 中绑定在顶层的名称 foobar。如果你没有必要访问这些名称,则无需在你自己的模块中 import mod2

1

把import想象成更像链接器。
使用"import mod2",你只是告诉Python可以在mod2.py文件中找到该函数。


2
请记住,导入是在运行时发生的。没有编译时间。你不需要声明所有东西。你只需提供一种解决名称的方法。 - S.Lott
这是否相关?导入仅告诉虚拟机要查找哪个文件,它在执行此操作时不会有任何影响。 - Martin Beckett
1
将import基本上视为整个文件的#include组合,以及名称更改xxx->mod.xxx。 - ilya n.
但是 - 我认为这真正是我的问题 - 我需要一个导入来知道show_answer的参数是否有answer()方法吗? - Steve Folly
@Steve,不是的,你可以尝试在任何对象上调用任何方法,如果该对象恰好因为某种原因具有该方法,则调用将成功。 - ilya n.

1

在Python中,import将模块加载到给定的命名空间中。因此,就好像def show_answer实际上存在于mod1.py模块中一样。由于这个原因,mod2.py不需要知道Universe类,因此您不需要从mod2.py导入mod1。


不会,因为导入发生在 Universe 类的定义之前。 - ilya n.
我有些措辞不当,我会更正为我原本想说的话。 - AlbertoPL

1

实际上,在这种情况下,在mod2.py中导入mod1应该不会起作用。
这不会创建循环引用吗?


确实,您可以在我的回答中的链接中查看更多信息。 - ilya n.
哎呀,是的,你说得对。我把示例代码削减得太多了。试着想象一下类 Universe 是在一个独立的模块中! - Steve Folly

1
实际上,根据this explanation,循环的import不会按您所期望的方式工作:如果取消注释import mod1,第二个模块仍然无法知道Universe
我认为这很合理。 如果您的两个文件都需要访问某些特定对象的类型,例如Universe,您有几个选择:
  • 如果程序很小,请使用一个文件
  • 如果很大,您需要决定您的文件是否都需要知道如何实现Universe,也许将尚未知类型的对象传递给show_answer就可以了
  • 如果这对您没有用,那么请把Universe放在一个单独的模块中并首先加载它。

是的,对于循环导入我很抱歉,那是我的错误。不过我的问题是:mod2 不需要 知道 Universe 是如何实现的吗?只要传递一个具有 answer() 方法的 ExamQuestion 对象给 show_answer 就可以了吗? - Steve Folly
是的,没错。我认为这使得Python更易于理解。虽然有些人抱怨这可能会导致在错误类型上调用方法的错误,但我还没有在实践中看到过这种情况。 - ilya n.

1

我对C++不是很了解,所以不能直接进行比较,但是...

import 基本上是将其他Python脚本(mod2.py)加载到当前脚本(mod1.py的顶层)中。它不是一个链接,更接近于一个 eval

例如,在Python风格的伪代码中:

eval("mod2.py")

与...相同

from mod2 import *

它执行mod2.py,并使定义的函数/类在当前脚本中可访问。

上述两个片段都允许您调用show_answer()(好吧,eval并不完全像这样工作,因此我称其为伪代码!)

import mod2

..基本上是一样的,但是它不是将所有函数带入“顶层”,而是将它们带入mod2模块中,因此您可以通过执行show_answer来调用它。

mod2.show_answer

我想问一下,[mod2.py中的导入]是不是不必要的?
完全正确。事实上,如果您尝试从mod2导入mod1,则会出现循环依赖错误(因为mod2然后尝试导入mod1等等...)。

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