问题 Python:如何使用包含在包外的修改版本覆盖包中的一个模块?


我想用我自己的模块版本更新python包中的一个模块,具有以下条件:

  • 我希望我更新的模块存在于原始包之外(因为我无法访问包源,或者因为我想将我的本地修改保存在单独的repo中等)。
  • 我想要 import 引用原始包/模块以解析到我的本地模块的语句

这是我想用django的细节做的一个例子,因为这就是我出现这个问题的地方:

说这是我的项目结构

django/
  ... the original, unadulterated django package ...
local_django/
  conf/
    settings.py
myproject/
  __init__.py
  myapp/
    myfile.py

然后在myfile.py中

# These imports should fetch modules from the original django package
from django import models
from django.core.urlresolvers import reverse

# I would like this following import statement to grab a custom version of settings 
# that I define in local_django/conf/settings.py 
from django.conf import settings

def foo():
  return settings.some_setting

我能用魔法做些什么吗? __import__ 声明 myproject/__init__.py 完成这个?是否有更“pythonic”的方法来实现这一目标?

更新 - 我为什么要这样做

这是我认为这有意义的场景。

  • 我正在全球预装django的服务器上启动一个django支持的网站。在这种情况下,我无法修改实际的django源。
  • 我的django项目使用第三方可重用的应用程序,我不想改变 imports 例如,在所有要导入的应用程序中 mycustomsettings。我想让可重用的应用程序幸福地无知我已经改变了django.conf.settings的实现。

7671
2018-05-22 05:19


起源



答案:


只需在sys.modules中设置条目,然后再导入它:

import sys
import myreplacement
sys.modules["original"] = myreplacement

然后,当某人“导入原始”时,他们将获得您的版本。

如果要替换子模块,可以这样做:

import sys
import thepackage
sys.modules["thepackage"].submodule = myreplacement
sys.modules["thepackage.submodule"] = myreplacement

然后“from thepackage import submodule”或“import thepackage.submodule”将给出“myreplacement”。


12
2018-05-22 11:32



使用这种方法,我可以覆盖包中的模块吗?我想要 import foo.bar.somemodule 加载我的somemodule版本。 - zlovelady
它似乎工作。我已经更新了答案...... - Thomas Leonard


答案:


只需在sys.modules中设置条目,然后再导入它:

import sys
import myreplacement
sys.modules["original"] = myreplacement

然后,当某人“导入原始”时,他们将获得您的版本。

如果要替换子模块,可以这样做:

import sys
import thepackage
sys.modules["thepackage"].submodule = myreplacement
sys.modules["thepackage.submodule"] = myreplacement

然后“from thepackage import submodule”或“import thepackage.submodule”将给出“myreplacement”。


12
2018-05-22 11:32



使用这种方法,我可以覆盖包中的模块吗?我想要 import foo.bar.somemodule 加载我的somemodule版本。 - zlovelady
它似乎工作。我已经更新了答案...... - Thomas Leonard


也许你可以利用 Django的值 项目,提供了一个 dbsettings 应用程序,...

...允许占位符进行设置   在Python中定义,而他们的值   由工作人员使用编辑设置   服务器已启动并正在运行。许多   值类型可用,它们   每个映射到本机Python类型,所以   模型方法和其他Python代码   可以作为标准类访问它们   属性。

还做了很多努力   减少此功能的开销,   这样只查询数据库   每次服务器重启期间一次,和   仅在值时更新   他们自己更新。

注意:这不是作为一个   替换settings.py。这是   专为预期的价值而设计   根据需要改变   网站或其用户,这样的   变化不需要那么多   重新开始。 settings.py仍然是   放置设置的地方   仅因项目而异。

看待它的一种方法是   settings.py适合那些东西   是技术要求   透视(数据库连接,   已安装的应用程序,avaialble   中间件等),而dbsettings是   最适合那些事情   由于组织政策的要求   (配额,最低要求等)。   这样,程序员就维持着   技术要求,而   管理员维护组织   政策。

该项目由Marty Alchin(编写Pro Django书籍)并且不需要对Django代码进行任何更改 - 它是标准的Django应用程序。


1
2018-05-22 07:45



django-values很好。我以前用过它。但我真的不是在寻找一种特定于django设置的解决方案。我只是把它作为一个例子。我希望能通过pythons import获得一些洞察力是否可行。 - zlovelady


import local_django.conf
import django.conf
django.conf.settings = local_django.conf.settings

模块是单身人士。模块仅初始化/加载一次。在导入使用django.conf.settings的模块之前,您需要执行此操作以获取更改。

阅读此链接以获取更多信息,以了解是否有更标准的方法使用django,因为文档特别建议不要像我在上面显示的设置对象一样。 http://docs.djangoproject.com/en/dev/topics/settings/ 它应该适用于其他对象和模块。


1
2018-05-22 11:24



真棒。不知道那很容易。谢谢。 - zlovelady
使用django.conf.settings的示例是一个不恰当的示例,因为django使用getter和setter来自动覆盖本地设置的全局设置。 django文档特别说不要做我在该示例中所做的事情,因为本地设置会自动覆盖全局设置。如果您对django中的特定设置对象使用上述方法,请注意副作用。 - freegnu


在这种特殊情况下,“最好遵循规定 用于创建自己的设置的机制

在一般情况下,使用进口产品可能会让您的读者感到困惑(可能是您)。改变类行为的pythonic方法是sub-class和override。


0
2018-05-22 05:28



谢谢msw。但是我不想创建我自己的设置,我想改变django围绕设置的核心行为,例如,通过在数据库中存储一些设置并使它们可以通过管理员进行编辑。我在澄清的问题中加入了一些理由。 - zlovelady
我确实想要子类和覆盖,但我想在一个存在于原始包目录结构之外的文件中 - zlovelady


大约两三天前,我被介绍过这个,但是这样做的一种方法是使用Python虚拟环境。这允许您指定您正在使用的Python版本,以及您要安装的模块的版本,并且能够相对容易地在不同版本和项目之间进行交换;它还允许您安装Python系统,否则您可能无法获得安装所需的权限。

您可以从中了解更多关于virtualenv的信息 virtualenv信息

还有一个“virtualenvwrapper”软件包,它提供了更容易在环境之间交换的工具。

不可否认,我对此没有多少经验,我发现这个问题是为了了解如何使用自定义版本覆盖Python标准库 - 所以它并不完美。但是,我对其指定安装内容的能力印象深刻,直到单个模块的版本!


0
2018-01-16 18:12