从模块中检测`import module` vs` from module import *`

时间:2013-10-10 23:44:54

标签: python import

我希望阻止模块内的某些名称被from module import *语句导入,以减少名称空间混乱。 (如果这是糟糕的设计,请告诉我。)

以下是我想要的行为(以osposix为例):

  • import os应该os.posix可用。
  • from os import *不应提供posix
  • 我不关心from os import posix是否会导致错误。

导入的模块module中的代码是否可以检测它是使用import module还是from module import *导入的?

2 个答案:

答案 0 :(得分:9)

我不确定我理解这个问题。您可以影响当另一个模块导入模块时,将导入模块中的哪些名称。例如这是一个简单的test模块:

__all__ = ['foo']

foo = 3
bar = 4

和一个交互式python会话:

>>> import test
>>> test.foo
3
>>> test.bar
4
>>> from test import *
>>> foo
3
>>> bar
Traceback (most recent call last):
  File "<stdin>", line 1, in <module>
NameError: name 'bar' is not defined

请注意test.bar可用,但bar不是因为它未包含在__all__列表中。


最后,值得指出的是,from ... import *成语应该尽可能地避免。它使得模拟和测试变得更加困难,除了你已经提到的命名空间冲突之外,它还引入了对象起源的模糊性,使得代码更难以阅读。

答案 1 :(得分:0)

  

导入的模块模块中的代码是否可以检测它是使用导入模块导入还是从模块导入导入*?

是的,至少在CPython中。但这不是一件非常有用的事情,它肯定无法解决您想要解决的问题。当然,正确的答案是使用__all__,正如mgilson所示。

但是让我们说明为什么这是错误的答案。

首先,这是一种方法:

import sys
import opcode

f1 = sys._getframe(1)
op = f1.f_code[f1.f_lasti+3]
del f1
if op == opcode.opmap['IMPORT_FROM']:
    print('from me import something')
elif op == opcode.opmap['IMPORT_STAR']:
    print('from me import *')
elif op == opcode.opmap['STORE_NAME']:
    print('import me')

所以,既然你掌握了这些信息,你能用它做什么?不在posix案例中导入IMPORT_STAR?没有它,你的模块仍然可以工作吗?

最重要的是,请记住,模块可以经常多次导入。如果一个模块使用import foo导入您,而另一个模块导入from foo import *,您希望发生什么?而且,即使你有答案,你怎么可能这样做,因为你的模块代码只是第一次运行?它必须看到未来发现其他人将要迟到from foo import *