问题
I'm doing some patching of import statements, and I need to know exactly what members are imported by from m import *
. The documentation seems to indicate that when __all__
is absent, all members will be imported that do not begin with an underscore. Is this exactly correct under all circumstances? I know that inspect.getmembers()
, dir()
, and m.__dict__
all have slightly different logic, so I'm not completely sure which (if any) will provide the same list as import *
.
回答1:
Let's take a look at what that from m import *
statement does:
>>> dis.dis(compile('from m import *', '<module>', 'single'))
1 0 LOAD_CONST 0 (0)
2 LOAD_CONST 1 (('*',))
4 IMPORT_NAME 0 (m)
6 IMPORT_STAR
8 LOAD_CONST 2 (None)
10 RETURN_VALUE
The key here is that it actually invokes a dedicated opcode IMPORT_STAR
, and this is implementation specific to the interpreter that will execute this code. This operator was originally specified in PEP-0221 but the implementation details specified is in the comments introduced by this specific commit.
In CPython, this is found in /Python/ceval.c (Python 3.7.2) and it in turns call import_all_from which shows the general logic on what that actually does inside the bytecode interpreter.
In PyPy, this is found in /pypy/interpreter/pyopcode.py, and again much like the C implementation it invokes the import_all_from function defined in RPython, which again has a similar logic but in a more familiar syntax for Python programmers.
In both the CPython and pypy implementation, if __all__
is presented as a list of names inside the imported module, all matching assignments will be added to the current local scope, including those names that are prefixed with an underscore (_
). Otherwise, every assignment inside the module that do not start with an underscore will be added to the current local scope.
回答2:
I'm currently using the following function to get a list of names and calling getattr(m, name)
on each name:
def public_members(module):
try:
return module.__all__ # If not iterable, imports will break.
except AttributeError:
return [name for name in dir(module) if not name.startswith('_')]
回答3:
This is probably the hackiest thing you'll see all day, but it might do the trick.
bound = globals().copy()
from module import *
for k, v in list( globals().items() ):
if k not in bound or bound[ k ] != v:
print( 'new', repr( k ), repr( v ) )
来源:https://stackoverflow.com/questions/54798679/how-to-determine-what-is-imported-via-from-m-import