简体   繁体   中英

Import method from Python submodule in __init__, but not submodule itself

I have a Python module with the following structure:

mymod/
    __init__.py
    tools.py
# __init__.py
from .tools import foo
# tools.py
def foo():
    return 42

Now, when import mymod , I see that it has the following members:

mymod.foo()
mymod.tools.foo()

I don't want the latter though; it just pollutes the namespace.

Funnily enough, if tools.py is called foo.py you get what you want:

mymod.foo()

(Obviously, this only works if there is just one function per file.)

How do I avoid importing tools ? Note that putting foo() into __init__.py is not an option. (In reality, there are many functions like foo which would absolutely clutter the file.)

The existence of the mymod.tools attribute is crucial to maintaining proper function of the import system. One of the normal invariants of Python imports is that if a module xy is registered in sys.modules , then the x module has a y attribute referring to the xy module. Otherwise, things like

import x.y
x.y.y_function()

break, and depending on the Python version, even

from x import y

can break. Even if you don't think you're doing any of the things that would break, other tools and modules rely on these invariants, and trying to remove the attribute causes a slew of compatibility problems that are nowhere near worth it.


Trying to make tools not show up in your mymod module's namespace is kind of like trying to not make "private" (leading-underscore) attributes show up in your objects' namespaces. It's not how Python is designed to work, and trying to force it to work that way causes more problems than it solves.

The leading-underscore convention isn't just for instance variables. You could mark your tools module with a leading underscore, renaming it to _tools . This would prevent it from getting picked up by from mymod import * imports (unless you explicitly put it in an __all__ list), and it'd change how IDEs and linters treat attempts to access it directly.

Try putting this in your __init__.py file:

from .tools import foo
del tools

You are not importing the tools module, it's just available when you import the package like you're doing:

import mymod

You will have access to everything defined in the __init__ file and all the modules of this package:

import mymod

# Reference a module
mymod.tools

# Reference a member of a module
mymod.tools.foo

# And any other modules from this package
mymod.tools.subtools.func

When you import foo inside __init__ you are are just making foo available there just like if you have defined it there, but of course you defined it in tools which is a way to organize your package, so now since you imported it inside __init__ you can:

import mymod

mymod.foo()

Or you can import foo alone:

from mymod import foo

foo()

But you can import foo without making it available inside __init__ , you can do the following which is exactly the same as the example above:

from mymod.tools import foo

foo()

You can use both approaches, they're both right, in all these example you are not "cluttering the file" as you can see accessing foo using mymod.tools.foo is namespaced so you can have multiple foo s defined in other modules.

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