简体   繁体   中英

Detecting `import module` vs `from module import *` from module

I want to prevent certain names inside a module from being imported by the from module import * statement to reduce namespace cluttering. (Let me know if this is bad design.)

Here's the behavior I want (using os and posix as an example):

  • import os should make os.posix available.
  • from os import * should not make posix available.
  • I don't care whether from os import posix results in an error.

Is it possible for code in an imported module module to detect whether it was imported with import module or from module import * ?

I'm not sure I understand the question. You can influence which names from your module will be imported when another module imports it. eg here's a simple test module:

__all__ = ['foo']

foo = 3
bar = 4

and an interactive python session:

>>> 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

notice that test.bar is available, but bar is not because it isn't included in the __all__ list.


Finally, it is worth pointing out that the from ... import * idiom really should be avoided as much as possible. It makes mocking and testing harder, it also introduces ambiguity in where the objects originated making the code harder to read in addition to the namespace clashes that you've already mentioned.

Is it possible for code in an imported module module to detect whether it was imported with import module or from module import *?

Yes, at least in CPython. But it's not a very useful thing to do, and it's certainly not going to solve the problem you wanted to solve. The right answer is, of course, to use __all__ , as mgilson shows.

But let's show why this is the wrong answer.

First, here's one way to do it:

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')

So, now that you have that information, what can you do with it? Not import posix in the IMPORT_STAR case? Is your module still going to work without it?

On top of that, remember that modules can be—and frequently are—imported multiple times. If one module imports you with import foo , and another later does from foo import * , what do you want to happen? And, even if you have an answer, how could you possibly do that, given that your module code only gets run the first time? It would have to see into the future to detect that someone else is going to later from foo import * .

The technical post webpages of this site follow the CC BY-SA 4.0 protocol. If you need to reprint, please indicate the site URL or the original address.Any question please contact:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM