【问题标题】:Differences between `import module` and `from package import module``import module` 和 `from package import module` 的区别
【发布时间】:2012-03-22 21:44:44
【问题描述】:

我一直在学习一些用于 python 的动态“插件加载”,并注意到 import modulefrom package import module 之间的一个不是真正的问题而是一个有趣的区别。

我创建了一个包含四个文件的测试脚本(类似于我自己的设置,以实现我想要实现的目标)

文件树如下所示:

  • 测试(主包)
    • sup(包,插件文件夹)
      • __init__.py
      • uber.py(插件)
    • __init__.py
    • bar.py ('main'-program)
    • foo.py(需要动态添加功能的对象)
    • poo.py(装饰器)

poo.py:

from test import foo

def decorate(method):
    print "before:", method.__name__ in dir(foo.Foo)
    setattr(foo.Foo, method.__name__, method)
    print "after :", method.__name__ in dir(foo.Foo)
    return method

foo.py:

import os

class Foo(object):

    def __init__(self):
        self.__loadplugins("sup")

    @classmethod
    def __loadplugins(cls, plugindir):
        for f in os.listdir(os.path.join(os.path.dirname(__file__), plugindir)):
            if f.endswith(".py"):
                __import__(("%s.%s" % (plugindir, f))[0:-3])

uber.py:

from test import poo

@poo.decorate
def aFunction(self, anArg):
    print anArg

我有两个版本的 bar.py,这个不行:

import foo

f = foo.Foo()

f.aFunction("print goes here") # pylint: disable-msg=E1101

这个确实有效:

from test import foo

f = foo.Foo()

f.aFunction("print goes here") # pylint: disable-msg=E1101

两个条之间的唯一区别是导入。一个是相对的,另一个不是。但是相对的不起作用,绝对的起作用。有没有人可以在他的机器上复制这个并且可以对它发生的原因给出某种解释?

更新
认为记下我的python版本会很有用: 使用普通的 python 版本 2.7.2 x86

更新
'错误' bar.py 的输出:

before: False
after : True
Traceback (most recent call last):
  File "C:\Users\Daan\workspace\python\mytests\src\test\bar.py", line 6, in <module>
    f.aFunction("print goes here") # pylint: disable-msg=E1101
AttributeError: 'Foo' object has no attribute 'aFunction'

“正确”bar.py 的输出:

before: False
after : True
print goes here

【问题讨论】:

  • 尝试在您的测试目录中添加一个名为__init__.py的空文件
  • 他们在那里。有点懒得把它们放在那个列表中。但我会特别为你添加它们。
  • 你说第一个不起作用是什么意思?如果您看到异常,请包含回溯。
  • @subdir 完成,这是一个相当标准的错误消息。希望这能进一步澄清这一点。

标签: python


【解决方案1】:

我假设你在 'test' 目录中并且有环境变量 PYTHONPATH=..(所以你可以同时执行 'import foo' 和 'from test import foo')。

在这种情况下 foo 和 test.foo 是两个不同的模块,分别加载(尽管它们是从同一个文件加载的)。

test.poo 模块中的'decorate' 函数向 test.foo 中的类 'Foo' 添加方法(poo.py 的第一行:“from test import foo”),同时来自foo 模块保持不变。

【讨论】:

  • 是的,您的第一个假设是正确的。在 Eclipse 中,pythonpath 中有一个“src”目录。 test 是一个包,我将 bar.py 作为 python-run 运行。
  • 好的,我想我回答了你的问题?
  • 我必须先测试它们,还没有时间尝试所有答案 :-)
  • 我确实混淆了 PYTHONPATH 的。我的“测试”包位于一个名为 src 的源目录中。我从测试中运行。但 src 也被添加到 pythonpath 中。我现在已经重组了我的代码/布局,现在它工作得很好。谢谢。
【解决方案2】:

你是如何执行 bar.py 的?我假设您要将它作为一个包运行,因为这就是您设计它的方式。否则,bar.py 的第二个版本中的from test import foo 没有多大意义,因为除非它作为包运行,否则 bar.py 将无法识别test

查看Intra-package references 的python 文档。他们主要谈论两种参考。比如说,你正试图从 foo.py 导入 uber.py。一种方法是通过from .sup import uber 类型的显式相对引用。另一种方法是绝对引用,格式为from test.sup import uberimport test.sup.uber

除了foo.py 之外,您似乎一直在使用绝对引用。在这里,您实际上是在调用__import__('sup.uber'),而它应该是__import__('test.sup.uber')。我不确定这是否是导致您报告的错误的原因,但我能够使用 bar.py 的两个版本来运行,

我在foo.py 中使用它来运行bar.py 的两个版本:

__import__(("%s.%s" % ('test.'+plugindir, f))[0:-3])

另外,你是如何将 bar.py 作为一个包运行的?一种方法是在test 目录之外的脚本中包含import test.bar 行,然后运行它。

【讨论】:

    【解决方案3】:

    对您的问题的简短回答:

    有没有人可以在他的机器上复制这个并且可以对它发生的原因给出某种解释?

    是的。 :-)

    稍长一些:

    是的 - 我能够重现它。

    这里是我的解释:

    在第一种情况下(发生错误),python 两次包含模块foo(您可以使用打印语句轻松检查)。类 Foo 被第二次创建,aFunction 被添加到第二个化身 - 仍然主程序使用第一个。

    在第二种情况下,模块foo 只被读入一次——因此Foo 类只存在一次。使用该类的相同化身添加 aFunction 和在 main 中。

    两次导入文件的原因是恕我直言,模块的排列有些奇怪。看起来对于 python,foo 模块的内部使用路径/名称在第一个示例中有所不同。

    (我使用了很多 print 中的 id(foo.Foo)globals() 来解决这个问题。)

    【讨论】:

      猜你喜欢
      • 1970-01-01
      • 1970-01-01
      • 2021-11-11
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      • 2022-09-28
      • 1970-01-01
      • 2020-12-14
      相关资源
      最近更新 更多