【问题标题】:Python - Get path of root project structurePython - 获取根项目结构的路径
【发布时间】:2014-10-12 21:19:52
【问题描述】:

我有一个 python 项目,项目根目录中有一个配置文件。 在整个项目中,需要在几个不同的文件中访问配置文件。

所以它看起来像:<ROOT>/configuration.conf <ROOT>/A/a.py<ROOT>/A/B/b.py(当 b,a.py 访问配置文件时)。

在不依赖于我所在项目中的哪个文件的情况下,获取项目根目录和配置文件的路径的最佳/最简单方法是什么?即不使用../../?可以假设我们知道项目根的名称。

【问题讨论】:

  • <ROOT>/__init__.py 存在吗?
  • 要么你的配置文件是一个python模块,你可以通过import语句轻松访问它,要么它不是一个python模块,你应该把它放在一个众所周知的位置。例如 $HOME/.my_project/my_project.conf。
  • @JohnSmithOptional - 这是一个 JSON 文件。我需要能够使用路径访问它。是的。所有文件夹都包含它。
  • _ 可以假设我们知道项目根目录的名称。_ 这是否意味着您知道项目的路径?那不就是 os.path.join(known_root_name, "configuration.conf") 吗?
  • 如果是用户配置,我通常会使用os.path.expanduser('~/.myproject/myproject.conf') 之类的配置。它适用于 Unix 和 Windows。

标签: python


【解决方案1】:

简单而动态!

此解决方案适用于任何操作系统和任何级别的目录:

假设你的项目文件夹名称是my_project

from pathlib import Path

current_dir = Path(__file__)
project_dir = [p for p in current_dir.parents if p.parts[-1]=='my_project'][0]

【讨论】:

  • 列表理解的替代方案是生成器,并使用Path.name:project_dir = next(p for p in current_dir.parents if p.name == "my_project")
【解决方案2】:

这不完全是这个问题的答案;但它可能会帮助某人。事实上,如果您知道文件夹的名称,就可以这样做。

import os
import sys

TMP_DEL = '×'
PTH_DEL = '\\'


def cleanPath(pth):
    pth = pth.replace('/', TMP_DEL)
    pth = pth.replace('\\', TMP_DEL)
    return pth


def listPath():
    return sys.path


def getPath(__file__):
    return os.path.abspath(os.path.dirname(__file__))


def getRootByName(__file__, dirName):
    return getSpecificParentDir(__file__, dirName)


def getSpecificParentDir(__file__, dirName):
    pth = cleanPath(getPath(__file__))
    dirName = cleanPath(dirName)
    candidate = f'{TMP_DEL}{dirName}{TMP_DEL}'
    if candidate in pth:
        pth = (pth.split(candidate)[0]+TMP_DEL +
               dirName).replace(TMP_DEL*2, TMP_DEL)
        return pth.replace(TMP_DEL, PTH_DEL)
    return None


def getSpecificChildDir(__file__, dirName):
    for x in [x[0] for x in os.walk(getPath(__file__))]:
        dirName = cleanPath(dirName)
        x = cleanPath(x)
        if TMP_DEL in x:
            if x.split(TMP_DEL)[-1] == dirName:
                return x.replace(TMP_DEL, PTH_DEL)
    return None

列出可用文件夹:

print(listPath())

用法:

#Directories
#ProjectRootFolder/.../CurrentFolder/.../SubFolder


print(getPath(__file__))
# c:\ProjectRootFolder\...\CurrentFolder

print(getRootByName(__file__, 'ProjectRootFolder'))
# c:\ProjectRootFolder

print(getSpecificParentDir(__file__, 'ProjectRootFolder'))
# c:\ProjectRootFolder

print(getSpecificParentDir(__file__, 'CurrentFolder'))
# None

print(getSpecificChildDir(__file__, 'SubFolder'))
# c:\ProjectRootFolder\...\CurrentFolder\...\SubFolder

【讨论】:

    【解决方案3】:

    我最终需要在各种不同的情况下执行此操作,其中不同的答案可以正常工作,其他人不能正常工作,或者经过各种修改,所以我制作了这个包以适用于大多数情况

    pip install get-project-root
    
        from get_project_root import root_path
        
        project_root = root_path(ignore_cwd=False)
        # >> "C:/Users/person/source/some_project/"
    

    https://pypi.org/project/get-project-root/

    【讨论】:

      【解决方案4】:

      这是一个解决这个问题的包:from-root

      pip install from-root

      from from_root import from_root, from_here
      
      # path to config file at the root of your project
      # (no matter from what file of the project the function is called!)
      config_path = from_root('config.json')
      
      # path to the data.csv file at the same directory where the callee script is located
      # (has nothing to do with the current working directory)
      data_path = from_here('data.csv')
      

      查看上面的链接并阅读自述文件以查看更多用例

      【讨论】:

        【解决方案5】:

        这是我对这个问题的看法。

        我有一个简单的用例困扰了我一段时间。尝试了一些解决方案,但我不喜欢其中任何一个都足够灵活。

        这就是我想出来的。

        • 在根目录中创建一个空白 python 文件 -> 我称之为beacon.py
          (假设项目根目录在 PYTHONPATH 中,所以可以导入)
        • 在我在这里调用的模块/类中添加几行 not_in_root.py
          这将导入beacon.py 模块并获取该模块的路径 模块

        这是一个示例项目结构

        this_project
        ├── beacon.py
        ├── lv1
        │   ├── __init__.py
        │   └── lv2
        │       ├── __init__.py
        │       └── not_in_root.py
        ...
        
        

        not_in_root.py的内容

        import os
        from pathlib import Path
        
        
        class Config:
            try:
                import beacon
                print(f"'import beacon' -> {os.path.dirname(os.path.abspath(beacon.__file__))}")  # only for demo purposes
                print(f"'import beacon' -> {Path(beacon.__file__).parent.resolve()}")  # only for demo purposes
            except ModuleNotFoundError as e:
                print(f"ModuleNotFoundError: import beacon failed with {e}. "
                      f"Please. create a file called beacon.py and place it to the project root directory.")
        
            project_root = Path(beacon.__file__).parent.resolve()
            input_dir = project_root / 'input'
            output_dir = project_root / 'output'
        
        
        if __name__ == '__main__':
            c = Config()
            print(f"Config.project_root: {c.project_root}")
            print(f"Config.input_dir: {c.input_dir}")
            print(f"Config.output_dir: {c.output_dir}")
        

        输出将是

        /home/xyz/projects/this_project/venv/bin/python /home/xyz/projects/this_project/lv1/lv2/not_in_root.py
        'import beacon' -> /home/xyz/projects/this_project
        'import beacon' -> /home/xyz/projects/this_project
        Config.project_root: /home/xyz/projects/this_project
        Config.input_dir: /home/xyz/projects/this_project/input
        Config.output_dir: /home/xyz/projects/this_project/output
        

        当然,它不需要被称为beacon.py,也不需要为空,基本上任何python文件(可导入)文件都可以,只要它在根目录中。

        使用一个空的 .py 文件可以保证它不会因为未来的重构而被移到别处。

        干杯

        【讨论】:

          【解决方案6】:

          我自己决定如下。
          需要从主文件中获取“MyProject/drivers”的路径。

          MyProject/
          ├─── RootPackge/
          │    ├── __init__.py
          │    ├── main.py
          │    └── definitions.py
          │
          ├─── drivers/
          │    └── geckodriver.exe
          │
          ├── requirements.txt
          └── setup.py
          

          定义.py
          不是放在项目的根目录下,而是放在主包的根目录下

          from pathlib import Path
          
          ROOT_DIR = Path(__file__).parent.parent
          

          使用 ROOT_DIR:
          main.py

          # imports must be relative,
          # not from the root of the project,
          # but from the root of the main package.
          # Not this way:
          # from RootPackge.definitions import ROOT_DIR
          # But like this:
          from definitions import ROOT_DIR
          
          # Here we use ROOT_DIR
          # get path to MyProject/drivers
          drivers_dir = ROOT_DIR / 'drivers'
          # Thus, you can get the path to any directory
          # or file from the project root
          
          driver = webdriver.Firefox(drivers_dir)
          driver.get('http://www.google.com')
          

          那么 PYTHON_PATH 将不会用于访问 'definitions.py' 文件。

          在 PyCharm 中工作:
          运行文件“main.py”(在 Windows 中为 ctrl + shift + F10)

          从项目根目录在 CLI 中工作:

          $ py RootPackge/main.py
          

          在 RootPackge 的 CLI 中工作:

          $ cd RootPackge
          $ py main.py
          

          从项目上面的目录工作:

          $ cd ../../../../
          $ py MyWork/PythoProjects/MyProject/RootPackge/main.py
          

          如果您提供主文件的绝对路径,则可以在任何地方工作。
          不依赖于venv。

          【讨论】:

            【解决方案7】:

            其他答案建议在项目的顶层使用文件。如果您使用 pathlib.Pathparent(Python 3.4 及更高版本),则不需要这样做。考虑以下目录结构,其中除了README.mdutils.py 之外的所有文件都已被省略。

            project
            │   README.md
            |
            └───src
            │   │   utils.py
            |   |   ...
            |   ...
            

            utils.py 中我们定义了以下函数。

            from pathlib import Path
            
            def get_project_root() -> Path:
                return Path(__file__).parent.parent
            

            在项目中的任何模块中,我们现在可以按如下方式获取项目根目录。

            from src.utils import get_project_root
            
            root = get_project_root()
            

            优点:任何调用get_project_root 的模块都可以在不改变程序行为的情况下移动。只有当模块 utils.py 被移动时,我们才必须更新 get_project_root 和导入(可以使用重构工具来自动执行此操作)。

            【讨论】:

            • 根目录中的任何模块。从根目录外部调用 src.utils 应该不起作用。我错了吗?
            • name 'file' 未定义,为什么?
            • @LukAron:一定要使用__file__(注意下划线),这是一个包含模块绝对路径的模块属性,否则不起作用。
            • 对我来说,这仍然没有帮助,因为如果你不在 src 中,你仍然需要知道 src/utils.py 的位置
            • 在我的情况下(由于 Linux 操作系统?),Path() 返回 relative 路径。因此,这个例子我需要Path(__file__).absolute().parent.parent
            【解决方案8】:

            我必须实施自定义解决方案,因为它并不像您想象的那么简单。 我的解决方案是基于堆栈跟踪检查 (inspect.stack()) + sys.path 并且无论调用函数的 python 模块的位置还是解释器的位置都可以正常工作(我尝试在 PyCharm 中运行它,在诗歌中壳和其他......)。这是 cmets 的完整实现:​​

            def get_project_root_dir() -> str:
                """
                Returns the name of the project root directory.
            
                :return: Project root directory name
                """
            
                # stack trace history related to the call of this function
                frame_stack: [FrameInfo] = inspect.stack()
            
                # get info about the module that has invoked this function
                # (index=0 is always this very module, index=1 is fine as long this function is not called by some other
                # function in this module)
                frame_info: FrameInfo = frame_stack[1]
            
                # if there are multiple calls in the stacktrace of this very module, we have to skip those and take the first
                # one which comes from another module
                if frame_info.filename == __file__:
                    for frame in frame_stack:
                        if frame.filename != __file__:
                            frame_info = frame
                            break
            
                # path of the module that has invoked this function
                caller_path: str = frame_info.filename
            
                # absolute path of the of the module that has invoked this function
                caller_absolute_path: str = os.path.abspath(caller_path)
            
                # get the top most directory path which contains the invoker module
                paths: [str] = [p for p in sys.path if p in caller_absolute_path]
                paths.sort(key=lambda p: len(p))
                caller_root_path: str = paths[0]
            
                if not os.path.isabs(caller_path):
                    # file name of the invoker module (eg: "mymodule.py")
                    caller_module_name: str = Path(caller_path).name
            
                    # this piece represents a subpath in the project directory
                    # (eg. if the root folder is "myproject" and this function has ben called from myproject/foo/bar/mymodule.py
                    # this will be "foo/bar")
                    project_related_folders: str = caller_path.replace(os.sep + caller_module_name, '')
            
                    # fix root path by removing the undesired subpath
                    caller_root_path = caller_root_path.replace(project_related_folders, '')
            
                dir_name: str = Path(caller_root_path).name
            
                return dir_name
            

            【讨论】:

            • @dev,inspect 中的 inspect.stack() 是什么
            • @binrebin inspect - Python 标准库中的模块。
            • @daveoncode,我在某处发现了另一件事。谢谢回复
            【解决方案9】:

            Below Code 返回项目根目录之前的路径

            import sys
            print(sys.path[1])
            

            【讨论】:

            • 不错的提示!我想知道为什么除了我之外没有人赞成你的答案:P
            • 谢谢Daveon 真的很感激!!
            • 不幸的是不是这样,简单:P ...看看我的完整解决方案:stackoverflow.com/a/62510836/267719
            • 谢谢.. 从共享解决方案中,我能够通过索引获得不同的路径。现在我只需要选择我想要的路径/根路径。 ``` import sys print(sys.path[0]) print(sys.path[1]) print(sys.path[2]) print(sys.path[3]) ```
            【解决方案10】:

            只是一个例子:我想从 helper1.py 中运行 runio.py

            项目树示例:

            myproject_root
            - modules_dir/helpers_dir/helper1.py
            - tools_dir/runio.py
            

            获取项目根目录:

            import os
            rootdir = os.path.dirname(os.path.realpath(__file__)).rsplit(os.sep, 2)[0]
            

            脚本的构建路径:

            runme = os.path.join(rootdir, "tools_dir", "runio.py")
            execfile(runme)
            

            【讨论】:

              【解决方案11】:

              在撰写本文时,其他解决方案都不是非常独立的。它们取决于环境变量或模块在包结构中的位置。 “Django”解决方案的最佳答案是要求相对导入,后者成为后者的牺牲品。它还具有必须在顶层修改模块的缺点。

              这应该是找到顶级包的目录路径的正确方法:

              import sys
              import os
              
              root_name, _, _ = __name__.partition('.')
              root_module = sys.modules[root_name]
              root_dir = os.path.dirname(root_module.__file__)
              
              config_path = os.path.join(root_dir, 'configuration.conf')
              

              它的工作原理是获取__name__ 中包含的点分字符串中的第一个组件,并将其用作sys.modules 中的键,该键返回顶级包的模块对象。它的__file__ 属性包含使用os.path.dirname() 修剪掉/__init__.py 后我们想要的路径。

              此解决方案是独立的。它适用于包的任何模块中的任何位置,包括顶级__init__.py 文件中。

              【讨论】:

              • 您能否添加关于您的解决方案的简短说明以及他们如何将其用作解决方案?
              【解决方案12】:

              我使用 ../ 方法获取当前项目路径。

              示例: 项目 1 -- D:\projects

              src

              配置文件

              配置.cfg

              Path="../src/ConfigurationFiles/Configuration.cfg"

              【讨论】:

                【解决方案13】:

                这里有很多答案,但我找不到涵盖所有情况的简单答案,所以请允许我也提出我的解决方案:

                import pathlib
                import os
                
                def get_project_root():
                    """
                    There is no way in python to get project root. This function uses a trick.
                    We know that the function that is currently running is in the project.
                    We know that the root project path is in the list of PYTHONPATH
                    look for any path in PYTHONPATH list that is contained in this function's path
                    Lastly we filter and take the shortest path because we are looking for the root.
                    :return: path to project root
                    """
                    apth = str(pathlib.Path().absolute())
                    ppth = os.environ['PYTHONPATH'].split(':')
                    matches = [x for x in ppth if x in apth]
                    project_root = min(matches, key=len)
                    return project_root

                【讨论】:

                • 我认为我们不知道我们的项目位于 PYTHONPATH 路径中。我可以为我的桌面(Microsoft Windows)创建一个快捷方式图标,它有一个位于我的 PYTHONPATH 之外的“起始目录”,我可以打开一个控制台,cd 到包含 Python 模块的目录,然后输入:python。 exe my_root_module.py 不在我的 PYTHONPATH 环境变量中。
                【解决方案14】:

                如果你正在使用 anaconda-project,你可以从环境变量中查询 PROJECT_ROOT --> os.getenv('PROJECT_ROOT')。这仅在脚本通过 anaconda-project run 执行时才有效。

                如果您不希望您的脚本由 anaconda-project 运行,您可以查询您正在使用的 Python 解释器的可执行二进制文件的绝对路径,并将路径字符串提取到 envs 目录 exclusiv。例如:我的 conda env 的 python 解释器位于:

                /home/user/project_root/envs/default/bin/python

                # You can first retrieve the env variable PROJECT_DIR.
                # If not set, get the python interpreter location and strip off the string till envs inclusiv...
                
                if os.getenv('PROJECT_DIR'):
                    PROJECT_DIR = os.getenv('PROJECT_DIR')
                else:
                    PYTHON_PATH = sys.executable
                    path_rem = os.path.join('envs', 'default', 'bin', 'python')
                    PROJECT_DIR = py_path.split(path_rem)[0]
                

                这仅适用于具有 anaconda-project 的固定项目结构的 conda-project

                【讨论】:

                  【解决方案15】:

                  在找到这个解决方案之前,我也一直在努力解决这个问题。 这是我认为最干净的解决方案。

                  在您的 setup.py 中添加“packages”

                  setup(
                  name='package_name'
                  version='0.0.1'
                  .
                  .
                  .
                  packages=['package_name']
                  .
                  .
                  .
                  )
                  

                  在你的 python_script.py

                  import pkg_resources
                  import os
                  
                  resource_package = pkg_resources.get_distribution(
                      'package_name').location
                  config_path = os.path.join(resource_package,'configuration.conf')
                  

                  【讨论】:

                  • 使用虚拟环境并使用python3 setup.py install 安装包,它不再指向源代码文件夹,而是指向~./virtualenv/..../app.egg 中的鸡蛋。所以我不得不将配置文件包含在包安装中。
                  【解决方案16】:

                  对于我认为您需要的所有以前的解决方案似乎都过于复杂,而且通常对我不起作用。以下一行命令可以满足您的要求:

                  import os
                  ROOT_DIR = os.path.abspath(os.curdir)
                  

                  【讨论】:

                  • 把它放在 config.py 中,在目录的根目录下,.. bamn!你有一个单身人士。
                  • 此方法假定您从其存在的路径中运行应用程序。许多“用户”都有他们从桌面单击的图标,或者可以完全从另一个目录运行应用程序。
                  【解决方案17】:

                  试试:

                  ROOT_DIR = os.path.dirname(os.path.dirname(os.path.abspath(__file__)))
                  

                  【讨论】:

                  • 这正是我所需要的。简单的解决方案,对我有用,因为我的结构是 root->config->conf.py 我想在 conf.py 中定义项目根目录,而根目录正好是该文件的两个级别。
                  【解决方案18】:

                  我最近一直在尝试做类似的事情,但我发现这些答案不足以满足我的用例(需要检测项目根目录的分布式库)。主要是我一直在与不同的环境和平台作斗争,但仍然没有找到完全通用的东西。

                  项目本地代码

                  我在一些地方看到过这个例子,Django 等中提到和使用过。

                  import os
                  print(os.path.dirname(os.path.abspath(__file__)))
                  

                  这很简单,它仅在 sn-p 所在的文件实际上是项目的一部分时才有效。 我们不检索项目目录,而是检索sn-p的目录

                  同样,sys.modules 方法在从应用程序入口点外部调用 时失效,特别是我观察到子线程在没有关系返回的情况下无法确定这一点到 'ma​​in' 模块。我已经明确地将导入放在一个函数中,以演示从子线程导入,将其移动到 app.py 的顶级将修复它。

                  app/
                  |-- config
                  |   `-- __init__.py
                  |   `-- settings.py
                  `-- app.py
                  

                  app.py

                  #!/usr/bin/env python
                  import threading
                  
                  
                  def background_setup():
                      # Explicitly importing this from the context of the child thread
                      from config import settings
                      print(settings.ROOT_DIR)
                  
                  
                  # Spawn a thread to background preparation tasks
                  t = threading.Thread(target=background_setup)
                  t.start()
                  
                  # Do other things during initialization
                  
                  t.join()
                  
                  # Ready to take traffic
                  

                  settings.py

                  import os
                  import sys
                  
                  
                  ROOT_DIR = None
                  
                  
                  def setup():
                      global ROOT_DIR
                      ROOT_DIR = os.path.dirname(sys.modules['__main__'].__file__)
                      # Do something slow
                  

                  运行此程序会产生属性错误:

                  >>> import main
                  >>> Exception in thread Thread-1:
                  Traceback (most recent call last):
                    File "C:\Python2714\lib\threading.py", line 801, in __bootstrap_inner
                      self.run()
                    File "C:\Python2714\lib\threading.py", line 754, in run
                      self.__target(*self.__args, **self.__kwargs)
                    File "main.py", line 6, in background_setup
                      from config import settings
                    File "config\settings.py", line 34, in <module>
                      ROOT_DIR = get_root()
                    File "config\settings.py", line 31, in get_root
                      return os.path.dirname(sys.modules['__main__'].__file__)
                  AttributeError: 'module' object has no attribute '__file__'
                  

                  ...因此是基于线程的解决方案

                  位置无关

                  使用与以前相同的应用程序结构,但修改 settings.py

                  import os
                  import sys
                  import inspect
                  import platform
                  import threading
                  
                  
                  ROOT_DIR = None
                  
                  
                  def setup():
                      main_id = None
                      for t in threading.enumerate():
                          if t.name == 'MainThread':
                              main_id = t.ident
                              break
                  
                      if not main_id:
                          raise RuntimeError("Main thread exited before execution")
                  
                      current_main_frame = sys._current_frames()[main_id]
                      base_frame = inspect.getouterframes(current_main_frame)[-1]
                  
                      if platform.system() == 'Windows':
                          filename = base_frame.filename
                      else:
                          filename = base_frame[0].f_code.co_filename
                  
                      global ROOT_DIR
                      ROOT_DIR = os.path.dirname(os.path.abspath(filename))
                  

                  分解: 首先我们要准确的找到主线程的线程ID。在 Python3.4+ 中,线程库有threading.main_thread() 但是,每个人都不会使用 3.4+,所以我们搜索所有线程来寻找主线程,保存它的 ID。如果主线程已经退出,则不会在threading.enumerate() 中列出。在这种情况下,我们会提出RuntimeError(),直到我找到更好的解决方案。

                  main_id = None
                  for t in threading.enumerate():
                      if t.name == 'MainThread':
                          main_id = t.ident
                          break
                  
                  if not main_id:
                      raise RuntimeError("Main thread exited before execution")
                  

                  接下来我们找到主线程的第一个堆栈帧。 使用 cPython 特定函数 sys._current_frames() 我们得到每个线程当前堆栈帧的字典。然后利用inspect.getouterframes(),我们可以检索主线程和第一帧的整个堆栈。 current_main_frame = sys._current_frames()[main_id] base_frame = inspect.getouterframes(current_main_frame)[-1] 最后,需要处理inspect.getouterframes() 的Windows 和Linux 实现之间的差异。使用清理后的文件名,os.path.abspath()os.path.dirname() 清理。

                  if platform.system() == 'Windows':
                      filename = base_frame.filename
                  else:
                      filename = base_frame[0].f_code.co_filename
                  
                  global ROOT_DIR
                  ROOT_DIR = os.path.dirname(os.path.abspath(filename))
                  

                  到目前为止,我已经在 Windows 上的 Python2.7 和 3.6 以及 WSL 上的 Python3.4 上对此进行了测试

                  【讨论】:

                    【解决方案19】:

                    要获取“根”模块的路径,可以使用:

                    import os
                    import sys
                    os.path.dirname(sys.modules['__main__'].__file__)
                    

                    但更有趣的是,如果您在最顶层的模块中有一个配置“对象”,您可以像这样读取它:

                    app = sys.modules['__main__']
                    stuff = app.config.somefunc()
                    

                    【讨论】:

                    • 这里os 默认情况下不可用。需要导入os。所以添加import os这行会使答案更完整。
                    • 这给出了包含已执行脚本的目录。例如,当运行python3 -m topmodule.submodule.script 时,它会给出/path/to/topmodule/submodule 而不是/path/to/topmodule
                    【解决方案20】:

                    您可以按照 Django 的方式执行此操作:从项目顶层的文件中为项目根定义一个变量。例如,如果这是您的项目结构看起来像:

                    project/
                        configuration.conf
                        definitions.py
                        main.py
                        utils.py
                    

                    您可以在definitions.py 中定义(这需要import os):

                    ROOT_DIR = os.path.dirname(os.path.abspath(__file__)) # This is your Project Root
                    

                    因此,在知道 Project Root 的情况下,您可以创建一个指向配置位置的变量(这可以在任何地方定义,但一个合乎逻辑的位置会将它放在定义常量的位置 - 例如definitions.py):

                    CONFIG_PATH = os.path.join(ROOT_DIR, 'configuration.conf')  # requires `import os`
                    

                    然后,您可以使用 import 语句(例如在utils.py)轻松访问常量(在任何其他文件中):from definitions import CONFIG_PATH

                    【讨论】:

                    • 要像这样包含definitions.py文件,是否也需要将__init__.py文件添加到项目根目录中?那应该是正确的吗?我刚开始使用 python,不确定最佳实践。谢谢。
                    • @akskap:不,不需要__init__.py,因为仅在定义包时才需要该文件:需要__init__.py文件才能使Python将目录视为包含包裹;这样做是为了防止具有通用名称(例如字符串)的目录无意中隐藏模块搜索路径中稍后出现的有效模块。在最简单的情况下,__init__.py 可以只是一个空文件,但它也可以执行包的初始化代码或设置__all__ 变量,稍后将介绍。 参见:docs.python.org/3/tutorial/modules.html#packages
                    • @JavNoor: 否 - 在您引用的示例中,os.path.abspath 正在调用字符串 '__file__'。回想一下,__file__ 实际上是为 Python 模块定义的导入属性。在这种情况下,__file__ 将返回加载模块的路径名。在此处阅读更多信息(参见模块部分):docs.python.org/3/reference/datamodel.html
                    • @AstroFloyd:只有当 subdir 不是子包时它才会失败(即它在 subdir 中缺少 __init__.py)。如果没有包结构,Python 将不知道应该从哪里引用 ROOT_DIR
                    • @AstroFloyd:显示的示例适用于 definitions.py 位于顶层的项目根目录。您显示的示例是针对子目录中的不同情况的,因为调用该标题下的文件中提供的语句只会打印出子目录的根目录。如果您的项目结构像一个包并且子目录以类似的方式完成,那么您可以使用绝对或相对导入语句从顶层的definitions.py 调用 ROOT_DIR。有关更多信息,请参阅此链接:realpython.com/absolute-vs-relative-python-imports
                    【解决方案21】:

                    这对我使用标准 PyCharm 项目和项目根目录下的虚拟环境 (venv) 很有用。

                    下面的代码不是最漂亮的,但始终获得项目根目录。它从 VIRTUAL_ENV 环境变量返回到 venv 的完整目录路径,例如/Users/NAME/documents/PROJECT/venv

                    然后它在最后一个/ 处拆分路径,给出一个包含两个元素的数组。第一个元素将是项目路径,例如/Users/NAME/documents/PROJECT

                    import os
                    
                    print(os.path.split(os.environ['VIRTUAL_ENV'])[0])
                    

                    【讨论】:

                    • 这不适用于像 anaconda 或 pipenv 这样的设置,因为在这些情况下项目中不包含虚拟环境。
                    【解决方案22】:

                    实现此目的的标准方法是使用pkg_resources 模块,它是setuptools 包的一部分。 setuptools 用于创建可安装的 python 包。

                    您可以使用pkg_resources 将所需文件的内容作为字符串返回,您可以使用pkg_resources 获取系统上所需文件的实际路径。

                    假设您有一个名为 stackoverflow 的包。

                    stackoverflow/
                    |-- app
                    |   `-- __init__.py
                    `-- resources
                        |-- bands
                        |   |-- Dream\ Theater
                        |   |-- __init__.py
                        |   |-- King's\ X
                        |   |-- Megadeth
                        |   `-- Rush
                        `-- __init__.py
                    
                    3 directories, 7 files
                    

                    现在假设您想从模块app.run 访问文件Rush。使用pkg_resources.resouces_filename获取Rush的路径,pkg_resources.resource_string获取Rush的内容;因此:

                    import pkg_resources
                    
                    if __name__ == "__main__":
                        print pkg_resources.resource_filename('resources.bands', 'Rush')
                        print pkg_resources.resource_string('resources.bands', 'Rush')
                    

                    输出:

                    /home/sri/workspace/stackoverflow/resources/bands/Rush
                    Base: Geddy Lee
                    Vocals: Geddy Lee
                    Guitar: Alex Lifeson
                    Drums: Neil Peart
                    

                    这适用于你的 python 路径中的所有包。因此,如果您想知道lxml.etree 在您的系统中的位置:

                    import pkg_resources
                    
                    if __name__ == "__main__":
                        print pkg_resources.resource_filename('lxml', 'etree')
                    

                    输出:

                    /usr/lib64/python2.7/site-packages/lxml/etree
                    

                    关键是您可以使用此标准方法访问系统上安装的文件(例如 pip install xxx 或 yum -y install python-xxx)以及您当前正在处理的模块中的文件.

                    【讨论】:

                    • 我喜欢你的乐队选择!
                    猜你喜欢
                    • 2023-02-07
                    • 1970-01-01
                    • 1970-01-01
                    • 1970-01-01
                    • 2015-07-29
                    • 1970-01-01
                    • 1970-01-01
                    • 1970-01-01
                    • 2011-07-09
                    相关资源
                    最近更新 更多