26

I have a module from a child package that is imported dynamically; how can I iterate over the classes that it contains?

I have been importing the module and listing the names like this:

package = current_module.__name__
package = package[:package.rindex(".")] # get the package
package = "%s.sub.%s"%(package,name) # make the name of the child
print "(loading package %s)"%package
module = __import__(package) # this succeeds
for name,obj in inspect.getmembers(module):
    print name,type(obj)

This only prints module attributes and not the class types that the module defines:

__builtins__ <type 'dict'>
__doc__ <type 'NoneType'>
__file__ <type 'str'>
__name__ <type 'str'>
__package__ <type 'NoneType'>
__path__ <type 'list'>
imported_package <type 'module'>

It seems that my classes are not in the __dict__ unless the fromlist is non-empty! The values in the from-list don't seem to be validated though; [""] seems to work just fine, and suddenly the classes show up!

Can anyone explain why this is?

(Standard ubuntu python 2.7.1+ (r271:86832)

Neuron
  • 5,141
  • 5
  • 38
  • 59
Will
  • 73,905
  • 40
  • 169
  • 246

3 Answers3

56

Example: to create a dict that maps the names to the classes:

dict([(name, cls) for name, cls in mod.__dict__.items() if isinstance(cls, type)])

where mod is the loaded module

pvoosten
  • 3,247
  • 27
  • 43
  • This isn't working unless the import has a fromlist-clause; question updated. – Will Sep 28 '11 at 15:44
  • 1
    Oh and minor point i think you neant issubclass in a try block? – Will Sep 28 '11 at 15:45
  • (First) You're right about the fromlist: I usually use `[None]` for that. Don't know why it's necessary. (Second) No, I really meant `isinstance`, not `issubclass` and without `try`. Each class is also an object of type 'type'. – pvoosten Sep 29 '11 at 09:30
  • 1
    `fromlist` is required to obtain what you want if and only if the module you are importing is not a top-level module (or rather, it does not contain dots). If an imported module name contains dots, the `__import__` call will return the leftmost module object if fromlist is not present, and the rightmost if fromlist is present. Compare `__import__('pkg.mod').__name__` and `__import__('pkg.mod', fromlist="").__name__`: the first is `pkg`, the second is `mod`. – Marco Capitani Apr 01 '18 at 09:46
5

If you define __all__ in the module you are importing, which defines which symbols will be exported, you can iterate by selecting these items specifically.

map(module.__dict__.get, module.__all__)
huntie
  • 950
  • 9
  • 14
4

Method 1:

import inspect
import mymodule

for name, obj in inspect.getmembers(mymodule):
    if inspect.isclass(obj):
        do stuff...

Method 2:

desired_classes = [obj for name, obj in somemodule.__dict__.items() if isinstance(obj, DesiredType)]

Method 3:

Inside the module you want to iterate on:

File: mymodule.py

class Dog:
    VOICE = 'haou'


class Cat:
    VOICE = 'meew'


class ImNotIncluded:
    VOICE = 'what a shame'


__all__ = ['Dog', 'Cat']

>>> from mymodule import *
>>> Dog.VOICE
'haou'
>>> Cat.VOICE
'meew'
>>> ImNotIncluded.VOICE
Traceback (most recent call last):
    File "<stdin>", line 1, in <module>
NameError: name 'ImNotIncluded' is not defined

Now to iterate you do:

>>> for cls in map(mymodule.__dict__.get, mymodule.__all__): cls
...
<class 'mymodule.Dog'>
<class 'mymodule.Cat'>
 
ניר
  • 1,204
  • 1
  • 8
  • 28