Python:如何用外部修改版覆盖包中的一个模块?
我想在一个Python包里更新一个模块,用我自己的版本,满足以下条件:
- 我希望我的更新模块放在原包之外(可能是因为我无法访问包的源代码,或者我想把本地的修改放在一个单独的仓库里等等)。
- 我希望所有指向原包/模块的
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
我能在myproject/__init__.py
里用__import__
语句做点什么魔法来实现这个吗?有没有更“pythonic”的方法来做到这一点?
更新 - 我为什么想这么做
这是我觉得这样做有意义的场景。
- 我在一个服务器上启动一个基于django的网站,而这个服务器上已经全局安装了django。我在这种情况下无法修改实际的django源代码。
- 我的django项目使用了一些第三方的可重用应用,我不想在所有这些应用里改动
import
语句,比如说改成mycustomsettings
。我希望这些可重用的应用对我修改了django.conf.settings的事情一无所知。
5 个回答
你可以试试这个叫做 django-values 的项目,它提供了一个叫 dbsettings
的应用。
这个应用允许你在Python中定义一些设置的占位符,而这些设置的具体值可以由管理员在服务器运行时通过编辑器来设置。它支持很多种值类型,每种类型都对应Python中的基本类型,这样模型方法和其他Python代码就可以像访问普通类属性一样访问这些值。
为了减少这个功能带来的负担,开发者也做了很多工作,确保数据库在每次服务器重启时只查询一次,并且只有在值被更新时才会更新。
注意:这个功能并不是用来替代settings.py的。它是为了那些根据网站或用户需求可能会变化的值设计的,这样的变化不需要重启服务器。settings.py 仍然是那些只会因项目不同而变化的设置的地方。
可以这么理解,settings.py 是处理技术层面必需的东西(比如数据库连接、已安装的应用、可用的中间件等),而 dbsettings 更适合处理组织政策相关的需求(比如配额、最低要求等)。这样,程序员负责技术需求,而管理员负责组织政策。
这个项目是由Marty Alchin(他写过《Pro Django》这本书)开发的,不需要对Django代码做任何修改——它是一个标准的Django应用。
import local_django.conf
import django.conf
django.conf.settings = local_django.conf.settings
模块是单例的。模块只会被初始化或加载一次。在导入那些使用django.conf.settings的模块之前,你需要先进行这个操作,这样它们才能识别到更改。
想了解更多信息,可以看看这个链接,看看是否有更标准的方法,因为文档特别建议不要像我上面展示的那样处理设置对象。http://docs.djangoproject.com/en/dev/topics/settings/ 对于其他对象和模块,这样做应该是没问题的。
只需要在其他任何东西导入之前,先在 sys.modules 中设置一个条目:
import sys
import myreplacement
sys.modules["original"] = myreplacement
这样,当有人执行 "import original" 时,他们会得到你自己的版本。
如果你想替换一个子模块,可以这样做:
import sys
import thepackage
sys.modules["thepackage"].submodule = myreplacement
sys.modules["thepackage.submodule"] = myreplacement
这样 "from thepackage import submodule" 或 "import thepackage.submodule" 就会得到 "myreplacement"。