我正在使用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模块开发的最佳实践的提示。
答案 0 :(得分:39)
这是开发Python软件包的基本过程,它包含了我认为是社区中的最佳实践。它是基本的 - 如果你真的认真地开发Python软件包,还有更多的东西,每个人都有自己的偏好,但它应该作为一个模板来开始,然后了解更多关于涉及的部分。基本步骤是:
virtualenv
进行隔离setuptools
用于创建可安装程序包并管理依赖项python setup.py develop
以开发模式安装该软件包首先,我建议使用virtualenv
来创建一个隔离的环境来开发你的软件包。在开发过程中,你需要安装,升级,降级和卸载软件包的依赖关系。 #39; t想要
site-packages
site-packages
影响您的开发环境污染整个系统范围site-packages
是不好的,因为您安装的所有软件包都可以使用系统Python安装的所有Python应用程序,即使您只需要为您的小项目提供依赖项。它只是安装在一个新版本中,它覆盖了系统范围内site-packages
的版本,并且与依赖它的$ {important_app}不兼容。你明白了。
让您的系统范围site-packages
影响您的开发环境是不好的,因为您的项目可能依赖于您在系统Python site-packages
中已经获得的模块。因此,您忘记正确声明您的项目依赖于该模块,但一切正常,因为它始终存在于您的本地开发框中。在您发布包之前,人们会尝试安装它,或者将其推送到生产中等等......在干净的环境中开发会迫使您正确地声明您的依赖项。
因此, virtualenv 是一个具有自己的Python解释器和模块搜索路径的隔离环境。它基于您之前安装的Python安装,但与之隔离。
要创建virtualenv,请使用virtualenv
或easy_install
将pip
软件包安装到系统范围的Python中来安装sudo pip install virtualenv
:
cd ~/pyprojects
virtualenv --no-site-packages foobar-env
请注意,这将是仅时间,您将以root身份(使用sudo)安装到全局站点包中。在此之后的一切都将发生在您即将创建的虚拟世界中。
现在创建一个用于开发包的virtualenv:
~/pyprojects/foobar-env
这将创建一个目录树cd
,这是你的virtualenv。
要激活virtualenv,source
并bin/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
和隔离的模块搜索路径。
现在创建您的包。基本上,您需要一个setup.py
包PasteScript
来正确声明您的包的元数据和依赖项。您可以按照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 foobar
或setup.py
__init__.py
在内部,你几乎总是创建一个Python包(如在&#34;一个带有import foobar
的目录),它们被称为相同的。这不是必需的,顶级Python包的名称可以是任何有效的包名,但它的一般惯例是将其命名为与分发相同。这就是为什么保持两者分开是重要的,但并不总是容易的。因为顶级python包名是什么
from 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
这将安装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运行。
<强> 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}}。
zest.releaser
建议阅读:
答案 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模块层次结构的顶层放两个非常通用的包名