Nico Schlömer
Nico Schlömer

Reputation: 58791

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

Upvotes: 4

Views: 1875

Answers (3)

glenfant
glenfant

Reputation: 1318

Try putting this in your __init__.py file:

from .tools import foo
del tools

Upvotes: 0

Pierre
Pierre

Reputation: 13046

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 foos defined in other modules.

Upvotes: 0

user2357112
user2357112

Reputation: 281277

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 x.y is registered in sys.modules, then the x module has a y attribute referring to the x.y 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.

Upvotes: 4

Related Questions