¿Cómo recuperar la ruta de un módulo?

757

Quiero detectar si el módulo ha cambiado. Ahora, usar inotify es simple, solo necesita conocer el directorio desde el que desea recibir notificaciones.

¿Cómo recupero la ruta de un módulo en Python?

Alegre
fuente
99
Si usted todavía está buscando en este sitio, por favor, actualice la respuesta correcta a esta . Es mucho más limpio que la solución propuesta y funciona también en casos donde __file__no está configurado.
erikbwork
3
@ erikb85: no solo es más limpio; inspectbasada en soluciones también funciona para execfile()mayúsculas y minúsculas cuando __file__produce un nombre incorrecto en silencio.
jfs
import pathlib, module; pathlib.Path(module.__file__).resolve().parentEsto
depende de la

Respuestas:

907
import a_module
print(a_module.__file__)

En realidad, le dará la ruta al archivo .pyc que se cargó, al menos en Mac OS X. Así que supongo que puede hacer:

import os
path = os.path.abspath(a_module.__file__)

También puedes probar:

path = os.path.dirname(a_module.__file__)

Para obtener el directorio del módulo.

orestis
fuente
54
esto responde cómo obtener la ruta del módulo que importa, pero no del módulo / script en el que se encuentra (para el script que está ejecutando, __file__no es una ruta completa, es relativa). Para el archivo en el que estoy, tuve que importar otro módulo desde el mismo directorio y hacer lo que se muestra aquí. ¿Alguien sabe una manera más conveniente?
Ben Bryant
55
@hbdgaf bastante seguro de que no existe un dispositivo incorporadoself.__file__
Dan Passaro
26
@BenBryant @hbdgaf os.path.dirname(__file__)funciona bien para mí y devuelve la ruta abs del directorio del módulo.
Niccolò
99
Intenté hacer esto y obtuve el rastreo: AttributeError: 'module' object has no attribute '__file__'
Dorian Dore
55
@DorianDore Estaba jugando un poco con los módulos y llegué a una solución path = module.__path__.__dict__["_path"][0], pero no estoy seguro de si es portátil o si tampoco difiere entre las versiones de Python. Está funcionando para mí, a diferencia de esta respuesta que me da el mismo error y la inspectrespuesta aumenta TypeError: <module 'module' (namespace)> is a built-in module...
Jezor
279

Hay un inspectmódulo en python.

Documentación oficial

El módulo de inspección proporciona varias funciones útiles para ayudar a obtener información sobre objetos vivos como módulos, clases, métodos, funciones, trazas, objetos de marco y objetos de código. Por ejemplo, puede ayudarlo a examinar el contenido de una clase, recuperar el código fuente de un método, extraer y formatear la lista de argumentos para una función u obtener toda la información que necesita para mostrar un rastreo detallado.

Ejemplo:

>>> import os
>>> import inspect
>>> inspect.getfile(os)
'/usr/lib64/python2.7/os.pyc'
>>> inspect.getfile(inspect)
'/usr/lib64/python2.7/inspect.pyc'
>>> os.path.dirname(inspect.getfile(inspect))
'/usr/lib64/python2.7'
Tomás Tomecek
fuente
77
También puede usar inspectpara obtener el nombre del archivo actual; ver stackoverflow.com/a/50905/320036
z0r el
55
Busqué en Google esta pregunta muchas veces y esta es la respuesta más razonable que he visto. inspect.currentframe()
Actualice
el inspect.getfile()enfoque no funciona con el módulo _io, pero funciona con el módulo io.
smwikipedia
70

Como han dicho las otras respuestas, la mejor manera de hacerlo es con __file__(se demuestra nuevamente a continuación). Sin embargo, hay una advertencia importante, que __file__NO existe si está ejecutando el módulo por sí solo (es decir, como __main__).

Por ejemplo, supongamos que tiene dos archivos (ambos en su PYTHONPATH):

#/path1/foo.py
import bar
print(bar.__file__)

y

#/path2/bar.py
import os
print(os.getcwd())
print(__file__)

Ejecutar foo.py dará el resultado:

/path1        # "import bar" causes the line "print(os.getcwd())" to run
/path2/bar.py # then "print(__file__)" runs
/path2/bar.py # then the import statement finishes and "print(bar.__file__)" runs

SIN EMBARGO si intenta ejecutar bar.py por sí solo, obtendrá:

/path2                              # "print(os.getcwd())" still works fine
Traceback (most recent call last):  # but __file__ doesn't exist if bar.py is running as main
  File "/path2/bar.py", line 3, in <module>
    print(__file__)
NameError: name '__file__' is not defined 

Espero que esto ayude. Esta advertencia me costó mucho tiempo y confusión al probar las otras soluciones presentadas.

mcstrother
fuente
55
En este caso, puede usar sys.argv [0] en lugar del archivo .
Jimothy
44
¿Es esa una advertencia específica de la versión? En 2.6 y 2.7, confío con éxito en el archivo , que funciona cuando el nombre __ == '__ main '. El único caso de falla que he visto es con "python -c 'print file '". Agregaré que a veces el archivo puede ser '<stdin>', lo que sucede cuando IDEs como emacs ejecutan el búfer actual.
Paul Du Bois
1
Tenga en cuenta que los caracteres "__" iniciales y finales ponen una palabra en negrita, así que tenga esto en cuenta cuando lea los comentarios anteriores :-P
Paul Du Bois
1
@PaulDuBois Puede rodearlo con tics posteriores: ` __file__` se convierte__file__
fncomp
1
¿Cómo se obtiene el NameError? @ Pablo Du Bois: he tratado de Python 2.3 a 3.4 y __file__se define sin embargo corro archivo de Python: python a.py, python -ma, ./a.py.
jfs
43

Intentaré abordar algunas variaciones sobre esta pregunta también:

  1. encontrar la ruta del guión llamado
  2. encontrar la ruta del script que se está ejecutando actualmente
  3. encontrar el directorio del script llamado

(Algunas de estas preguntas se han formulado en SO, pero se han cerrado como duplicados y se han redirigido aquí).

Advertencias de uso __file__

Para un módulo que haya importado:

import something
something.__file__ 

devolverá la ruta absoluta del módulo. Sin embargo, dado el siguiente script foo.py:

#foo.py
print '__file__', __file__

Llamarlo con 'python foo.py' devolverá simplemente 'foo.py'. Si agrega un shebang:

#!/usr/bin/python 
#foo.py
print '__file__', __file__

y llamarlo usando ./foo.py, devolverá './foo.py'. Llamarlo desde un directorio diferente (por ejemplo, poner foo.py en la barra de directorio), luego llamar

python bar/foo.py

o agregando un shebang y ejecutando el archivo directamente:

bar/foo.py

devolverá 'bar / foo.py' (la ruta relativa ).

Encontrar el directorio

Ahora, ir desde allí para obtener el directorio, os.path.dirname(__file__)también puede ser complicado. Al menos en mi sistema, devuelve una cadena vacía si la llama desde el mismo directorio que el archivo. ex.

# foo.py
import os
print '__file__ is:', __file__
print 'os.path.dirname(__file__) is:', os.path.dirname(__file__)

dará salida:

__file__ is: foo.py
os.path.dirname(__file__) is: 

En otras palabras, devuelve una cadena vacía, por lo que esto no parece confiable si desea usarlo para el archivo actual (a diferencia del archivo de un módulo importado). Para evitar esto, puede envolverlo en una llamada a abspath:

# foo.py
import os
print 'os.path.abspath(__file__) is:', os.path.abspath(__file__)
print 'os.path.dirname(os.path.abspath(__file__)) is:', os.path.dirname(os.path.abspath(__file__))

que produce algo como:

os.path.abspath(__file__) is: /home/user/bar/foo.py
os.path.dirname(os.path.abspath(__file__)) is: /home/user/bar

Tenga en cuenta que abspath () NO resuelve enlaces simbólicos. Si desea hacer esto, use realpath () en su lugar. Por ejemplo, hacer un enlace simbólico file_import_testing_link apuntando a file_import_testing.py, con el siguiente contenido:

import os
print 'abspath(__file__)',os.path.abspath(__file__)
print 'realpath(__file__)',os.path.realpath(__file__)

la ejecución imprimirá rutas absolutas como:

abspath(__file__) /home/user/file_test_link
realpath(__file__) /home/user/file_test.py

file_import_testing_link -> file_import_testing.py

Usar inspeccionar

@SummerBreeze menciona el uso del módulo de inspección .

Esto parece funcionar bien, y es bastante conciso, para módulos importados:

import os
import inspect
print 'inspect.getfile(os) is:', inspect.getfile(os)

obedientemente devuelve el camino absoluto. Sin embargo, para encontrar la ruta del script que se está ejecutando actualmente, no vi una manera de usarlo.

jpgeek
fuente
1
inspect.getfile (os) es lo mismo que os .__ file__ del código: def getfile (objeto): "" "Determine en qué fuente o archivo compilado se definió un objeto." "" if ismodule (objeto): if hasattr (objeto, ' archivo '): devolver objeto .__ archivo__
idanzalz
44
Puede usar inspect.getfile(inspect.currentframe())para obtener la ruta del script que se está ejecutando actualmente.
jbochi
33

No entiendo por qué nadie habla de esto, pero para mí la solución más simple es usar imp.find_module ("modulename") (documentación aquí ):

import imp
imp.find_module("os")

Da una tupla con el camino en segunda posición:

(<open file '/usr/lib/python2.7/os.py', mode 'U' at 0x7f44528d7540>,
'/usr/lib/python2.7/os.py',
('.py', 'U', 1))

La ventaja de este método sobre el "inspeccionar" es que no necesita importar el módulo para que funcione, y puede usar una cadena de entrada. Útil cuando se comprueban módulos llamados en otro script, por ejemplo.

EDITAR :

En python3, el importlibmódulo debería hacer:

Doc de importlib.util.find_spec:

Devuelve la especificación para el módulo especificado.

Primero, se comprueba sys.modules para ver si el módulo ya se importó. Si es así, entonces sys.modules [nombre]. Se devuelve la especificación . Si eso se establece en Ninguno, se genera ValueError. Si el módulo no está en sys.modules, se busca en sys.meta_path una especificación adecuada con el valor de 'ruta' dado a los buscadores. No se devuelve ninguno si no se puede encontrar ninguna especificación.

Si el nombre es para submódulo (contiene un punto), el módulo principal se importa automáticamente.

Los argumentos de nombre y paquete funcionan igual que importlib.import_module (). En otras palabras, los nombres de módulos relativos (con puntos iniciales) funcionan.

PlasmaBinturong
fuente
2
imp NO se deprecia en python 2 (versión actual 2.7.13). imp se deprecia en python 3 desde 3.4. importlib se utilizará en python 3 en su lugar. Me gusta esta solución, porque incluso funciona cuando falla la importación real (por ejemplo, porque el módulo de 64 bits para un motor de 32 bits)
mdew
Y REALMENTE agradable cuando intenta encontrar la ruta de sqlite3 de 64 bits y falla la importación. Perfecto.
rahvin_t
2
importlib.machinery.PathFinder().find_module("os").get_filename()La alternativa más corta imp.find_moduleque he encontrado en Python3 +. Si alguien está buscando el uso de importlib.util.find_spec.
Torxed
Este método funciona sin importar el módulo real, lo cual es excelente ya que lo estoy usando para averiguar qué versión de un módulo se está importando desde una computadora compartida.
Gouda
19

Esto fue trivial.

Cada módulo tiene una __file__variable que muestra su ruta relativa desde donde se encuentra ahora.

Por lo tanto, obtener un directorio para que el módulo lo notifique es simple como:

os.path.dirname(__file__)
Alegre
fuente
14
Casi pero no del todo correcto - el archivo no es "relativo a donde estás ahora"; cuando es relativo (que será solo cuando haya rutas relativas en sys.path), es relativo a dónde estaba cuando se cargó el módulo .
Charles Duffy
17
import os
path = os.path.abspath(__file__)
dir_path = os.path.dirname(path)
vinoth
fuente
1
no funciona en mi Linux Python 2.6 ya que __file__es solo dir / test.py, abspath involucra el cwd para completar el nombre de ruta que no es el resultado deseado, pero si importa un módulo, entonces m.__file__da el resultado deseado.
Ben Bryant
10
import module
print module.__path__

Paquetes apoyan un atributo más especial, __path__. Esto se inicializa para ser una lista que contiene el nombre del directorio que contiene el paquete __init__.pyantes de que se ejecute el código en ese archivo. Esta variable puede ser modificada; hacerlo afecta las búsquedas futuras de módulos y subpaquetes contenidos en el paquete.

Si bien esta característica no se necesita con frecuencia, se puede usar para extender el conjunto de módulos que se encuentran en un paquete.

Fuente

Lukas Greblikas
fuente
9

Utilidad de línea de comando

Puede ajustarlo a una utilidad de línea de comando,

python-which <package name>

ingrese la descripción de la imagen aquí


Crear /usr/local/bin/python-which

#!/usr/bin/env python

import importlib
import os
import sys

args = sys.argv[1:]
if len(args) > 0:
    module = importlib.import_module(args[0])
    print os.path.dirname(module.__file__)

Hazlo ejecutable

sudo chmod +x /usr/local/bin/python-which
Jossef Harush
fuente
7

Así que pasé bastante tiempo tratando de hacer esto con py2exe. El problema era obtener la carpeta base del script, ya sea que se ejecutara como un script de Python o como un ejecutable de py2exe. También para que funcione si se estaba ejecutando desde la carpeta actual, otra carpeta o (esta fue la más difícil) desde la ruta del sistema.

Finalmente utilicé este enfoque, usando sys.frozen como indicador de ejecución en py2exe:

import os,sys
if hasattr(sys,'frozen'): # only when running in py2exe this exists
    base = sys.prefix
else: # otherwise this is a regular python script
    base = os.path.dirname(os.path.realpath(__file__))
uri
fuente
7

simplemente puede importar su módulo y luego presionar su nombre y obtendrá su ruta completa

>>> import os
>>> os
<module 'os' from 'C:\\Users\\Hassan Ashraf\\AppData\\Local\\Programs\\Python\\Python36-32\\lib\\os.py'>
>>>
Hassan Ashraf
fuente
4

Si desea recuperar la ruta raíz del paquete desde cualquiera de sus módulos, lo siguiente funciona (probado en Python 3.6):

from . import __path__ as ROOT_PATH
print(ROOT_PATH)

__init__.pyTambién se puede hacer referencia a la ruta principal utilizando en su __file__lugar.

¡Espero que esto ayude!

fr_andres
fuente
3

Si la única advertencia de uso __file__es cuando está actual, el directorio relativo está en blanco (es decir, cuando se ejecuta como un script desde el mismo directorio donde está el script), entonces una solución trivial es:

import os.path
mydir = os.path.dirname(__file__) or '.'
full  = os.path.abspath(mydir)
print __file__, mydir, full

Y el resultado:

$ python teste.py 
teste.py . /home/user/work/teste

El truco está or '.'después de la dirname()llamada. Establece el directorio como ., lo que significa directorio actual y es un directorio válido para cualquier función relacionada con la ruta.

Por lo tanto, el uso abspath()no es realmente necesario. Pero si lo usa de todos modos, el truco no es necesario: abspath()acepta rutas en blanco e interpreta correctamente como el directorio actual.

MestreLion
fuente
Es mejor cambiar el orden y simplemente usarlo os.path.dirname(os.path.abspath(__file__))porque no tendrás que preocuparte por las rutas relativas. Una oportunidad menos para los errores.
szali
3

Me gustaría contribuir con un escenario común (en Python 3) y explorar algunos enfoques.

La función incorporada open () acepta la ruta relativa o absoluta como su primer argumento. Sin embargo, la ruta relativa se trata como relativa al directorio de trabajo actual, por lo que se recomienda pasar la ruta absoluta al archivo.

Dicho de manera simple, si se ejecuta un archivo de comandos con el siguiente código, se no garantiza que el example.txtarchivo se creará en el mismo directorio donde se encuentra el archivo de script:

with open('example.txt', 'w'):
    pass

Para arreglar este código, necesitamos obtener la ruta al script y hacerlo absoluto. Para garantizar que la ruta sea absoluta, simplemente usamos la función os.path.realpath () . Para obtener la ruta al script hay varias funciones comunes que devuelven varios resultados de ruta:

  • os.getcwd()
  • os.path.realpath('example.txt')
  • sys.argv[0]
  • __file__

Ambas funciones os.getcwd () y os.path.realpath () devuelven resultados de ruta basados ​​en el directorio de trabajo actual . Generalmente no es lo que queremos. El primer elemento de la lista sys.argv es la ruta del script raíz (el script que ejecuta) independientemente de si llama a la lista en el script raíz o en cualquiera de sus módulos. Puede ser útil en algunas situaciones. La variable __file__ contiene la ruta del módulo desde el que se ha llamado.


El siguiente código crea correctamente un archivo example.txten el mismo directorio donde se encuentra el script:

filedir = os.path.dirname(os.path.realpath(__file__))
filepath = os.path.join(filedir, 'example.txt')

with open(filepath, 'w'):
    pass
Jeyekomon
fuente
3

Si desea conocer la ruta absoluta de su script, puede usar el objeto Ruta :

from pathlib import Path

print(Path().absolute())
print(Path().resolve('.'))
print(Path().cwd())

método cwd ()

Devuelve un nuevo objeto de ruta que representa el directorio actual (como lo devolvió os.getcwd ())

método resolve ()

Haz que la ruta sea absoluta, resolviendo los enlaces simbólicos. Se devuelve un nuevo objeto de ruta:

Vlad Bezden
fuente
1

Desde dentro de los módulos de un paquete de Python tuve que referirme a un archivo que residía en el mismo directorio que el paquete. Ex.

some_dir/
  maincli.py
  top_package/
    __init__.py
    level_one_a/
      __init__.py
      my_lib_a.py
      level_two/
        __init__.py
        hello_world.py
    level_one_b/
      __init__.py
      my_lib_b.py

Así que arriba tuve que llamar a maincli.py desde el módulo my_lib_a.py sabiendo que top_package y maincli.py están en el mismo directorio. Así es como obtengo el camino a maincli.py:

import sys
import os
import imp


class ConfigurationException(Exception):
    pass


# inside of my_lib_a.py
def get_maincli_path():
    maincli_path = os.path.abspath(imp.find_module('maincli')[1])
    # top_package = __package__.split('.')[0]
    # mod = sys.modules.get(top_package)
    # modfile = mod.__file__
    # pkg_in_dir = os.path.dirname(os.path.dirname(os.path.abspath(modfile)))
    # maincli_path = os.path.join(pkg_in_dir, 'maincli.py')

    if not os.path.exists(maincli_path):
        err_msg = 'This script expects that "maincli.py" be installed to the '\
        'same directory: "{0}"'.format(maincli_path)
        raise ConfigurationException(err_msg)

    return maincli_path

Basado en la publicación de PlasmaBinturong modifiqué el código.

Al Conrad
fuente
1

Si desea hacer esto dinámicamente en un "programa", pruebe este código:
Mi punto es que es posible que no sepa el nombre exacto del módulo para "codificarlo". Puede seleccionarse de una lista o puede no estar ejecutándose actualmente para usar __file__.

(Lo sé, no funcionará en Python 3)

global modpath
modname = 'os' #This can be any module name on the fly
#Create a file called "modname.py"
f=open("modname.py","w")
f.write("import "+modname+"\n")
f.write("modpath = "+modname+"\n")
f.close()
#Call the file with execfile()
execfile('modname.py')
print modpath
<module 'os' from 'C:\Python27\lib\os.pyc'>

Traté de deshacerme del problema "global", pero encontré casos en los que no funcionó, creo que "execfile ()" se puede emular en Python 3 Dado que esto está en un programa, se puede poner fácilmente en un método o módulo para reutilizar.

Robin Randall
fuente
0

Si lo instaló usando pip, "pip show" funciona muy bien ('Ubicación')

$ pip show detectron2

Name: detectron2
Version: 0.1
Summary: Detectron2 is FAIR next-generation research platform for object detection and segmentation.
Home-page: https://github.com/facebookresearch/detectron2
Author: FAIR
Author-email: None
License: UNKNOWN
Location: /home/ubuntu/anaconda3/envs/pytorch_p36/lib/python3.6/site-packages
Requires: yacs, tabulate, tqdm, pydot, tensorboard, Pillow, termcolor, future, cloudpickle, matplotlib, fvcore
Javi
fuente
0

Aquí hay un script bash rápido en caso de que sea útil para alguien. Solo quiero poder establecer una variable de entorno para poder pushdhacerlo con el código.

#!/bin/bash
module=${1:?"I need a module name"}

python << EOI
import $module
import os
print os.path.dirname($module.__file__)
EOI

Ejemplo de Shell:

[root@sri-4625-0004 ~]# export LXML=$(get_python_path.sh lxml)
[root@sri-4625-0004 ~]# echo $LXML
/usr/lib64/python2.7/site-packages/lxml
[root@sri-4625-0004 ~]#
musaraña
fuente