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

时间:2013-11-09 14:17:21

标签: 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模块开发的最佳实践的提示。

2 个答案:

答案 0 :(得分:39)

我的Python开发工作流程

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

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

的virtualenv

首先,我建议使用virtualenv来创建一个隔离的环境来开发你的软件包。在开发过程中,你需要安装,升级,降级和卸载软件包的依赖关系。 #39; t想要

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

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

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

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

要创建virtualenv,请使用virtualenveasy_installpip软件包安装到系统范围的Python中来安装sudo pip install virtualenv

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

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

现在创建一个用于开发包的virtualenv:

~/pyprojects/foobar-env

这将创建一个目录树cd,这是你的virtualenv。

要激活virtualenv,sourcebin/activate script ~/pyprojects $ cd foobar-env/ ~/pyprojects/foobar-env $ . bin/activate (foobar-env) ~/pyprojects/foobar-env $

.

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

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

一个setuptools包

现在创建您的包。基本上,您需要一个setup.pyPasteScript来正确声明您的包的元数据和依赖项。您可以按照setuptools documentation自行执行此操作,也可以使用Paster templates创建包关键字。要使用Paster模板,请在您的virtualenv中安装pip install PasteScript

mkdir src
cd src/

让我们为新软件包创建一个源目录,以保持组织有序(可能您希望将项目拆分为多个软件包,或者稍后使用源代码中的依赖项):

paster create -t basic_package foobar

现在要创建包,请执行

foobar

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

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

的名称
  • 人们将使用pip install foobarsetup.py
  • 来安装您的软件包
  • 其他软件包将在__init__.py
  • 中使用的名称取决于您的名称
  • PyPi
  • 上的内容

在内部,你几乎总是创建一个Python包(如在&#34;一个带有import foobar的目录),它们被称为相同的。这不是必需的,顶级Python包的名称可以是任何有效的包名,但它的一般惯例是将其命名为与分发相同。这就是为什么保持两者分开是重要的,但并不总是容易的。因为顶级python包名是什么

  • 人们(或您)将使用from foobar import bazcd 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

这将安装src/foobar依赖项和您的包作为开发蛋。因此,它与您的virtualenv网站包相关联,但仍然存在于from foobar.models import Page,您可以在其中进行更改并让它们立即在virtualenv中处于活动状态,而无需重新安装您的软件包。

现在您的原始问题,使用相对路径导入:我的建议是,不要这样做。既然您已经安装了可以安装和导入的正确的setuptools软件包,那么您当前的工作目录就不再重要了。只需执行python client.py或类似操作,声明该对象所在的完全限定名称。这使您的源代码对于您自己和阅读代码的其他人来说更具可读性和可发现性。

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

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

setuptools console_scripts入口点

<强> 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

然后你去,/usr/local/bin/run-foo

一旦您(或其他人)在virtualenv之外真实地安装您的软件包,入口点将位于$PATH或simiar的某个位置,它将自动位于{{1}}。

进一步的步骤

建议阅读:

答案 1 :(得分:2)

所以,你有两个,第一个包含名为:

的模块
server         # server/__init__.py
server.service # server/service.py
server.http    # server/http.py

第二个模块名称:

client         # client/__init__.py
client.client  # client/client.py

如果您想假设这两个包都在导入路径(sys.path)中,并且您想要的类位于client/client.py,那么您必须在服务器中执行以下操作:

from client.client import PyCachedClient

您要求client中的符号,而不是client.client,并且根据您的描述,该符号不是定义该符号的位置。

我个人会考虑制作这个包(即将__init__.py放在文件夹中一层,并给它一个合适的python包名称),并client和{{1}是该包的子包。然后(a)如果你想(server)你可以做相对导入,(b)你的项目更适合重新分配,而不是在python模块层次结构的顶层放两个非常通用的包名