python模块导入相对路径issu

2024-09-30 06:23:33 发布

您现在位置:Python中文网/ 问答频道 /正文

我正在用python2.7开发自己的模块。它位于~/Development/.../myModule,而不是/usr/lib/python2.7/dist-packages或{}。内部结构是:

/project-root-dir
  /server
    __init__.py
    service.py
    http.py
  /client
    __init__.py
    client.py

client/client.py包括PyCachedClient类。我遇到了导入问题:

^{pr2}$

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

我将非常感谢关于python模块开发的最佳实践的提示。在


Tags: 模块py路径projectclienthttpinitlib
2条回答

我的Python开发工作流

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

  • 使用^{}隔离
  • ^{}用于创建可安装包并管理依赖项
  • python setup.py develop以开发模式安装该包

虚拟人

首先,我建议使用^{}来获得一个独立的环境来开发您的包。在开发过程中,您将需要安装、升级、降级和卸载包的依赖项,而您并不希望

  • {cd5>污染你的系统的依赖性
  • 系统范围内的site-packages来影响您的开发环境
  • 版本冲突

污染系统范围内的site-packages是很糟糕的,因为您在那里安装的任何包都可以用于您安装的使用系统Python的Python应用程序,即使您只是需要为您的小项目提供这种依赖性。它只是安装在一个新版本中,它重写了系统范围内的site-packages,并且与依赖它的${important_app}不兼容。你明白了。在

让您的系统范围的site-packages影响您的开发环境是不好的,因为您的项目可能依赖于系统Python的site-packages中已有的模块。因此,您忘记正确声明您的项目依赖于该模块,但一切都正常,因为它总是存在于您的本地开发框中。直到你发布你的软件包,人们试图安装它,或者把它推到生产环境中,等等。。。在干净的环境中开发会迫使您正确地声明依赖项。在

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

要创建virtualenv,请使用easy_installpip将其安装到系统范围的Python中,以安装virtualenv包:

sudo pip install virtualenv

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

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

^{pr2}$

这将创建一个目录树~/pyprojects/foobar-env,这是您的虚拟环境。在

要激活virtualenv,cd进入其中,sourcebin/activate script

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

注意前导点.,它是sourceshell命令的简写。还要注意提示是如何变化的:(foobar-env)表示您在激活的virtualenv中(并且总是需要隔离才能工作)。所以每次打开新的终端选项卡或SSH会话时都要激活env。。在

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

setuptools包

现在来创建你的包。基本上,您需要一个带有setup.pysetuptools包来正确声明包的元数据和依赖关系。您可以通过跟随setuptools documentation自行完成此操作,或者使用Paster templates创建一个包框架。要使用粘贴模板,请将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模块的依赖关系:

  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中活动,而无需重新安装您的软件包。在

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

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

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

setuptools控制台脚本入口点

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。在

进一步的步骤

建议阅读:

因此,您有两个,第一个包的模块名为:

server         # server/__init__.py
server.service # server/service.py
server.http    # server/http.py

第二个模块名为:

^{pr2}$

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

from client.client import PyCachedClient

您请求的符号来自client,而不是client.client,根据您的描述,这不是该符号的定义位置。在

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

相关问题 更多 >

    热门问题