【问题标题】:Python: why can't an imported module reference another imported module?Python:为什么一个导入的模块不能引用另一个导入的模块?
【发布时间】:2010-10-11 18:40:11
【问题描述】:

ma​​in.py:

import subone
import subtwo

subone.py:

a = 'abc'

subtwo.py:

print subone.a

运行python main.py 会抛出NameError: name 'subone' is not defined。我希望它打印“abc”。

重构它以使用 from import 和类没有帮助:

ma​​in.py:

from subone import *   # Only using from X import * for example purposes.
from subtwo import *

print 'from main.py:', a.out

subone.py:

class A:
    out = 'def'

a = A()

subtwo.py:

# This throws NameError: name 'a' is not defined
print a.out

# This throws NameError: name 'A' is not defined
b = A()
print b.out

但是它打印'from main.py: def'。 (使用import 也可以。)

为什么会这样?似乎一旦 subone 被导入,它应该对 subtwo 可用。

是不是因为不通过它们的“父”模块而让导入的模块相互依赖是不好的编程?还有其他标准方法吗?

更新:

我现在明白第一个示例不起作用,因为行 print subone.a 无法识别名称 subone,它不在 subtwo 命名空间中(即使它是在main.py's) 中,它是从模块subtwo 中调用的。这可以通过在subtwo.py 顶部使用import subone 来解决——它不会重新加载模块,但会将其添加到subtwo 的命名空间中,以便subtwo 可以使用它。

但是这个呢:

ma​​in.py:

from subone import Nugget
from subtwo import Wrap

wrap = Wrap()
print wrap.nugget.gold

subone.py:

class Nugget:
    gold = 'def'

subtwo.py:

class Wrap:
    nugget = Nugget()

我认为由于WrapNugget 都直接加载到main 的命名空间中,它们将使用main 的命名空间并能够相互引用,但它会抛出一个@ 987654350@。是因为Wrap 在被加载到main 的命名空间之前,是从subtwo 的命名空间内部 评估/检查的吗?

【问题讨论】:

  • 你的封装好像真的坏了……
  • 您需要查找词法范围。基本思想是代码可以访问它可以在源代码中“看到”的内容。运行时发生的事情与它无关。

标签: python import module


【解决方案1】:

如果您以这种方式修改了 subtwo.py,那么它将起作用

import subone
print subone.a

当你在 subtwo.py 中做 subone.a 时,你试图访问 subtwo.py 中的命名空间 subone,在命名空间“subone”中,应该有一个属性“a”。

当你这样做时 - 在 subtwo.py 中导入 subone,然后 subone 被添加到命名空间并且 subone 命名空间具有属性 a。所以 subone.a 会起作用。

我还建议您使用 dir() 来查看命名空间是如何添加的。

在 subtwo.py 中,您可以执行以下操作:

print dir()
import subone
print dir()
print subone.a

同样,尝试在导入语句之前和之后添加“print dir()”,您应该会明白这个想法。

"import x" 将 'x' 添加到当前模块 命名空间,而“from x import *”将 添加所有模块级属性 直接进入当前模块命名空间

所以在上面第一个 main.py、subone.py 和 subtwo.py 示例中,main.py 中的命名空间将包含“subone”和“subtwo”,而 subtwo.py 将有一个空命名空间和无法访问 subone.a。

[编辑:更多解释] 考虑以下文件: main.py

print "Before importing subone : ", dir()
import subone
print "After importing subone and before importing subtwo: ",  dir()
import subtwo
print "After importing subone and subtwo: ", dir()

subone.py

a = 'abc'

subtwo.py

print dir()
import subone
print "module level print: ", subone.a
print dir()
def printX():
    print subone.a

以及运行main.py的输出:

Before importing subone :  ['__builtins__', '__doc__', '__file__', '__name__', '__package__']
After importing subone and before importing subtwo:  ['__builtins__', '__doc__', '__file__', '__name__', '__package__', 'subone']
['__builtins__', '__doc__', '__file__', '__name__', '__package__']
module level print:  abc
['__builtins__', '__doc__', '__file__', '__name__', '__package__', 'subone']
After importing subone and subtwo:  ['__builtins__', '__doc__', '__file__', '__name__', '__package__', 'subone', 'subtwo']

一些观察

  1. 您会注意到导入模块 subtwo.py 后,立即执行打印语句。
  2. 所以当在 main.py 中导入 subone 和 subtwo 时,main.py 的命名空间被扩充了。
  3. 这并不意味着 subtwo 的命名空间会增加。所以“a”只能通过 subone.a 在 main.py 中使用
  4. 当我们在 subtwo.py 中导入 subone 时,subtwo 的命名空间会增加 subone 并且模块 subone 的属性 a 在 subtow.py 中通过 subone.a 可用

【讨论】:

  • 这很有帮助。 dir() 真的让我很清楚。我唯一的问题(正如我在下面评论的)是,如果“from x import *”将所有模块级属性直接添加到当前命名空间中,那么为什么这些属性不能相互访问?
  • 该导入实际上并没有改变属性的范围,它只是将它们的定义复制到本地范围。
  • @willell:Ignacio Vazquez-Abrams 是正确的。我应该更清楚。这与复制代码不同,因为它发生在“C”中#include 的情况下。导入的模块已经加载和评估。如果您在导入的模块中有一个 print 语句,那么它已经被评估了。
  • @Ignacio:你是说每个属性都有自己的命名空间,继承自它来自的模块?
  • 每个属性都有自己的作用域,引用会随身携带。
【解决方案2】:

当 subone 被 main 导入时,你能解释一下为什么你觉得 subone 应该对 subtwo 可用吗?事实上,subtwo.py 可以在不知道 main.py 导入了什么的情况下进行编译。

另外,如果第二个程序导入 subtwo.py,subtwo 对 subone 的了解是否应该取决于两个主程序中的哪一个正在导入 subtwo?这会降低 subtwo 的可重用性。

您似乎将编译视为具有定义顺序的过程,累积状态信息:编译 main.py,在此期间我们编译/导入 subone.py,从中累积信息,然后我们编译/使用我们已经积累的信息导入 subtwo.py。

相反,每个模块的编译都是独立于其他模块的,除非声明了依赖关系。这使得重用和维护代码变得更加容易:隐藏的依赖项更少。

是不是因为编程不好 导入的模块依赖于每个 其他没有经过他们的 “父”模块?

不是这样......让模块 2 依赖于模块 1 只是糟糕的编程不这么说,即没有模块 2 声明“我依赖于模块 1”。

【讨论】:

  • 啊,我明白了。你描述它的方式有帮助。我想我已经习惯了 PHP 的 includerequire 语句,它们直接嵌入代码。相反,模块是需要调用的对象。
  • @willell:我从未使用过 PHP,但 C 与预处理器 #include 类似,只是将整个文件粘贴在那里。无论如何,无论语言如何,像这样的某种隐式依赖似乎都不聪明。
  • @willell,这是有道理的……您的想法确实与 PHP 和 C 包含机制的工作方式相匹配。但是,您可以看到这如何导致难以追踪的依赖关系......为了找出文件 B 所依赖的内容,您必须查看包含文件 B 的所有位置。因此,“包含文件”和“导入模块”之间存在根本区别。前者更像是文本字符串的简单字面插入,不考虑语义后果。后者更多是一个程序组件与另一个程序组件的概念链接。
  • 是的,我明白了。无论如何,您所说的概念链接是我使用 Python 的重要原因。唯一让我感到困惑的是:如果from import 将所有模块级对象加载到当前命名空间中,那么为什么这些对象不能相互引用?是因为这些对象在加载到当前模块之前在它们各自的模块中进行了评估?
【解决方案3】:

subtwo 命名空间将完全为空,除非您将 subone 导入其中。

就编程实践而言,subonesubtwo 可以根据需要相互依赖,您只需要显式链接它们(使用 import

【讨论】:

  • 好的,但是为什么即使使用 from - import 也不起作用,它将所有内容加载到 main 的命名空间中?
  • 没关系,当subtwo 运行时(当主import 运行时), 命名空间中没有任何内容。
  • 记住 Python 中的 import 与其他语言中的 include 不同,这可能会有所帮助。
【解决方案4】:

关于你的第二个例子,“main.py”知道Nugget,但“subtwo.py”不知道。

我认为这样想会有所帮助。每个模块(文件)都必须像唯一存在的其他模块是它导入的模块一样工作。在这种情况下,“subtwo.py”将无法自行运行,因为它没有导入Nugget。本质上“subtwo.py”不知道“main.py”知道什么。它不应该,因为任何人都可以从任何地方调用它,而且它不能依赖任何其他人导入它需要的东西。

【讨论】:

    【解决方案5】:

    这是因为导入的模块有自己独立的命名空间。你写的很像:

    def func1():
        a = 1
    
    def func2():
        print a
    
    func1()
    func2()  # This will throw an error
    
    a = 2
    
    func2()  # Now this will print 2.
    

    模块在本地有自己的命名空间,当您使用from subone import * 时,您只能将命名空间导入main.py 命名空间,而subtwo 无法访问该命名空间。

    尽管如此 - 您尝试做的是非常糟糕的做法。避免使用全局变量和import *,因为你会像现在这样越来越糊涂。


    更多信息: https://docs.python.org/3/reference/import.html

    https://bytebaker.com/2008/07/30/python-namespaces/

    http://www.diveintopython.net/html_processing/locals_and_globals.html

    也许:http://sebastianraschka.com/Articles/2014_python_scope_and_namespaces.html

    【讨论】:

      猜你喜欢
      • 1970-01-01
      • 2018-04-23
      • 1970-01-01
      • 2020-05-18
      • 1970-01-01
      • 2022-01-08
      • 1970-01-01
      • 2014-01-12
      • 1970-01-01
      相关资源
      最近更新 更多