¿Cómo importar miembros de todos los módulos dentro de un paquete?

Estoy desarrollando un paquete que tiene una estructura de archivos similar a la siguiente:

test.py package/ __init__.py foo_module.py example_module.py 

Si llamo al import package en test.py, quiero que el módulo del paquete se vea similar a esto:

 >>> vars(package) mapping_proxy({foo: <function foo at 0x…}, {example: <function example at 0x…}) 

En otras palabras, quiero que los miembros de todos los módulos en el package estén en el espacio de nombres del package , y no quiero que los módulos estén en el espacio de nombres. package no es un subpaquete

Digamos que mis archivos se ven así:

foo_module.py:

 def foo(bar): return bar 

example_module.py:

 def example(arg): return foo(arg) 

test.py:

 print(example('derp')) 

¿Cómo estructuro las declaraciones de importación en test.py, example_module.py y __init__.py para trabajar desde fuera del directorio del paquete (es decir, test.py) y dentro del mismo paquete (es decir, foo_module.py y example_module.py)? Todo lo que bash da al Parent module '' not loaded, cannot perform relative import o ImportError: No module named 'module_name' .

Además, como nota al margen (según PEP 8): “Las importaciones relativas para las importaciones dentro del paquete son altamente desalentadas. Siempre use la ruta absoluta del paquete para todas las importaciones. Incluso ahora que PEP 328 está completamente implementado en Python 2.5, su estilo de las importaciones relativas explícitas se desaconseja activamente; las importaciones absolutas son más portátiles y generalmente más legibles “.

Estoy usando Python 3.3.

Quiero que los miembros de todos los módulos en el paquete estén en el espacio de nombres del paquete, y no quiero que los módulos estén en el espacio de nombres.

Pude hacerlo adaptando algo que he usado en Python 2 para importar automáticamente complementos para que también funcionen en Python 3.

En pocas palabras, así es como funciona:

  1. El archivo __init_.py del paquete importa todos los demás archivos de Python en el mismo directorio de paquetes que no comienzan con un carácter '_' (guión bajo).

  2. Luego agrega cualquier nombre en el espacio de nombres del módulo importado al de __init__ module’s (que también es el espacio de nombres del paquete). Nota: tuve que hacer que el módulo example_module import foo explícitamente desde foo_module .

Un aspecto importante de hacer las cosas de esta manera es darse cuenta de que es dynamic y no requiere que los nombres de los módulos del paquete estén codificados en el archivo __init__.py . Por supuesto, esto requiere más código, pero también lo hace muy genérico y capaz de trabajar con casi cualquier paquete (de un solo nivel), ya que importará automáticamente nuevos módulos cuando se agreguen y ya no intentará importar ninguno eliminado. desde el directorio.

test.py :

 from package import * print(example('derp')) 

__init__.py :

 def _import_all_modules(): """ Dynamically imports all modules in this package. """ import traceback import os global __all__ __all__ = [] globals_, locals_ = globals(), locals() # Dynamically import all the package modules in this file's directory. for filename in os.listdir(__name__): # Process all python files in directory that don't start # with underscore (which also prevents this module from # importing itself). if filename[0] != '_' and filename.split('.')[-1] in ('py', 'pyw'): modulename = filename.split('.')[0] # Filename sans extension. package_module = '.'.join([__name__, modulename]) try: module = __import__(package_module, globals_, locals_, [modulename]) except: traceback.print_exc() raise for name in module.__dict__: if not name.startswith('_'): globals_[name] = module.__dict__[name] __all__.append(name) _import_all_modules() 

foo_module.py :

 def foo(bar): return bar 

example_module.py :

 from package.foo_module import foo # added def example(arg): return foo(arg) 

Creo que puede obtener los valores que necesita sin saturar su espacio de nombres, usando las from module import name estilo de from module import name . Creo que estas importaciones funcionarán para lo que estás pidiendo:

Importaciones para example_module.py :

 from package.foo_module import foo 

Importaciones para __init__.py :

 from package.foo_module import foo from package.example_module import example __all__ = [foo, example] # not strictly necessary, but makes clear what is public 

Importaciones para test.py :

 from package import example 

Tenga en cuenta que esto solo funciona si está ejecutando test.py (o algo más en el mismo nivel de la jerarquía de paquetes). De lo contrario, deberá asegurarse de que la carpeta que contiene el package encuentre en la ruta de búsqueda del módulo de Python (ya sea instalando el paquete en algún lugar donde Python lo busque o agregando la carpeta correspondiente a sys.path ).