关于python:如何检索模块module的路径?

How to retrieve a module's path?

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

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


1
2
import a_module
print(a_module.__file__)

将实际为您提供加载的.pyc文件的路径,至少在Mac OS X上是如此。因此,我想您可以这样做:

1
2
import os
path = os.path.dirname(a_module.__file__)

您也可以尝试:

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

获取模块的目录。


python中有inspect模块。

官方文件

The inspect module provides several useful functions to help get
information about live objects such as modules, classes, methods,
functions, tracebacks, frame objects, and code objects. For example,
it can help you examine the contents of a class, retrieve the source
code of a method, extract and format the argument list for a function,
or get all the information you need to display a detailed traceback.

例子:

1
2
3
4
5
6
7
8
>>> 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'


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

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

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

1
2
3
4
#/path2/bar.py
import os
print(os.getcwd())
print(__file__)

运行foo.py将提供以下输出:

1
2
3
/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,您将获得:

1
2
3
4
5
/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

希望这有帮助。在测试其他解决方案时,这个警告花费了我大量的时间和混乱。


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

  • 查找被调用脚本的路径
  • 正在查找当前正在执行的脚本的路径
  • 查找被调用脚本的目录
  • (其中一些问题已被问过,但已作为重复问题关闭,并在此处重新定向。)

    使用__file__注意事项

    对于已导入的模块:

    1
    2
    import something
    something.__file__

    将返回模块的绝对路径。但是,考虑到下面的脚本foo.py:

    1
    2
    #foo.py
    print '__file__', __file__

    用"python foo.py"调用它只返回"foo.py"。如果添加shebang:

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

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

    1
    python bar/foo.py

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

    1
    bar/foo.py

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

    正在查找目录

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

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

    意志产出:

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

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

    1
    2
    3
    4
    # 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__))

    输出如下:

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

    请注意,abspath()不解析符号链接。如果要执行此操作,请改用realpath()。例如,制作一个symlink文件"导入测试"链接指向文件"导入测试"py,内容如下:

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

    执行将打印绝对路径,如下所示:

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

    文件导入测试链接->文件导入测试.py

    使用检查

    @SummerBreeze提到使用检查模块。

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

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

    顺服地返回绝对路径。但是,为了找到当前正在执行的脚本的路径,我没有找到使用它的方法。


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

    1
    2
    import imp
    imp.find_module("os")

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

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

    与"inspect"方法相比,此方法的优点是不需要导入模块来使其正常工作,并且可以在输入中使用字符串。例如,检查在另一个脚本中调用的模块时很有用。

    编辑:

    在python3中,importlib模块应执行以下操作:

    importlib.util.find_spec号文件:

    Return the spec for the specified module.

    First, sys.modules is checked to see if the module was already imported. If so, then sys.modules[name].spec is returned. If that happens to be
    set to None, then ValueError is raised. If the module is not in
    sys.modules, then sys.meta_path is searched for a suitable spec with the
    value of 'path' given to the finders. None is returned if no spec could
    be found.

    If the name is for submodule (contains a dot), the parent module is
    automatically imported.

    The name and package arguments work the same as importlib.import_module().
    In other words, relative module names (with leading dots) work.


    这是微不足道的。

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

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

    1
    os.path.dirname(__file__)


    1
    2
    3
    import os
    path = os.path.abspath(__file__)
    dir_path = os.path.dirname(path)


    命令行实用程序

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

    1
    python-which <package name>

    enter image description here

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

    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    #!/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__)

    使其可执行

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

    1
    2
    import module
    print module.__path__

    Packages support one more special attribute, __path__. This is
    initialized to be a list containing the name of the directory holding
    the package’s __init__.py before the code in that file is executed.
    This variable can be modified; doing so affects future searches for
    modules and subpackages contained in the package.

    While this feature is not often needed, it can be used to extend the
    set of modules found in a package.

    来源


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

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

    1
    2
    3
    4
    5
    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__))

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

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


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

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

    结果:

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

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

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


    我想为一个常见的场景(在Python3中)做出贡献,并探索一些方法。

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

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

    1
    2
    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 list的第一个元素是根脚本(运行的脚本)的路径,而不管您是在根脚本本身还是在其任何模块中调用该列表。在某些情况下,它可能很有用。_uuu file_uuu变量包含调用模块的路径。

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

    1
    2
    3
    4
    5
    filedir = os.path.dirname(os.path.realpath(__file__))
    filepath = os.path.join(filedir, 'example.txt')

    with open(filepath, 'w'):
        pass

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

    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    11
    12
    13
    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

    因此在上面,我必须从我的_lib_a.py模块调用maincli.py,因为知道top_包和maincli.py在同一目录中。以下是我获取maincli.py路径的方法:

    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    11
    12
    13
    14
    15
    16
    17
    18
    19
    20
    21
    22
    23
    24
    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发布的基础上,我修改了代码。


    如果希望从脚本中了解绝对路径,可以使用路径对象:

    1
    2
    3
    4
    5
    from pathlib import Path

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

    CWD()方法

    Return a new path object representing the current directory (as returned by os.getcwd())

    resolve()方法

    Make the path absolute, resolving any symlinks. A new path object is returned:


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

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

    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    11
    12
    13
    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+"
    "
    )
    f.write("modpath ="+modname+"
    "
    )
    f.close()
    #Call the file with execfile()
    execfile('modname.py')
    print modpath
    <module 'os' from 'C:\Python27\lib\os.pyc'>

    我试图摆脱"全球性"问题,但发现了一些不起作用的案例我认为"execfile()"可以在python 3中模拟因为它在程序中,所以可以很容易地放入方法或模块中以便重用。


    如果您想从包的任何模块中检索包的根路径,那么以下方法可以工作(在Python3.6上测试):

    1
    2
    from . import __path__ as ROOT_PATH
    print(ROOT_PATH)

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

    希望这有帮助!