python - python模块导入-相对路径问题

标签 python module

我正在python 2.7中开发自己的模块。它驻留在~/Development/.../myModule中,而不是/usr/lib/python2.7/dist-packages/usr/lib/python2.7/site-packages中。内部结构为:

/project-root-dir
  /server
    __init__.py
    service.py
    http.py
  /client
    __init__.py
    client.py
client/client.py包括PyCachedClient类。我遇到导入问题:
project-root-dir$ python
Python 2.7.2+ (default, Jul 20 2012, 22:12:53) 
[GCC 4.6.1] on linux2
Type "help", "copyright", "credits" or "license" for more information.
>>> from server import http
Traceback (most recent call last):
  File "<stdin>", line 1, in <module>
  File "server/http.py", line 9, in <module>
    from client import PyCachedClient
ImportError: cannot import name PyCachedClient

我没有将PythonPath设置为包括我的project-root-dir,因此,当server.http尝试包括client.PyCachedClient时,它将尝试从相对路径加载它并失败。我的问题是-我应该如何以良好的pythonic方式设置所有路径/设置?我知道每次打开控制台并尝试运行服务器时都可以在shell中运行export PYTHONPATH=...,但是我猜这不是最好的方法。如果我的模块是通过PyPi安装的(或类似的东西),我会将其安装在/usr/lib/python...路径中,并将自动加载。

我将感谢有关python模块开发中最佳实践的技巧。

最佳答案

我的Python开发工作流程

这是开发Python软件包的基本过程,其中包含了我认为是社区中的最佳实践。这是基本的-如果您真的很认真地开发Python软件包,那么它还有更多的东西,每个人都有自己的喜好,但是它应该作为入门的模板,然后进一步了解其中涉及的部分。基本步骤是:

  • 使用 virtualenv 隔离
  • setuptools 用于创建可安装的软件包并管理依赖项
  • python setup.py develop以开发模式
  • 安装该软件包

    虚拟环境

    首先,我建议使用 virtualenv 获得一个隔离的环境来开发您的程序包。在开发过程中,您将需要安装,升级,降级和卸载程序包的依赖项,而您不想
  • 您的开发依赖项会污染系统范围内的site-packages
  • 您的系统级site-packages会影响您的开发环境
  • 版本冲突

  • 在整个系统范围内污染site-packages是不好的,因为即使您的小型项目只需要依赖项,安装的所有软件包仍将对使用系统Python的所有Python应用程序可用。而且它只是安装在新版本中,该版本覆盖了系统范围site-packages中的版本,并且与依赖于该版本的$ {important_app}不兼容。你明白了。

    让系统范围的site-packages影响您的开发环境是很糟糕的,因为也许您的项目取决于系统Python的site-packages中已经拥有的模块。因此,您忘记正确地声明您的项目依赖于该模块,但是一切正常,因为它始终在本地开发框中。在发布软件包之前,人们会尝试安装它或将其投入生产等。在干净的环境中进行开发会迫使您正确声明依赖项。

    因此,虚拟环境是一个隔离的环境,具有自己的Python解释器和模块搜索路径。它基于您先前安装但与之隔离的Python安装。

    要创建一个virtualenv,请使用virtualenveasy_installpip软件包安装到系统范围的Python中,以进行安装:

    sudo pip install virtualenv
    

    请注意,这将是您唯一一次以root用户身份(使用sudo)在全局站点程序包中安装某些程序。此后的所有操作都将在您要创建的virtualenv内部进行。

    现在创建一个virtualenv来开发您的软件包:

    cd ~/pyprojects
    virtualenv --no-site-packages foobar-env
    

    这将创建一个目录树~/pyprojects/foobar-env,它是您的virtualenv。

    要激活virtualenv,请在其中添加cd并在source中添加bin/activate script:

    ~/pyprojects $ cd foobar-env/
    ~/pyprojects/foobar-env $ . bin/activate
    (foobar-env) ~/pyprojects/foobar-env $
    

    注意前导点.,这是source shell命令的简写。还要注意提示的变化:(foobar-env)表示您在激活的virtualenv内部(并且始终需要隔离才能起作用)。因此,每次您打开新的终端标签或SSH session 等时,请激活您的环境。

    如果现在在已激活的环境中运行python,它将实际上使用~/pyprojects/foobar-env/bin/python作为解释器,并具有自己的site-packages和隔离的模块搜索路径。

    setuptools软件包

    现在创建您的包。基本上,您需要一个带有setuptoolssetup.py软件包,以正确声明您的软件包的元数据和依赖项。您可以按照setuptools documentation自己进行操作,也可以使用Paster templates创建软件包框架。要使用Paster模板,请将PasteScript安装到您的virtualenv中:

    pip install PasteScript
    

    让我们为新软件包创建一个源目录,以使事情井井有条(也许您想将您的项目分成几个软件包,或者稍后使用源代码中的依赖项):

    mkdir src
    cd src/
    

    现在创建您的包,执行

    paster create -t basic_package foobar
    

    并在交互式界面中回答所有问题。大多数是可选的,只需按ENTER即可保留默认值。

    这将创建一个名为foobar的包(或更准确地说,是setuptools发行版)。这是那个名字
  • 人们将使用easy_installpip install foobar
  • 来安装您的软件包
  • 其他软件包将使用的名称取决于setup.py
  • PyPi上将被称为

  • 在内部,您几乎总是创建一个称为的Python包(例如在带有__init__.py的目录中)。这不是必需的,顶级Python包的名称可以是任何有效的包名称,但这是命名的通用约定。它与发行版相同,这就是为什么将两者分开很重要但并非总是那么容易的原因,因为顶级python软件包名称是什么
  • 人们(或您)将使用import foobarfrom foobar import baz导入您的软件包

  • 因此,如果您使用粘贴模板,它将已经为您创建了该目录:

    cd foobar/foobar/
    

    现在创建您的代码:

    vim models.py
    

    models.py

    class Page(object):
        """A dumb object wrapping a webpage.
        """
    
        def __init__(self, content, url):
            self.content = content
            self.original_url = url
    
        def __repr__(self):
            return "<Page retrieved from '%s' (%s bytes)>" % (self.original_url, len(self.content))
    

    在使用client.py的同一目录中还有一个models.py:

    client.py

    import requests
    from foobar.models import Page
    
    url = 'http://www.stackoverflow.com'
    
    response = requests.get(url)
    page = Page(response.content, url)
    
    print page
    

    requests中声明对setup.py模块的依赖:

      install_requires=[
          # -*- Extra requirements: -*-
          'setuptools',
          'requests',
      ],
    

    版本控制
    src/foobar/是您现在要置于版本控制下的目录:

    cd src/foobar/
    git init
    vim .gitignore
    

    .gitignore
    *.egg-info
    *.py[co]
    

    git add .
    git commit -m 'Create initial package structure.
    

    将软件包作为开发工具安装

    现在是时候以开发模式安装软件包了:

    python setup.py develop
    

    这将安装requests依赖项和您的软件包作为开发工具。因此,它已链接到您的virtualenv的站点程序包中,但仍位于src/foobar中,您可以在其中进行更改,并使它们立即在virtualenv中处于 Activity 状态,而无需重新安装程序包。

    现在,对于您的原始问题,使用相对路径导入:我的建议是,不要这样做。现在您已经有了一个正确的setuptools程序包,该程序包已安装并且可以导入,您当前的工作目录不再重要。只需执行from foobar.models import Page或类似方法,即可声明该对象所在位置的完全限定名称。对于您自己和其他阅读您代码的人来说,这使您的源代码更具可读性和可发现性。

    现在,您可以通过在激活的virtualenv内部的任何位置执行python client.py来运行代码。 python src/foobar/foobar/client.py可以正常工作,您的软件包已正确安装,并且您的工作目录不再重要。

    如果您想更进一步,甚至可以为CLI脚本创建setuptools入口点。这将在您的virtualenv中创建一个bin/something脚本,您可以从shell中运行它。

    setuptools console_scripts入口点

    setup.py

      entry_points='''
      # -*- Entry points: -*-    
      [console_scripts]
      run-fooobar = foobar.main:run_foobar
      ''',
    

    client.py

    def run_client():
        # ...
    

    main.py

    from foobar.client import run_client
    
    def run_foobar():
        run_client()
    

    重新安装软件包以激活入口点:

    python setup.py develop
    

    然后去了bin/run-foo

    一旦您(或其他人)在virtualenv之外真正安装了软件包,入口点将在/usr/local/bin/run-foo或类似的地方,而该位置将自动在$PATH

    进一步的步骤
  • 创建软件包的发行版并将其上传到PyPi,例如,使用 zest.releaser
  • 保留变更日志并对软件包
  • 进行版本控制
  • 了解declaring dependencies
  • 了解Differences between distribute, distutils, setuptools and distutils2

  • 建议阅读:
  • The Hitchhiker’s Guide to Packaging
  • The pip cookbook
  • 关于python - python模块导入-相对路径问题,我们在Stack Overflow上找到一个类似的问题: https://stackoverflow.com/questions/19876993/

    相关文章:

    python - 优化比较次数

    python - 从另一个 pip 安装的包针对本地代码运行 py.test 测试

    module - 在 OCaml 中从 Map 扩展模块

    python - 搜索文本中的单词,无论词形变化如何 : Python

    python - 如何在 Python 中创建 tmp 文件?

    python - 导入前安装模块

    angular - 找不到模块'node-sass

    c# - 单个包上的 DNN 多个模块

    python - python 练习中有些相当棘手的事情

    python - 检查错误