【问题标题】:How to retrieve a module's path?如何检索模块的路径?
【发布时间】:2010-09-19 20:27:15
【问题描述】:

我想检测模块是否发生了变化。现在,使用 inotify 很简单,您只需要知道要从中获取通知的目录即可。

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

【问题讨论】:

标签: python module inotify


【解决方案1】:
import a_module
print(a_module.__file__)

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

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

你也可以试试:

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

获取模块的目录。

【讨论】:

  • 这回答了如何获取您导入的模块的路径,而不是您所在的模块/脚本的路径(对于您正在运行的脚本,__file__ 不是完整路径,它是相对的)。对于我所在的文件,我必须从同一目录导入另一个模块并按照此处所示进行操作。有谁知道更方便的方法吗?
  • @hbdgaf 很确定没有内置的self.__file__
  • @BenBryant @hbdgaf os.path.dirname(__file__) 对我来说很好,并返回模块目录的 abs 路径。
  • 我尝试这样做并获得回溯:AttributeError: 'module' object has no attribute '__file__'
  • @DorianDore 我有点搞砸了模块并找到了解决方案path = module.__path__.__dict__["_path"][0],但我不确定它是否可移植,或者它在 python 版本之间是否也没有区别。它对我有用,不像这个答案给我同样的错误和inspect 答案引发TypeError: <module 'module' (namespace)> is a built-in module...
【解决方案2】:

python中有inspect模块。

Official documentation

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'

【讨论】:

  • 您也可以使用inspect 来获取当前文件的名称;见stackoverflow.com/a/50905/320036
  • 我在谷歌上搜索了很多次这个问题,this 是我见过的最合理的答案!请更新inspect.currentframe()的信息
  • inspect.getfile() 方法不适用于模块 _io,但适用于模块 io
【解决方案3】:

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

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

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

#/path2/bar.py
import os
print(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 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 

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

【讨论】:

  • 在这种情况下,您可以使用 sys.argv[0] 代替 file
  • 这是特定于版本的警告吗?在 2.6 和 2.7 中,我成功地依赖于 file,它在 name__=='__main' 时工作文件。我见过的唯一失败案例是“python -c 'print file'”。我要补充一点,有时 file 可以是 '',当像 emacs 这样的 IDE 执行当前缓冲区时会发生这种情况。
  • 请注意,前导和尾随“__”字符将单词加粗,因此在阅读之前的 cmets 时请记住这一点:-P
  • @PaulDuBois 你可以用后面的抽动来包围它:`__file__` 变成 __file__
  • 如何获得NameError? @Paul Du Bois:我已经尝试过 Python 2.3-3.4 并且定义了 __file__ 但是我运行 Python 文件:python a.pypython -ma./a.py
【解决方案4】:

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

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

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

使用__file__ 的注意事项

对于您已导入的模块:

import something
something.__file__ 

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

#foo.py
print '__file__', __file__

用'python foo.py'调用它会简单地返回'foo.py'。如果添加 shebang:

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

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

python bar/foo.py

或添加shebang并直接执行文件:

bar/foo.py

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

查找目录

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

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

将输出:

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

换句话说,它返回一个空字符串,所以如果你想将它用于当前的文件(而不是文件文件一个导入的模块)。为了解决这个问题,您可以将其包装在对 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__))

输出类似:

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

注意 abspath() 不能解析符号链接。如果您想这样做,请改用 realpath()。例如,创建一个指向 file_import_testing.py 的符号链接 file_import_testing_link,其内容如下:

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

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

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

file_import_testing_link -> file_import_testing.py

使用检查

@SummerBreeze 提到使用inspect 模块。

对于导入的模块,这似乎效果很好,而且非常简洁:

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

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

inspect.getfile(inspect.currentframe())

(感谢@jbochi)

inspect.getabsfile(inspect.currentframe()) 

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

【讨论】:

  • inspect.getfile(os) 与代码中的 os.__file__ 相同: def getfile(object): """找出对象是在哪个源文件或编译文件中定义的。""" if ismodule(object): if hasattr(object, 'file'): return object.__file__
  • 这应该是公认的答案。感谢您提供详尽的回答。
  • 在此处添加inspect.getabsfile(inspect.currentframe()) 给出了当前执行脚本的绝对路径。
【解决方案5】:

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

import imp
imp.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].spec。如果碰巧是 设置为 None,然后引发 ValueError。如果模块不在 sys.modules,然后在 sys.meta_path 中搜索合适的规范 给发现者的“路径”值。如果没有规范,则返回 None 找到了。

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

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

【讨论】:

  • imp 在 python 2(当前版本 2.7.13)中不折旧。自 3.4 以来,imp 在 python 3 中已贬值。 importlib 应在 python 3 中使用。我喜欢这个解决方案,因为它甚至可以在实际导入失败时工作(例如,因为 32 位引擎的 64 位模块)
  • 在尝试查找 64 位 sqlite3 的路径但导入失败时非常好。完美。
  • importlib.machinery.PathFinder().find_module("os").get_filename() imp.find_module 的最短替代我在 Python3+ 中找到的。如果有人在寻找importlib.util.find_spec 的用法。
  • 此方法无需导入实际模块即可工作,这很好,因为我使用它来确定从共享计算机导入的模块版本。
  • DeprecationWarning:imp 模块已弃用,取而代之的是 importlib.. 和 "AttributeError: module 'importlib' has no attribute 'find_module"
【解决方案6】:

这很简单。

每个模块都有一个__file__ 变量,用于显示您当前所在位置的相对路径。

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

os.path.dirname(__file__)

【讨论】:

  • 几乎但不完全正确——file 不是“相对于你现在所处的位置”;当它是相对的(只有在 sys.path 中有相对路径时才会如此),它是相对于加载模块时您所在的位置
  • 从 Python 3.9 开始 __file__always an absolute path
【解决方案7】:
import os
path = os.path.abspath(__file__)
dir_path = os.path.dirname(path)

【讨论】:

  • 在我的 Linux python 2.6 上不起作用,因为__file__ 只是 dir/test.py,abspath 涉及 cwd 来完成路径名,这不是所需的结果,但是如果你导入一个模块然后m.__file__ 给出想要的结果。
【解决方案8】:
import module
print module.__path__

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

虽然这个功能并不经常需要,但它可以用来扩展 包中的一组模块。

Source

【讨论】:

    【解决方案9】:

    命令行实用程序

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

    python-which <package name>
    


    创建/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__)
    

    使其可执行

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

    【讨论】:

      【解决方案10】:

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

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

      【讨论】:

        【解决方案11】:

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

        最终我使用了这种方法,使用 sys.frozen 作为在 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__))
        

        【讨论】:

          【解决方案12】:

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

          from . import __path__ as ROOT_PATH
          print(ROOT_PATH)
          

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

          希望这会有所帮助!

          【讨论】:

          • ImportError: attempted relative import with no known parent package
          • 此代码旨在放入 python 包中。确保所有文件夹都有一个__init__.py 文件。谷歌搜索该错误会显示其他潜在原因
          【解决方案13】:

          如果您想知道脚本的绝对路径,可以使用Path object:

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

          cwd() method

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

          resolve() method

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

          【讨论】:

          • 这将失败,因为它只会返回工作目录,而不是模块路径
          【解决方案14】:

          当您导入一个模块时,您可以访问大量信息。查看dir(a_module)。至于路径,有一个笨蛋: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'>
          

          【讨论】:

            【解决方案15】:

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

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

            结果:

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

            诀窍在于or '.'dirname() 调用之后。它将目录设置为.,表示当前目录,是任何路径相关函数的有效目录。

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

            【讨论】:

            • 最好交换顺序并只使用os.path.dirname(os.path.abspath(__file__)),因为这样您根本不必担心相对路径。减少错误的机会。
            【解决方案16】:

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

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

            简单的说,如果你用下面的代码运行一个脚本文件,保证example.txt文件会创建在脚本文件所在的同一目录下: p>

            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
            

            【讨论】:

              【解决方案17】:

              在 python 包的模块中,我必须引用与 package.json 位于同一目录中的文件。例如。

              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
              

              所以在上面我必须从 my_lib_a.py 模块调用 maincli.py,因为我知道 top_package 和 maincli.py 在同一个目录中。这是我获取 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
              

              根据 PlasmaBinturong 的发布,我修改了代码。

              【讨论】:

                【解决方案18】:

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

                (我知道,它不适用于 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'>
                

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

                【讨论】:

                  【解决方案19】:

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

                  #!/bin/bash
                  module=${1:?"I need a module name"}
                  
                  python << EOI
                  import $module
                  import os
                  print os.path.dirname($module.__file__)
                  EOI
                  

                  外壳示例:

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

                  【讨论】:

                    【解决方案20】:

                    如果您使用了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 相同。

                    【讨论】:

                      【解决方案21】:

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

                      $ pip showdetectron2

                      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
                      

                      更新:

                      $ python -m pip 显示我的模块

                      (作者:wisbucky)

                      【讨论】:

                      【解决方案22】:

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

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

                      示例输出:

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

                      【讨论】:

                        猜你喜欢
                        • 2011-04-01
                        • 1970-01-01
                        • 1970-01-01
                        • 1970-01-01
                        • 1970-01-01
                        • 1970-01-01
                        • 1970-01-01
                        • 2011-05-09
                        相关资源
                        最近更新 更多