14

I found this, but that's not quite what I want to do.

I want to import all the classes in all the files in a directory. Basically, I want to replace this:

from A import *
from B import *
from C import *

With something dynamic, so that I don't have keep editing my __init__.py every time I add another file.


The glob solution seems to be the equivalent of

import A
import B
import C

which is not the same at all.

Community
  • 1
  • 1
mpen
  • 272,448
  • 266
  • 850
  • 1,236
  • What do you want to achieve with this? – Ignacio Vazquez-Abrams Jun 05 '11 at 22:56
  • And what's wrong with this answer: http://stackoverflow.com/questions/1057431/loading-all-modules-in-a-folder-in-python/1057534#1057534 ? – viraptor Jun 05 '11 at 22:58
  • @Ignacio: Last sentence. I've got about 15 models, and all of them need to be imported, always. Doesn't make sense for me to have to keep updating `__init__.py` when this can be done programatically. – mpen Jun 05 '11 at 23:08
  • 7
    @Ignacio: You're not making sense. Why? Less typing. Less error prone. Won't forget to update the file. Saves time. Do I really need to justify this? – mpen Jun 06 '11 at 00:54
  • So then you're not doing this for the purpose of having plugins? – Ignacio Vazquez-Abrams Jun 06 '11 at 00:55
  • Explicit is better than implicit. – AJ. Jun 06 '11 at 00:56
  • @Mark: Absolutely less typing involved, but that doesn't mean it is less error prone. In fact, in deference to AJ's comment, I think it is potentially more error prone. – Greg Jun 06 '11 at 14:07
  • 2
    @Ignacio: No, no plugins. It's not supposed to be something like where you drop a file in there to include a plugin. Everything is going to be built by me. I've got a whole bunch of classes and I've split them all into separate files. The framework needs to load them all on startup so it knows they exist, and it also makes it easy for me to work with them without having to import a dozen different things all the time. – mpen Jun 06 '11 at 17:34
  • 1
    @AJ/Greg: Maybe. But they're classes. They're unlikely to introduce subtle bugs unless they're executed. – mpen Jun 06 '11 at 17:35

2 Answers2

14

You can do something like this, although keep in mind isinstance(cls, type) only works with new-style classes.

import os, sys

path = os.path.dirname(os.path.abspath(__file__))

for py in [f[:-3] for f in os.listdir(path) if f.endswith('.py') and f != '__init__.py']:
    mod = __import__('.'.join([__name__, py]), fromlist=[py])
    classes = [getattr(mod, x) for x in dir(mod) if isinstance(getattr(mod, x), type)]
    for cls in classes:
        setattr(sys.modules[__name__], cls.__name__, cls)
Zach Kelling
  • 52,505
  • 13
  • 109
  • 108
  • 3
    Yes, I saw that answer, it doesn't work. It imports the modules, but not the classes. I'd have to access the file as `address.Address` whereas I need to be able access it with just `Address`. There's a difference. – mpen Jun 05 '11 at 23:06
  • You should use `os.path.join(os.path.dirname(__file__), '*.py')` instead of `os.path.dirname(__file__) + "/*.py"`. – Artur Gaspar Jun 05 '11 at 23:06
  • @Mark Adjusted my answer to reflect that. – Zach Kelling Jun 05 '11 at 23:44
  • Hrm....close. This `'.'.join([pkg, py])` needs to go one more level up. i.e., I had to replace it with `'.'.join(['shipments', pkg, py])` because the root of my project is one level higher. Can't we get the absolute path of the module so this doesn't have to be hard-coded? – mpen Jun 06 '11 at 00:01
  • 1
    @Mark Yeah sure, should just use `__name__` anyways. Adjusted again, see if that works for ya. – Zach Kelling Jun 06 '11 at 00:12
  • 2
    For me, `ImportError: No module named ...` occurs at `mod = __import__('.'.join([__name__, py]), fromlist=[py])`. – SparkAndShine Mar 15 '16 at 22:00
  • @zeekay what's the purpose of `setattr(sys.modules[__name__], cls.__name__, cls)`? – Nauman Naeem Jul 28 '22 at 19:20
8

Let's assume your file structure is as follows:

/Foo
    A.py
    B.py
    C.py

To import all at once, you need to create the __init__.py file in the directory you'd like to import everything from, with following code inside:

__all__ = ['A', 'B', 'C']

This is the file structure after those changes:

/Foo
    A.py
    B.py
    C.py
    __init__.py

Then, you can simply use:

from Foo import *
Danny Bullis
  • 3,043
  • 2
  • 29
  • 35
Kamil Marczak
  • 161
  • 1
  • 3