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

duc*_*cin 17 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
Run Code Online (Sandbox Code Playgroud)

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
Run Code Online (Sandbox Code Playgroud)

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

我很欣赏有关python模块开发的最佳实践的技巧.

Luk*_*raf 40

我的Python开发工作流程

这是开发Python包的基本过程,它包含了我认为是社区中的最佳实践.这是基本的 - 如果你真的认真地开发Python软件包,还有更多的东西,并且每个人都有自己的偏好,但它应该作为一个模板来开始,然后了解更多相关的部分.基本步骤是:

  • 使用virtualenv隔离
  • setuptools 用于创建可安装程序包和管理依赖项
  • python setup.py develop 在开发模式下安装该程序包

的virtualenv

首先,我建议使用virtualenv一个隔离的环境来开发你的软件包.在开发过程中,你需要安装,升级,降级和卸载软件包的依赖项,而你不需要

  • 您的开发依赖项污染您的系统范围 site-packages
  • 您的系统范围,site-packages以影响您的开发环境
  • 版本冲突

在整个系统范围内进行污染site-packages是很糟糕的,因为您安装的所有软件包都可以使用系统Python安装的所有Python应用程序,即使您只需要为您的小项目提供依赖项.它只是安装在一个新版本中,它覆盖了系统范围内的版本site-packages,并且与依赖它的$ {important_app}不兼容.你明白了.

让您的系统广泛site-packages影响您的开发环境是不好的,因为您的项目可能依赖于您在系统Python中已经获得的模块site-packages.因此,您忘记正确声明您的项目依赖于该模块,但一切正常,因为它始终存在于您的本地开发框中.在您发布包之前,人们会尝试安装它,或者将其推送到生产中等等......在干净的环境中开发会迫使您正确地声明您的依赖项.

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

要创建virtualenv,请virtualenv使用easy_install或通过将其安装到系统范围的Python来安装该软件包pip:

sudo pip install virtualenv
Run Code Online (Sandbox Code Playgroud)

请注意,这将是您以root用户身份(使用sudo)安装到全局站点包中的唯一时间.在此之后的一切都将发生在您即将创建的virtualenv中.

现在创建一个virtualenv来开发你的包:

cd ~/pyprojects
virtualenv --no-site-packages foobar-env
Run Code Online (Sandbox Code Playgroud)

这将创建一个目录树~/pyprojects/foobar-env,这是你的virtualenv.

要激活的virtualenv,cd到里面sourcebin/activate script:

~/pyprojects $ cd foobar-env/
~/pyprojects/foobar-env $ . bin/activate
(foobar-env) ~/pyprojects/foobar-env $
Run Code Online (Sandbox Code Playgroud)

注意前导点.,这是sourceshell命令的简写.还要注意提示符如何更改:(foobar-env)表示你在激活的virtualenv内部(并且总是需要隔离才能工作).因此,每次打开新的终端选项卡或SSH会话等时都要激活你的env.

如果你现在python在激活的env中运行,它实际上将~/pyprojects/foobar-env/bin/python用作解释器,具有自己的site-packages独立模块搜索路径.

一个setuptools包

现在创建你的包.基本上你需要一个setuptoolssetup.py来正确声明你的包的元数据和依赖项.您可以通过遵循setuptools文档自行完成此操作,或使用Paster模板创建包骨架.要使用Paster模板,请安装PasteScript到virtualenv中:

pip install PasteScript
Run Code Online (Sandbox Code Playgroud)

让我们为我们的新包创建一个源目录,以保持组织有序(也许你想要将你的项目分成几个包,或者稍后使用来自源的依赖项):

mkdir src
cd src/
Run Code Online (Sandbox Code Playgroud)

现在创建你的包,做

paster create -t basic_package foobar
Run Code Online (Sandbox Code Playgroud)

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

这将创建一个名为的包(或更确切地说,一个setuptools发行版)foobar.这就是这个名字

  • 人们将使用easy_install或安装您的包pip install foobar
  • 其他包将用于依赖您的名称 setup.py
  • 它将在PyPi上调用什么

在里面,你几乎总是创建一个Python包(就像在一个带有a的目录中__init__.py)被称为相同.这不是必需的,顶级Python包的名称可以是任何有效的包名,但它是一个常见的约定名称与分布相同.这就是为什么保持两者分开很重要,但并不总是容易的.因为顶级python包名是什么

  • 人们(或您)将使用import foobar或导入您的包裹from foobar import baz

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

cd foobar/foobar/
Run Code Online (Sandbox Code Playgroud)

现在创建你的代码:

vim models.py
Run Code Online (Sandbox Code Playgroud)

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))
Run Code Online (Sandbox Code Playgroud)

并且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
Run Code Online (Sandbox Code Playgroud)

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

  install_requires=[
      # -*- Extra requirements: -*-
      'setuptools',
      'requests',
  ],
Run Code Online (Sandbox Code Playgroud)

版本控制

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

cd src/foobar/
git init
vim .gitignore
Run Code Online (Sandbox Code Playgroud)

.gitignore

*.egg-info
*.py[co]
Run Code Online (Sandbox Code Playgroud)
git add .
git commit -m 'Create initial package structure.
Run Code Online (Sandbox Code Playgroud)

将包装作为开发蛋安装

现在是时候在开发模式下安装包了:

python setup.py develop
Run Code Online (Sandbox Code Playgroud)

这将安装requests依赖项和您的包作为开发egg.因此它与您的virtualenv的网站包相关联,但仍然存在于src/foobar您可以进行更改的位置,并让它们立即在virtualenv中活动而无需重新安装您的包.

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

您现在可以通过python client.py激活的virtualenv中的任何位置执行代码来运行代码.python src/foobar/foobar/client.py工作得很好,你的包正确安装,你的工作目录不再重要.

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

setuptools console_scripts入口点

setup.py

  entry_points='''
  # -*- Entry points: -*-    
  [console_scripts]
  run-fooobar = foobar.main:run_foobar
  ''',
Run Code Online (Sandbox Code Playgroud)

client.py

def run_client():
    # ...
Run Code Online (Sandbox Code Playgroud)

main.py

from foobar.client import run_client

def run_foobar():
    run_client()
Run Code Online (Sandbox Code Playgroud)

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

python setup.py develop
Run Code Online (Sandbox Code Playgroud)

你去吧bin/run-foo.

一旦你(或其他人)真正安装你的软件包,在virtualenv之外,入口点就会出现在/usr/local/bin/run-foosimiar或其中的某个地方,它将自动进入$PATH.

进一步的步骤

建议阅读:

  • 我喜欢这篇文章...没想到收到这样的答案:)谢谢 (6认同)
  • 我尽量不发表这样的评论,但是......这非常有帮助.谢谢. (2认同)
  • 当你在项目目录中时,你也可以使用`pip install -e .`代替`python setup.py develop`. (2认同)