modules - python no module named
Python: compartir código común entre una familia de scripts (3)
Mi preferencia sería un directorio "bin" o "scripts" separado, con subproyectos como bibliotecas / paquetes:
projectroot
|
|- scripts
|
|- lib
| |
| `- matcher.py
| `- merger.py
| `- subproject1
| `- subproject2
| `- subproject3
La idea de ser sus scripts puede hacer referencia a los subproyectos necesarios como paquetes habituales. Y sus subproyectos también pueden hacer referencia entre sí con las importaciones.
También puede tener un script principal o compartido que configure los paquetes de subproyectos para usted, si eso le ayuda.
Estoy escribiendo una familia de scripts de Python dentro de un proyecto; cada script está dentro de un subdirectorio del proyecto, de esta manera:
projectroot
|
|- subproject1
| |
| |- script1.main.py
| `- script1.merger.py
|
|- subproject2
| |
| |- script2.main.py
| |- script2.matcher.py
| `- script2.merger.py
|
`- subproject3
|
|- script3.main.py
|- script3.converter.py
|- script3.matcher.py
`- script3.merger.py
Ahora varios de los scripts comparten algún código. El código compartido se considera mejor como parte del proyecto en sí, y no es algo que compilaría por separado y crearía una biblioteca o dejaría caer en un PYTHONPATH en todo el sitio. Podría colocar ese código en varios lugares, como en el projectroot
directorio projectroot
, o en un directorio secundario de projectroot
llamado common
(quizás).
Sin embargo, la mayoría de las formas en las que he pensado hasta ahora implican hacer paquetes de mis subproyectos con archivos __init__.py
vacíos y usar importaciones relativas (o sys.path
redundante con sys.path
en cada subproyecto. Peor aún, parece como construir una estructura de paquetes Alrededor de esta familia de scripts se opone a la siguiente advertencia del PEP-3122 rechazado:
¡Atención! Este PEP ha sido rechazado. Guido ve ejecutando scripts dentro de un paquete como un anti-patrón.
Si las secuencias de comandos dentro de un paquete son anti-patrón, ¿cómo puedo configurar las cosas de una manera que mantenga el código común en el mismo proyecto? ¿O es aceptable aquí un módulo y un sistema basado en paquetes? ¿Cuál es el enfoque más limpio? (FWIW preferiría tener un archivo como shared.py
o common.py
en el directorio raíz del proyecto, en lugar de crear un directorio de utilidades que sea hermano de los subproyectos "reales").
Sugiero colocar scripts triviales de "iniciador" en el nivel superior de su proyecto y convertir cada una de las carpetas de subproyectos en paquetes. Los módulos en los paquetes pueden importarse entre sí o el código común puede ser factorizado en un paquete common
.
Así es como se vería la estructura, si asumimos que los diversos módulos de merger
se pueden refactorizar en una versión compartida:
projectroot
|- script1.py # launcher scripts, see below for example code
|- script2.py
|- script3.py
|
|- common
| |- __init__.py
| |- merger.py # from other packages, use from ..common import merger to get this
|
|- subproject1
| |- __init__.py # this can be empty
| |- script1_main.py
|
|- subproject2
| |- __init__.py
| |- script2_main.py
| |- script2_matcher.py
|
|- subproject3
|- __init__.py
|- script3_main.py
|- script3_converter.py
|- script3_matcher.py
Los scripts de inicio pueden ser muy simples:
from subproject1 import script1_main
if __name__ == "__main__":
script1_main.main()
Es decir, todo lo que hace es importar el módulo "scriptN_main" apropiado y ejecutar una función en él. El uso de un script simple también puede tener algunos beneficios pequeños para la velocidad de inicio del script, ya que el módulo main
puede tener su bytecode compilado en caché en un archivo .pyc
, mientras que los scripts nunca se almacenan en caché.
Nota: Cambié el nombre de sus módulos, cambiando _
caracteres por el .
caracteres. No puedes tener un .
en un identificador (como el nombre de un módulo), ya que Python espera que indique el acceso a los atributos. Eso significaba que esos módulos nunca podrían ser importados. (Supongo que esto es solo un artefacto de los archivos de ejemplo, no algo que tenga en su código real).
Utilice setuptools
para distribuir scripts y bibliotecas:
p.ej
from setuptools import setup
setup(
# other arguments here... (e.g. packages / package_dir)
entry_points = {
''console_scripts'': [
''script1 = subproject1.script1:main'',
''script2 = subproject2.script2:main'',
],
}
)
Si puede escribir todo su código como bibliotecas y no necesita módulos separados para tener sus puntos de entrada, esta es la herramienta para usted. Si tiene scripts, eso también está bien, pero necesitará una función main
que pueda hacer referencia (consulte el ejemplo anterior)