如何检索模块的路径?

我想检测模块是否已更改。现在,使用inotify很简单,您只需要知道要从中获取通知的目录。

如何在python中检索模块的路径?

987205 次浏览

这是微不足道的。

每个模块都有一个__file__变量,显示它与您现在所在位置的相对路径。

因此,获取模块通知它的目录很简单:

os.path.dirname(__file__)
import a_moduleprint(a_module.__file__)

实际上会给你加载的. pyc文件的路径,至少在Mac OS X上是这样。所以我想你可以这样做:

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

您也可以尝试:

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

获取模块的目录。

import ospath = os.path.abspath(__file__)dir_path = os.path.dirname(path)

正如其他答案所说,最好的方法是使用__file__(下面再次演示)。然而,有一个重要的警告,如果你独自运行模块(即__main__),__file__不存在。

例如,假设您有两个文件(都在您的PYTHONPATH上):

#/path1/foo.pyimport barprint(bar.__file__)

#/path2/bar.pyimport osprint(os.getcwd())print(__file__)

运行foo.py将给出输出:

/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

但是,如果您尝试自己运行bar.py,您将获得:

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

希望这能有所帮助。这个警告花了我很多时间和困惑,同时测试了其他的解决方案。

所以我花了相当多的时间尝试用py2exe做这个问题是获取脚本的基本文件夹,无论它是作为python脚本还是作为py2exe可执行文件运行。此外,无论它是从当前文件夹、另一个文件夹还是(这是最难的)从系统路径运行,都要让它正常工作。

最终我使用了这种方法,使用sys.frozen作为在py2exe中运行的指示器:

import os,sysif hasattr(sys,'frozen'): # only when running in py2exe this existsbase = sys.prefixelse: # otherwise this is a regular python scriptbase = os.path.dirname(os.path.realpath(__file__))

Python中有inspect模块。

正式留档

检查模块提供了几个有用的函数来帮助获取有关活动对象的信息,例如模块、类、方法、函数、回溯、框架对象和代码对象。例如,它可以帮助您检查类的内容,检索源方法的代码,提取和格式化函数的参数列表,或获取显示详细回溯所需的所有信息。

示例:

>>> 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'

我也会尝试解决这个问题的一些变化:

  1. 查找被调用脚本的路径
  2. 查找当前正在执行的脚本的路径
  3. 查找被调用脚本的目录

(其中一些问题已在SO上提出,但已作为副本关闭并重定向到此处。

使用注意事项__file__

对于您导入的模块:

import somethingsomething.__file__

将返回模块的绝对路径。但是,给定以下脚本foo.py:

#foo.pyprint '__file__', __file__

使用'pythonfoo.py'调用它将简单地返回'foo.py'。如果您添加一个sheband:

#!/usr/bin/python#foo.pyprint '__file__', __file__

并使用./foo.py调用它,它将返回'./foo.py'。从不同的目录调用它,(例如将foo.py放在目录栏中),然后调用

python bar/foo.py

或者添加一个sheband并直接执行文件:

bar/foo.py

将返回'bar/foo.py'(相对路径)。

查找目录

现在从那里获取目录,os.path.dirname(__file__)也可能很棘手。至少在我的系统上,如果您从与文件相同的目录调用它,它会返回一个空字符串。ex。

# foo.pyimport osprint '__file__ is:', __file__print 'os.path.dirname(__file__) is:', os.path.dirname(__file__)

将输出:

__file__ is: foo.pyos.path.dirname(__file__) is:

换句话说,它返回一个空字符串,因此如果您想将其用于当前文件(而不是导入模块的文件),这似乎不可靠。为了解决这个问题,你可以将其包装在对abspath的调用中:

# foo.pyimport osprint '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__))

它输出如下内容:

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

请注意,abspath()不会解析符号链接。如果你想这样做,请改用real path()。例如,制作一个符号链接file_import_testing_link指向file_import_testing.py,内容如下:

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

执行将打印绝对路径,例如:

abspath(__file__) /home/user/file_test_linkrealpath(__file__) /home/user/file_test.py

file_import_testing_link->file_import_testing.py

使用检查

@SummerBreeze提到使用检查模块。

对于导入的模块,这似乎工作得很好,并且非常简洁:

import osimport inspectprint 'inspect.getfile(os) is:', inspect.getfile(os)

乖乖返回绝对路径。要查找当前执行脚本的路径:

inspect.getfile(inspect.currentframe())

(感谢@jbochi)

inspect.getabsfile(inspect.currentframe())

给出当前执行脚本的绝对路径(感谢@Sadman_Sakib)。

如果使用__file__的唯一警告是当当前相对目录为空时(即,当作为脚本从脚本所在的同一目录运行时),那么一个简单的解决方案是:

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

而结果:

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

诀窍是在dirname()调用后的or '.'中。它将目录设置为.,这意味着当前目录,并且是任何与路径相关的函数的有效目录。

因此,使用abspath()并不是真正需要的。但如果您无论如何都使用它,则不需要技巧:abspath()接受空白路径并正确地将其解释为当前目录。

import moduleprint module.__path__

包支持另一个特殊属性__path__。这是初始化为包含目录名称的列表在执行该文件中的代码之前,包的__init__.py。可以修改此变量;这样做会影响将来的搜索包中包含的模块和子包。

虽然不经常需要此功能,但它可用于扩展在包中找到的一组模块。

来源

如果您想在“程序”中动态执行此操作,请尝试以下代码:
我的观点是,您可能不知道要“硬编码”它的模块的确切名称。它可以从列表中选择,也可以当前未运行以使用__file__。

(我知道,它在Python 3中不起作用)

global modpathmodname = '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'>

我试图摆脱“全局”问题,但发现它不起作用的情况我认为可以在Python 3中模拟“execfile()”由于这是在程序中,因此可以很容易地将其放入方法或模块中以供重用。

我不明白为什么没有人在谈论这个,但对我来说,最简单的解决方案是使用imp.find_module("模块名")(留档这里):

import impimp.find_module("os")

它给出了一个路径位于第二位的元组:

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

与“检查”方法相比,此方法的优势在于您不需要导入模块即可使其工作,并且您可以在输入中使用字符串。例如,在检查另一个脚本中调用的模块时很有用。

编辑

在python3中,importlib模块应该做:

文档importlib.util.find_spec

返回指定模块的规范。

首先,检查sys.modules以查看模块是否已经导入。如果是,则sys.modules[name]。返回规格。如果碰巧是设置为无,则引发ValueError。如果模块不在sys.modules,然后sys.meta_path搜索一个合适的规范给定查找器的路径值。如果没有规范可以返回,则不返回任何值找到了

如果名称是子模块(包含一个点),则父模块是自动导入。

名称和包参数的工作方式与importlib.import_module()相同。换句话说,相对模块名称(带前导点)起作用。

从python包的模块中,我必须引用与包位于同一目录中的文件。前。

some_dir/maincli.pytop_package/__init__.pylevel_one_a/__init__.pymy_lib_a.pylevel_two/__init__.pyhello_world.pylevel_one_b/__init__.pymy_lib_b.py

所以在上面,我不得不从my_lib_a.py模块调用maincli.py,知道top_package和maincli.py在同一个目录中。

import sysimport osimport imp

class ConfigurationException(Exception):pass

# inside of my_lib_a.pydef 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

基于PlasmaBinturong的发布,我修改了代码。

命令行实用程序

您可以将其调整为命令行实用程序,

python-which <package name>

在此处输入图片描述


创建/usr/local/bin/python-which

#!/usr/bin/env python
import importlibimport osimport sys
args = sys.argv[1:]if len(args) > 0:module = importlib.import_module(args[0])print os.path.dirname(module.__file__)

使其可执行

sudo chmod +x /usr/local/bin/python-which

我想贡献一个常见的场景(在Python 3中)并探索一些方法。

内置函数open()接受相对或绝对路径作为其第一个参数。尽管相对路径被视为相对于当前工作目录,但建议将绝对路径传递给文件。

简单地说,如果您使用以下代码运行脚本文件,则没有保证example.txt文件将在脚本文件所在的同一目录中创建:

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

要修复此代码,我们需要获取脚本的路径并使其成为绝对路径。为了确保路径是绝对的,我们只需使用os.path.realpath()函数。要获取脚本的路径,有几个常见的函数会返回各种路径结果:

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

函数os.getcwd()os.path.realpath()都返回基于当前工作目录的路径结果。通常不是我们想要的。sys.argv列表的第一个元素是根脚本的路径(您运行的脚本),无论您是在根脚本本身还是在其任何模块中调用列表。在某些情况下它可能会很方便。__file__变量包含调用它的模块的路径。


以下代码在脚本所在的同一目录中正确创建了一个文件example.txt

filedir = os.path.dirname(os.path.realpath(__file__))filepath = os.path.join(filedir, 'example.txt')
with open(filepath, 'w'):pass

您可以导入您的模块然后点击它的名字,你会得到它的完整路径

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

如果您想从脚本中知道绝对路径,您可以使用路径对象:

from pathlib import Path
print(Path().absolute())print(Path().resolve('.'))print(Path().cwd())

cwd()方法

返回一个表示当前目录的新路径对象(由os.getcwd()返回)

解决方法

将路径设为绝对路径,解析任何符号链接。返回一个新的路径对象:

如果您想从其任何模块检索包的根路径,可以使用以下方法(在Python 3.6上测试):

from . import __path__ as ROOT_PATHprint(ROOT_PATH)

__init__.py路径也可以通过使用__file__来引用。

希望这有帮助!

如果您使用pip安装它,“pip show”效果很好(“位置”)

$pip show Detectron2

Name: detectron2Version: 0.1Summary: Detectron2 is FAIR next-generation research platform for object detection and segmentation.Home-page: https://github.com/facebookresearch/detectron2Author: FAIRAuthor-email: NoneLicense: UNKNOWNLocation: /home/ubuntu/anaconda3/envs/pytorch_p36/lib/python3.6/site-packagesRequires: yacs, tabulate, tqdm, pydot, tensorboard, Pillow, termcolor, future, cloudpickle, matplotlib, fvcore

更新时间:

$python-m pip显示我的模块

(作者: wisbucky)

这是一个快速的bash脚本,以防它对任何人都有用。我只想能够设置一个环境变量,以便我可以pushd到代码。

#!/bin/bashmodule=${1:?"I need a module name"}
python << EOIimport $moduleimport osprint os.path.dirname($module.__file__)EOI

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 ~]#

当您导入模块时,您可以访问大量信息。查看dir(a_module)。至于路径,有一个dunder:a_module.__path__。您也可以只打印模块本身。

>>> import a_module>>> print(dir(a_module))['__builtins__', '__cached__', '__doc__', '__file__', '__loader__', '__name__', '__package__', '__path__', '__spec__']>>> print(a_module.__path__)['/.../.../a_module']>>> print(a_module)<module 'a_module' from '/.../.../a_module/__init__.py'>

如果您使用了pip,那么您可以调用pip show,但您必须使用您正在使用的python的特定版本调用它。例如,这些都可能给出不同的结果:

$ python -m pip show numpy$ python2.7 -m pip show numpy$ python3 -m pip show numpy
Location: /System/Library/Frameworks/Python.framework/Versions/2.7/Extras/lib/python

不要简单地运行$ pip show numpy,因为不能保证不同的python版本调用的是相同的pip

如果要检索模块路径不加载它:

import importlib.util
print(importlib.util.find_spec("requests").origin)

输出示例:

/usr/lib64/python3.9/site-packages/requests/__init__.py

如果您的导入是一个站点包(例如pandas),我建议使用它来获取其目录(如果导入是一个模块,则不起作用,例如pathlib):

from importlib import resources  # part of core Pythonimport pandas as pd
package_dir = resources.path(package=pd, resource="").__enter__()

一般来说,当任务是关于访问站点包的路径/资源时,可以考虑importlib.resources