otro - leer pdf en python
¿Cómo organizar múltiples archivos Python en un solo módulo sin que se comporte como un paquete? (4)
¿Hay alguna manera de usar __init__.py
para organizar múltiples archivos en un módulo ?
Motivo: los módulos son más fáciles de usar que los paquetes, ya que no tienen tantas capas de espacio de nombres.
Normalmente hace un paquete, esto lo consigo. El problema es con un paquete, ''importar el paquete'' me da un espacio de nombres vacío. Luego, los usuarios deben usar "from thepackage import *" (mal visto) o saber exactamente lo que contiene y extraerlo manualmente en un espacio de nombres utilizable.
Lo que quiero es que el usuario ''importe el paquete'' y tenga espacios de nombres limpios y agradables que se vean así, exponiendo funciones y clases relevantes para el proyecto.
current_module
/
doit_tools/
/
- (class) _hidden_resource_pool
- (class) JobInfo
- (class) CachedLookup
- (class) ThreadedWorker
- (Fn) util_a
- (Fn) util_b
- (Fn) gather_stuff
- (Fn) analyze_stuff
El trabajo del mantenedor sería evitar definir el mismo nombre en diferentes archivos, lo que debería ser fácil cuando el proyecto es pequeño como el mío.
También sería bueno si las personas pueden hacer from doit_stuff import JobInfo
y hacer que recupere la clase, en lugar de un módulo que contenga la clase.
Esto es fácil si todo mi código está en un archivo gigantesco, pero me gusta organizar cuando las cosas empiezan a hacerse grandes. Lo que tengo en el disco se ve así:
place_in_my_python_path/
doit_tools/
__init__.py
JobInfo.py
- class JobInfo:
NetworkAccessors.py
- class _hidden_resource_pool:
- class CachedLookup:
- class ThreadedWorker:
utility_functions.py
- def util_a()
- def util_b()
data_functions.py
- def gather_stuff()
- def analyze_stuff()
Solo los separé para que mis archivos no sean grandes ni navegables. Todos están relacionados, aunque alguien (posible yo) puede querer usar las clases por sí mismo sin importar todo.
He leído varias sugerencias en varios hilos, esto es lo que sucede con cada sugerencia que puedo encontrar sobre cómo hacer esto:
Si no uso un __init__.py
, no puedo importar nada porque Python no desciende a la carpeta de sys.path.
Si uso un __init__.py
blanco , cuando import doit_tools
es un espacio de nombres vacío sin nada en él. Ninguno de mis archivos importados, lo que hace que sea más difícil de usar.
Si __all__
los submódulos en __all__
, puedo usar la sintaxis (¿mal visto?) from thing import *
, pero todas mis clases están detrás de barreras de espacio de nombres innecesarias de nuevo. El usuario tiene que (1) saber que debe usar from x import *
lugar de import x
, (2) modificar manualmente las clases hasta que puedan obedecer razonablemente las restricciones de estilo de ancho de línea.
Si agrego from thatfile import X
declaraciones from thatfile import X
a __init__.py
, me acerco pero tengo conflictos de espacio de nombres (?) Y espacios de nombres adicionales para cosas que no quería que estuvieran allí. En el siguiente ejemplo, verá que:
- La clase JobInfo sobrescribió el objeto del módulo denominado JobInfo porque sus nombres eran los mismos. De alguna manera Python puede resolver esto, porque JobInfo es del tipo
<class ''doit_tools.JobInfo.JobInfo''>
. (doit_tools.JobInfo es una clase, pero doit_tools.JobInfo.JobInfo es esa misma clase ... esto está enredado y parece muy malo, pero no parece romper nada). - Cada nombre de archivo llegó al espacio de nombres doit_tools, lo que hace que sea más confuso mirar si alguien está mirando el contenido del módulo. Quiero que doit_tools.utility_functions.py mantenga algún código, no defina un nuevo espacio de nombres.
.
current_module
/
doit_tools/
/
- (module) JobInfo
/
- (class) JobInfo
- (class) JobInfo
- (module) NetworkAccessors
/
- (class) CachedLookup
- (class) ThreadedWorker
- (class) CachedLookup
- (class) ThreadedWorker
- (module) utility_functions
/
- (Fn) util_a
- (Fn) util_b
- (Fn) util_a
- (Fn) util_b
- (module) data_functions
/
- (Fn) gather_stuff
- (Fn) analyze_stuff
- (Fn) gather_stuff
- (Fn) analyze_stuff
Además, alguien que importa solo la clase de abstracción de datos obtendría algo diferente de lo que esperaban cuando lo hicieran ''desde doit_tools import JobInfo'':
current_namespace
/
JobInfo (module)
/
-JobInfo (class)
instead of:
current_namespace
/
- JobInfo (class)
Entonces, ¿es esta una forma incorrecta de organizar el código de Python? Si no es así, ¿cuál es la forma correcta de dividir el código relacionado pero aún así recopilarlo en forma de módulo?
¿Tal vez el mejor de los casos es que hacer ''from doit_tools import JobInfo'' es un poco confuso para alguien que usa el paquete?
¿Tal vez un archivo python llamado ''api'' para que las personas que usan el código hagan lo siguiente ?:
import doit_tools.api
from doit_tools.api import JobInfo
=========================================
Ejemplos en respuesta a los comentarios:
Tome los siguientes contenidos del paquete, dentro de la carpeta ''foo'' que está en la ruta de Python.
foo/__init__.py
__all__ = [''doit'',''dataholder'',''getSomeStuff'',''hold_more_data'',''SpecialCase'']
from another_class import doit
from another_class import dataholder
from descriptive_name import getSomeStuff
from descriptive_name import hold_more_data
from specialcase import SpecialCase
foo/specialcase.py
class SpecialCase:
pass
foo/more.py
def getSomeStuff():
pass
class hold_more_data(object):
pass
foo/stuff.py
def doit():
print "I''m a function."
class dataholder(object):
pass
Hacer esto:
>>> import foo
>>> for thing in dir(foo): print thing
...
SpecialCase
__builtins__
__doc__
__file__
__name__
__package__
__path__
another_class
dataholder
descriptive_name
doit
getSomeStuff
hold_more_data
specialcase
another_class
y descriptive_name
están another_class
cosas, y también tienen copias adicionales de, por ejemplo, doit () debajo de sus espacios de nombres.
Si tengo una clase llamada Data dentro de un archivo llamado Data.py, cuando hago ''de Datos de importación de datos'' entonces aparece un conflicto de espacio de nombres porque Data es una clase en el espacio de nombres actual que está dentro de los datos del módulo, de alguna manera también está en el espacio de nombres actual. (Pero Python parece ser capaz de manejar esto).
Defina __all__ = [''names'', ''that'', ''are'', ''public'']
en el __init__.py
por ejemplo:
__all__ = [''foo'']
from ._subpackage import foo
Ejemplo del mundo real: numpy/__init__.py
.
Usted tiene una idea equivocada sobre cómo funcionan los paquetes de Python:
Si no uso un
__init__.py
, no puedo importar nada porque Python no desciende a la carpeta de sys.path.
Necesita el archivo __init__.py
en versiones de Python anteriores a Python 3.3 para marcar un directorio que contiene un paquete de Python.
Si uso un
__init__.py
blanco, cuando importo doit_tools es un espacio de nombre vacío sin nada en él. Ninguno de mis archivos importados, lo que hace que sea más difícil de usar.
No impide la importación:
from doit_tools import your_module
Funciona como se esperaba.
Si
__all__
los submódulos en__all__
, puedo usar la sintaxis (¿mal visto?)from thing import *
, pero todas mis clases están detrás de barreras de espacio de nombres innecesarias de nuevo. El usuario debe (1) saber que debe usarfrom x import *
lugar deimport x
, (2) modificar manualmente las clases hasta que puedan obedecer razonablemente las restricciones de estilo de ancho de línea.
(1) Sus usuarios (en la mayoría de los casos) no deberían usar from your_package import *
fuera de un shell interactivo de Python.
(2) podría usar ()
para romper una línea larga de importación:
from package import (function1, Class1, Class2, ..snip many other names..,
ClassN)
Si agrego
from thatfile import X
declaracionesfrom thatfile import X
a__init__.py
, me acerco pero tengo conflictos de espacio de nombres (?) Y espacios de nombres adicionales para cosas que no quería que estuvieran allí.
Depende de usted resolver los conflictos de espacio de nombres (diferentes objetos con el mismo nombre). El nombre puede referirse a cualquier objeto: entero, cadena, paquete, módulo, clase, funciones, etc. Python no puede saber qué objeto puede preferir e incluso si pudiera, sería incoherente ignorar algunos enlaces de nombre en este caso particular con respecto al uso de enlaces de nombre en todos los demás casos.
Para marcar nombres como no públicos, puede prefijarlos con _
eg, package/_nonpublic_module.py
.
Existen razones perfectamente válidas para ocultar la subestructura de un paquete (no solo cuando se depura). Entre ellos se encuentran la conveniencia y la eficiencia . Al tratar de hacer un prototipo rápido con un paquete, es extremadamente molesto tener que interrumpir el hilo de pensamiento solo para buscar la información totalmente inútil sobre cuál es el submódulo exacto para una función o clase específica.
Cuando todo está disponible en el nivel superior de un paquete, The idiom:
python -c ''import pkg; help(pkg)''
muestra toda la ayuda , no solo algunos nombres de módulos miserables.
Siempre puede desactivar las importaciones de submódulos para el código de producción, o para limpiar los módulos del paquete después del desarrollo.
La siguiente es la mejor manera que he encontrado hasta ahora. Maximiza la conveniencia al intentar no suprimir los errores válidos. Ver también la fuente completa con la documentación más doctest .
Defina el nombre del paquete y los submódulos que se importan para evitar la duplicación propensa a errores:
_package_ = ''flat_export''
_modules_ = [''sub1'', ''sub2'', ''sub3'']
Use importaciones relativas cuando esté disponible (esto es imperativo, vea is_importing_package
):
_loaded = False
if is_importing_package(_package_, locals()):
for _module in _modules_:
exec (''from .'' + _module + '' import *'')
_loaded = True
del(_module)
Intente importar el paquete, incluidos __all__
.
Esto ocurre cuando se ejecuta un archivo de módulo como script con el paquete en la ruta de búsqueda (por ejemplo, python flat_export/__init__.py
)
if not _loaded:
try:
exec(''from '' + _package_ + '' import *'')
exec(''from '' + _package_ + '' import __all__'')
_loaded = True
except (ImportError):
pass
Como último recurso, intente importar los submódulos directamente.
Esto sucede cuando se ejecuta un archivo de módulo como secuencia de comandos dentro del directorio del paquete sin el paquete en la ruta de búsqueda (por ejemplo, cd flat_export; python __init__.py
).
if not _loaded:
for _module in _modules_:
exec(''from '' + _module + '' import *'')
del(_module)
Construye __all__
(dejando fuera los módulos), a menos que se haya importado antes:
if not __all__:
_module_type = type(__import__(''sys''))
for _sym, _val in sorted(locals().items()):
if not _sym.startswith(''_'') and not isinstance(_val, _module_type) :
__all__.append(_sym)
del(_sym)
del(_val)
del(_module_type)
Aquí está la función is_importing_package
:
def is_importing_package(_package_, locals_, dummy_name=None):
""":returns: True, if relative package imports are working.
:param _package_: the package name (unfortunately, __package__
does not work, since it is None, when loading ``:(``).
:param locals_: module local variables for auto-removing function
after use.
:param dummy_name: dummy module name (default: ''dummy'').
Tries to do a relative import from an empty module `.dummy`. This
avoids any secondary errors, other than::
ValueError: Attempted relative import in non-package
"""
success = False
if _package_:
import sys
dummy_name = dummy_name or ''dummy''
dummy_module = _package_ + ''.'' + dummy_name
if not dummy_module in sys.modules:
import imp
sys.modules[dummy_module] = imp.new_module(dummy_module)
try:
exec(''from .'' + dummy_name + '' import *'')
success = True
except:
pass
if not ''sphinx.ext.autodoc'' in __import__(''sys'').modules:
del(locals_[''is_importing_package''])
return success
Puede hacerlo, pero no es realmente una buena idea y está luchando contra la forma en que se supone que funcionan los módulos / paquetes de Python. Al importar los nombres apropiados en __init__.py
puede hacerlos accesibles en el espacio de nombres del paquete. Al eliminar los nombres de los módulos, puede hacerlos inaccesibles. (Para saber por qué necesita eliminarlos, vea esta pregunta ). Para que pueda acercarse a lo que desea con algo como esto (en __init__.py
):
from another_class import doit
from another_class import dataholder
from descriptive_name import getSomeStuff
from descriptive_name import hold_more_data
del another_class, descriptive_name
__all__ = [''doit'', ''dataholder'', ''getSomeStuff'', ''hold_more_data'']
Sin embargo, esto interrumpirá los intentos posteriores de import package.another_class
. En general, no puede importar nada desde un package.module
sin hacer que package.module
accesible como una referencia importable a ese módulo (aunque con __all__
puede bloquear from package import module
).
De manera más general, dividiendo su código por clase / función, está trabajando contra el sistema de paquete / módulo de Python. Un módulo Python generalmente debe contener cosas que desea importar como una unidad. No es raro importar componentes de submódulo directamente en el espacio de nombres del paquete de nivel superior para mayor comodidad, pero a la inversa --- tratando de ocultar los submódulos y permitir el acceso a sus contenidos solo a través del espacio de nombres del paquete de nivel superior --- va a conducir a los problemas Además, no se gana nada tratando de "limpiar" el espacio de nombres de los paquetes de los módulos. Se supone que esos módulos están en el espacio de nombres del paquete; ahí es donde pertenecen.
Python no es java. El nombre del archivo del módulo no necesita ser el mismo que el nombre de la clase. De hecho, Python recomienda utilizar todas las minúsculas para el nombre del archivo del módulo.
Además, "de math import sqrt" solo agregará sqrt al espacio de nombres, no a las matemáticas.