【问题标题】:How to refer to relative paths of resources when working with a code repository使用代码存储库时如何引用资源的相对路径
【发布时间】:2010-11-19 05:57:38
【问题描述】:

我们正在使用部署到 Windows 和 Linux 的代码存储库 - 有时位于不同的目录中。项目内部的模块之一应该如何引用项目中的非 Python 资源之一(CSV 文件等)?

如果我们这样做:

thefile = open('test.csv')

或:

thefile = open('../somedirectory/test.csv')

仅当脚本从一个特定目录或目录的子集运行时才会起作用。

我想做的是这样的:

path = getBasePathOfProject() + '/somedirectory/test.csv'
thefile = open(path)

有可能吗?

【问题讨论】:

    标签: python path relative-path


    【解决方案1】:

    尝试使用相对于当前文件路径的文件名。 './my_file' 的示例:

    fn = os.path.join(os.path.dirname(__file__), 'my_file')
    

    在 Python 3.4+ 中你也可以使用pathlib:

    fn = pathlib.Path(__file__).parent / 'my_file'
    

    【讨论】:

    • 我认为这个解决方案只有在资源位于 python 文件的同一目录或它的子目录中时才有效。当你有以下树结构时,你如何解决它: /Project_Root_dir /python_files_dir /Some more subdirs here py_file.py /resources /some subdirs here resource_file.csv
    • 抱歉,文件树在最后一条消息上出现乱码...第二次尝试:您的文件位于 /Project_Root_dir/python_files_dir/some_subdirs/py_file.py 并且您的资源文件位于 /Project_Root_dir/资源/some_subdirs/resource_file.csv
    • 您应该能够使用 join(foo, '..') 访问父目录。所以从 /root/python_files/module/myfile,使用 os.path.join(os.path.dirname(__file__), '..', '..', 'resources')
    • os.pardir 略好于'..',尽管两者在 POSIX 和 Windows 上是等效的。
    • @cedbeu:它在我遇到的每个系统上都是等效的,我认为每个系统都运行 python(如果我在这里错了,请纠正我)。但是,如果您希望将来将 python 移植到使用不同路径分隔符的系统,并希望您的代码能够为它做好准备,那么 os.pardir 将更便于移植。我会证明每个程序员,即使是从未读过任何 python 的程序员都知道“..”的含义,而“os.pardir”是一个间接级别,因此我个人必须在文档中查找d坚持“..”。
    【解决方案2】:

    如果您使用安装工具或分发(setup.py 安装),那么访问这些打包资源的“正确”方式似乎是使用 package_resources。

    你的例子是

    import pkg_resources
    my_data = pkg_resources.resource_string(__name__, "foo.dat")
    

    当然是读取资源,读取的二进制数据就是my_data的值

    如果你只需要文件名,你也可以使用

    resource_filename(package_or_requirement, resource_name)
    

    例子:

    resource_filename("MyPackage","foo.dat")
    

    优点是即使它是像鸡蛋一样的存档发行版,它也可以保证工作。

    http://packages.python.org/distribute/pkg_resources.html#resourcemanager-api

    【讨论】:

    • 我知道这是一个旧答案,我的首选方式是(/可能是?)使用 pkg_resources,但是随着压缩鸡蛋的消失,仅使用 __file__ 像过去的美好时光?
    • 这是一个可靠的方法。即使 egg 约定即将消失,setuptools 也不会,而且许多人仍在针对 git repos 安装 deps,其中 egg 是在运行时构建的
    • 在 Python 3.7+ 中,您应该更喜欢 importlib.resources。相同的东西,但标准库和更好的性能。
    【解决方案3】:

    在 Python 中,路径相对于当前工作目录,在大多数情况下,它是您运行程序的目录。 当前工作目录很可能与你的模块文件的目录不同,所以使用相对于你当前模块文件的路径总是一个不好的选择。

    使用绝对路径应该是最好的解决方案:

    import os
    package_dir = os.path.dirname(os.path.abspath(__file__))
    thefile = os.path.join(package_dir,'test.cvs')
    

    【讨论】:

      【解决方案4】:

      我经常使用类似这样的东西:

      import os
      DATA_DIR = os.path.abspath(os.path.join(os.path.dirname(__file__), 'datadir'))
      
      # if you have more paths to set, you might want to shorten this as
      here = lambda x: os.path.abspath(os.path.join(os.path.dirname(__file__), x))
      DATA_DIR = here('datadir') 
      
      pathjoin = os.path.join
      # ...
      # later in script
      for fn in os.listdir(DATA_DIR):
          f = open(pathjoin(DATA_DIR, fn))
          # ...
      

      变量

      __file__
      

      保存您编写该代码的脚本的文件名,因此您可以创建相对于脚本的路径,但仍使用绝对路径编写。它运作良好有几个原因:

      • 路径是绝对的,但仍然是相对的
      • 项目仍然可以部署在相关容器中

      但您需要注意平台兼容性 - Windows 的 os.pathsep 与 UNIX 不同。

      【讨论】:

        【解决方案5】:
        import os
        cwd = os.getcwd()
        path = os.path.join(cwd, "my_file")
        f = open(path)
        

        您还尝试使用os.path.abspath(os.getcwd()) 规范化您的cwd。更多信息here

        【讨论】:

        • 很少有cwd 是模块路径的用例
        • 它不能在包中工作,只能来自脚本设置的相同目录(或工作目录)。
        • 如果用户使用来自不同目录的绝对路径运行程序,这将不起作用。例如python3 /usr/someone/test.py
        【解决方案6】:

        您可以使用 __file__ 变量中的构建。它包含当前文件的路径。我会在项目根目录中的一个模块中实现 getBaseOfProject 。在那里我会得到__file__ 的路径部分并将其返回。然后可以在您的项目中的任何地方使用此方法。

        【讨论】:

          【解决方案7】:

          我有点被难住了。想把一些资源文件打包成一个wheel文件并访问它们。是否使用清单文件进行打包,但 pip install 不会安装它,除非它是子目录。希望这些截图能有所帮助

          ├── cnn_client
          │   ├── image_preprocessor.py
          │   ├── __init__.py
          │   ├── resources
          │   │   ├── mscoco_complete_label_map.pbtxt
          │   │   ├── retinanet_complete_label_map.pbtxt
          │   │   └── retinanet_label_map.py
          │   ├── tf_client.py
          

          MANIFEST.in

          recursive-include cnn_client/resources *
          

          使用标准 setup.py 创建了一个 weel。 pip 安装了 wheel 文件。 安装后检查是否安装了资源。他们是

          ls /usr/local/lib/python2.7/dist-packages/cnn_client/resources
          
          mscoco_complete_label_map.pbtxt
          retinanet_complete_label_map.pbtxt 
           retinanet_label_map.py  
          

          在 tfclient.py 中访问这些文件。 from

          templates_dir = os.path.join(os.path.dirname(__file__), 'resources')
           file_path = os.path.join(templates_dir, \
                      'mscoco_complete_label_map.pbtxt')
                  s = open(file_path, 'r').read()
          
          

          而且它有效。

          【讨论】:

            【解决方案8】:

            既然你说你有一些代码要部署到各个地方,那么你应该使用python生态系统来分发资源,而不仅仅是文件。它还支持访问 zip 存档中的文件,这很好,这样您就不必为此烦恼了。

            以前,这是由 pkg_resourcessetuptools 处理的,但随着越来越多的工具出现,生态系统已经发生了变化。从 python 3.7 开始,你应该使用importlib.resources

            import importlib.resources
            with importlib.resources.open_text('mypackage.somedirectory','text.csv') as f:
                print(f.read()) # or whatever
            

            但您还必须指示安装程序包含软件包资源。否则,pip install mypackage 不会捆绑数据文件。

            有很多方法可以做到这一点,但一种方法是添加

            [options.package_data]
            mypackage = 
                "somedirectory/*.csv"
            

            进入您的setup.cfg。使用setup.pypyproject.toml 时有等效的方法。 setuptools homepage 上提供更完整的帐户

            【讨论】:

              【解决方案9】:

              我花了很长时间想出这个问题的答案,但我终于得到了它(实际上很简单):

              import sys
              import os
              sys.path.append(os.getcwd() + '/your/subfolder/of/choice')
              
              # now import whatever other modules you want, both the standard ones,
              # as the ones supplied in your subfolders
              

              这会将您的子文件夹的相对路径附加到 python 查找的目录 它非常快速和肮脏,但它就像一个魅力:)

              【讨论】:

              • 这仅在您从与相关 .py 文件相同的目录中运行 Python 程序时才有效。在这种情况下,无论如何你都可以使用open('your/subfolder/of/choice')
              • 并且 OP 提到代码需要在 Windows 和 Linux 上运行。这不会。
              猜你喜欢
              • 1970-01-01
              • 1970-01-01
              • 1970-01-01
              • 2015-08-04
              • 1970-01-01
              • 2012-01-09
              • 1970-01-01
              • 2010-12-08
              • 2011-12-15
              相关资源
              最近更新 更多