简体   繁体   中英

How to force Python's “import” to treat name as a module, not a function?

I'm using SymPy, and encountering the following problem:

>>> import sympy.utilities.lambdify
>>> sympy.utilities.lambdify
<function lambdify at 0x0000000003802B38>

Wait, shouldn't sympy.utilities.lambdify be a module?

>>> from sympy.utilities.lambdify import implemented_function
>>> implemented_function
<function implemented_function at 0x0000000003802CF8>
>>> implemented_function.__module__
'sympy.utilities.lambdify'

Oh, so there's a naming conflict.

Now obviously I can just use implemented_function , but that's not my question.

My question is: how can I import the sympy.utilities.lambdify module rather than the function ?

(I just used SymPy for illustration here. Whether or not this is proper library usage is beside the point. The source is here .)

Actually, it looks like this can be done quite cleanly with importlib:

import importlib
lamdify = importlib.import_module('sympy.utilities.lambdify')
print lambdify
  <module 'sympy.utilities.lambdify' from 'c:\python-2.7.8-amd64\lib\site-packages\sympy\utilities\lambdify.pyc'>

EDIT from comments (thanks @Mehrdad):

Alternatively, if you want to avoid putting module names in strings, you could do this:

def import_module(lambda_): 
    import importlib; 
    return importlib.import_module('.'.join(lambda_.__code__.co_names)) 

Usage: lambdify = import_module(lambda: sympy.utilities.lambdify)

Though this looks a bit longer and less clear to me.

The apparent inconsistency is due to the fact that lambdify is not a module, it's a python file containing multiple function definitions. Consider the following example in a file called bar.py :

def foo(): pass

def bar(): pass

Then from bar import bar; print(bar.__module__) from bar import bar; print(bar.__module__) will give you 'bar' .

The reason lambdify can be imported like this is the line in the __init__ of sympy.utilities :

from .lambdify import lambdify

That is, in the lambdify.py file of the sympy.utilities module, there's a definition for lambdify() , and this is imported into the sympy.utilities namespace. So when you import sympy.utilities , you have access to lambdify. But you can still import other functions defined in lambdify.py by from sympy.utilities.lambdify import ... .


Your actual question is whether you can import a module like this. Well, you can , but you probably shouldn't. Let's model the situation with the above bar.py , and make it a package by creating an __init__py :

from .bar import bar

Putting these two files in the directory named tmp , we get a model of your scenario, with a module named tmp and a "submodule" bar (but I don't think it's a proper submodule, lacking an __init__.py ). The same thing happens if we import tmp.bar , as with sympy :

>>> import tmp
>>> tmp
<module 'tmp' from '/home/user/python/tmp/__init__.py'>
>>> import tmp.bar
>>> tmp.bar
<function bar at 0x7f24426461e0>
>>> tmp.bar.foo
Traceback (most recent call last):
  File "<stdin>", line 1, in <module>
AttributeError: 'function' object has no attribute 'foo'
>>> from tmp.bar import foo
>>> foo
<function foo at 0x7f2442646158>

So tmp.bar refers to the function rather than the module. Apparently. But actually it also gives access to the module, despite it being shadowed by the function of the same name!

Let's repeat the above, but keep monitoring sys.modules :

>>> import sys
>>> 'tmp' in sys.modules
False
>>> import tmp.bar
>>> tmp.bar
<function bar at 0x7fc833025158>
>>> 'tmp.bar' in sys.modules
True
>>> tmp.bar.foo
Traceback (most recent call last):
  File "<stdin>", line 1, in <module>
AttributeError: 'function' object has no attribute 'foo'
>>> sys.modules['tmp.bar'].foo
<function foo at 0x7fc833020f28>

So while tmp.bar refers to the function in the local namespace, we can access the module itself through sys.modules['tmp.bar'] coming from the same import.

Just to be explicit:

>>> import sys
>>> import sympy.utilities.lambdify
>>> lambdify_module = sys.modules['sympy.utilities.lambdify']
>>> lambdify_module.implemented_function
<function implemented_function at 0x7fc83183ca60>

This is specific to the structure of sympy . See this line of the source code for the utilities __init__.py :

from .lambdify import lambdify

It imports the function lambdify from the local module lambdify ; thus utilities.lambdify is the function .

Supposing that you wish to truly import the module, and not the function, lambdify . Perhaps something like,

import os
import imp

import sympy.utilities as mod
sub_mod_name = 'lambdify'
sub_mod_file = '{}.py'.format(sub_mod_name)

mod_loc = os.path.dirname(mod.__file__)
file_ = os.path.join(mod_loc, sub_mod_file)  # Full path for source file
sub_mod = imp.load_source(sub_mod_name, file_)  # Import module from source file

print sub_mod
# <module 'lambdify' from '/usr/local/lib/python2.7/dist-packages/sympy/utilities/lambdify.pyc'>

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