当前位置: 首页 > 面试题库 >

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

巢皓君
2023-03-14
问题内容

我正在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方式设置所有路径/设置?我知道export PYTHONPATH=...每次打开控制台并尝试运行服务器时都可以在shell中运行,但是我猜这不是最好的方法。如果我的模块是通过PyPi(或类似的东西)安装的,则我会将其安装在/usr/lib/python...path中并自动加载。

我将感谢有关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。因此,您忘记正确地声明您的项目依赖于该模块,但是一切正常,因为它始终在本地开发框中。在发布软件包之前,人们会尝试安装它或将其投入生产等。在干净的环境中进行开发会迫使您正确声明依赖项。

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

要创建的virtualenv,安装virtualenv通过广泛使用Python它安装到你的系统包easy_installpip

sudo pip install virtualenv

请注意,这将是您 唯一 一次以root用户身份(使用sudo)在全局站点程序包中安装某些程序。之后的一切都会在您要创建的virtualenv内部发生。

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

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

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

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

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

注意前导点.,这是sourceshell命令的简写。还要注意提示如何更改:(foobar- env)意味着您处于激活的virtualenv内部(始终需要隔离才能起作用)。因此,每次您打开新的终端标签或SSH会话等时,请激活您的环境。

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

setuptools软件包

现在创建您的包。基本上,您将希望带有的setuptools软件包setup.py正确声明您的软件包的元数据和依赖项。您可以按照setuptools文档的要求自行完成此操作,也可以使用Paster模板创建软件包框架。要使用Paster模板,请安装PasteScript到您的virtualenv中:

pip install PasteScript

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

mkdir src
cd src/

现在创建您的包,执行

paster create -t basic_package foobar

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

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

  • 人们将使用easy_install或安装您的软件包pip 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中处于活动状态,而无需重新安装程序包。

现在,对于您的原始问题,使用相对路径导入:我的建议是,不要这样做。现在您已经有了一个正确的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
  ''',

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



 类似资料:
  • 问题内容: 给定相对路径,如何导入Python模块? 例如,如果包含和,和包含,我怎么导入到? 这是一个视觉表示: 希望包含,但是重组文件夹层次结构不是一种选择。 问题答案: 假设你的两个目录都是真实的Python包(文件中确实有文件),那么这是一个相对于脚本位置包含模块的安全解决方案。 我假设你想这样做,因为你需要在脚本中包括一组模块。我在多个产品的生产环境中使用了此功能,并在许多特殊情况下工作

  • 根据这个答案,您可以使用来使用类似这样的相对导入: 为什么相对导入不适用于sklearn。特征提取。文本 我验证了是一个具有以下功能的模块: 编辑 “不工作”,我的意思是它不导入模块。 我正在使用Python 3.4 绝对方式工作: 相对方式不:

  • 问题内容: 我有一个模块foo,包含util.py和bar.py。 我想将其导入IDLE或python会话中。我该怎么办? 我找不到关于如何在当前目录或默认python PATH中导入模块的文档。经过尝试后, 我能得到的最接近的是 这给了我在Windows 7上拒绝的权限。 问题答案: 一种方法是简单地修改路径: 请注意,这要求foo为python包,即包含一个文件。如果您不想修改,也可以修改环境

  • 过去,每当我想在Node.js应用程序中拥有相对路径时,我都会使用。如果我通过格式使用ES模块,在某个目录路径变为相对路径时,我如何具有相同的功能? 另一种方法是,我是否能够将别名分配给目录,使所有相对路径都与该别名相关,就像是相对于当前目录的路径的别名一样。

  • rank ▲ ✰ vote url 53 400 168 184 url 从相对路径引入一个模块 怎么用相对路径引入一个模块? 例如,如果dirFoo包含Foo.py和dirBar,而dirBar包含Bar.py,怎么样才能在Foo.py里引入Bar.py? 结构: dirFoo\ Foo.py dirBar\ Bar.py Foo希望包含Bar,但是重新组织文件

  • 问题 将代码组织成包,想用import语句从另一个包名没有硬编码过的包中导入子模块。 解决方案 使用包的相对导入,使一个模块导入同一个包的另一个模块 举个例子,假设在你的文件系统上有mypackage包,组织如下: mypackage/ __init__.py A/ __init__.py spam.py grok.py B/