2012-01-02 23 views
15

En la siguiente jerarquía, ¿existe una forma conveniente y universal de hacer referencia al top_package utilizando un término genérico en todos los archivos .py a continuación? Me gustaría tener una forma consistente de importar otros módulos, de modo que incluso cuando el "top_package" cambie de nombre no se rompa nada.¿Cómo hacer referencia al módulo de nivel superior en Python dentro de un paquete?

No estoy a favor de usar la importación relativa como "..level_one_a" ya que la ruta relativa será diferente a cada archivo python a continuación. Estoy buscando un modo que:

  1. Cada archivo python puede tener la misma declaración de importación para el mismo módulo en el paquete.
  2. Una referencia de desacoplamiento a "top_package" en cualquier archivo .py dentro del paquete, por lo que cualquiera que sea el nombre "top_package" cambia, nada se rompe.

    top_package/ 
        __init__.py 
        level_one_a/ 
        __init__.py 
        my_lib.py 
        level_two/ 
         __init__.py 
         hello_world.py 
        level_one_b/ 
        __init__.py 
        my_lib.py 
        main.py 
    
+1

¿Por qué quiere una referencia al módulo de nivel superior? (A veces esta respuesta revela el verdadero problema/solución.) –

+1

Quiero hacer el paquete como reutilizable como sea posible, de modo que cuando cambio el nombre del módulo superior, todo lo que todavía puede trabajar normalmente sin tener que cambiar cada uno de los nombre de importación dentro. – hllau

Respuesta

10

Esto debería hacer el trabajo:

top_package = __import__(__name__.split('.')[0]) 

El truco aquí es que por cada módulo de la variable __name__ contiene la ruta completa al módulo separados por puntos, como, por ejemplo, top_package.level_one_a.my_lib. Por lo tanto, si desea obtener el nombre del paquete superior, solo necesita obtener el primer componente de la ruta e importarlo usando __import__.

A pesar de que el nombre de variable utilizado para acceder al paquete todavía se llama top_package, puede cambiar el nombre del paquete y si seguirá funcionando.

+1

-1 La pregunta solicitó una solución que no depende del nombre "top_package". –

+0

@ Jon-Eric Gracias por tu comentario. He corregido mi respuesta para trabajar independientemente del nombre del paquete superior. – jcollado

+1

Esto no es del todo correcto; si el módulo actual no se importa desde el nivel superior, su '__name__' no contendrá la ruta completa a este módulo, sino solo una parte de esto, que comenzará desde donde se importa este módulo. – thuzhf

0

Creo que # 2 es imposible sin usar importaciones relativas o el paquete nombrado. Debe especificar qué módulo desea importar llamando explícitamente su nombre o utilizando una importación relativa. de lo contrario, ¿cómo sabría el intérprete lo que quiere?

Si hacer su lanzador de aplicaciones un nivel por encima top_level/ y tienen que import top_leve l, podrá hacer referencia top_level.* desde cualquier lugar dentro del paquete top_level.

(puedo mostrar un ejemplo de software que estoy trabajando: http://github.com/toddself/beerlog/)

1

se puede utilizar una combinación de la función __import__() y el atributo __path__ de un paquete.

Por ejemplo, supongamos que desea importar <whatever>.level_one_a.level_two.hello_world desde otro lugar en el paquete. Se podría hacer algo como esto:

import os 
_temp = __import__(__path__[0].split(os.sep)[0] + ".level_one_a.level_two.hello_world") 
my_hello_world = _temp.level_one_a.level_two.hello_world 

Este código es independiente del nombre del paquete de nivel superior y se puede utilizar en cualquier parte del paquete. También es bastante feo.

5

Deja tus paquete y el guión main en un directorio contenedor externo, así:

container/ 
    main.py 
    top_package/ 
     __init__.py 
     level_one_a/ 
      __init__.py 
      my_lib.py 
      level_two/ 
       __init__.py 
       hello_world.py 
     level_one_b/ 
      __init__.py 
      my_lib.py 

cuando se ejecuta main.py, su directorio padre (container) se añadirá automáticamente al inicio de sys.path. Y dado que top_package ahora está en el mismo directorio, se puede importar desde cualquier lugar dentro del árbol de paquetes.

Así hello_world.py podría importar level_one_b/my_lib.py así:

from top_package.level_one_b import my_lib 

No importa lo que el nombre del directorio contenedor es, o dónde se encuentra, las importaciones serán siempre trabajar con este arreglo.

Pero tenga en cuenta que, en su ejemplo original, top_package fácilmente podría funcionar como el directorio propio contenedor. Todo lo que tendría que hacer es eliminar top_package/__init__.py, y se quedaría con la misma disposición.

La sentencia import anterior cambiaría entonces a:

from level_one_b import my_lib 

y que estaría libre para cambiar el nombre de top_package sin embargo usted deseaba.

+0

Si elimina top_package/__ init__.py, level_one_a y level_one_b no podrán importarse entre sí. – DonGar

+0

@DonGar. No, funcionará perfectamente bien. Pero tenga en cuenta que me refería a la estructura en la pregunta de OP, no la que se muestra en mi respuesta. La diferencia fundamental es la ubicación del script 'main.py'. – ekhumoro

+0

La parte principal de su respuesta estoy de acuerdo, pero el párrafo que comienza con "Pero tenga en cuenta que" es con lo que no estoy de acuerdo. A menos que también signifique mover tanto level_one_a como level_one_b a un nuevo submódulo de top_container. – DonGar

0

Esto funciona desde dentro de un módulo de biblioteca:

import __main__ as main_package 
TOP_PACKAGE = main_package.__package__.split('.')[0] 
Cuestiones relacionadas