我有同一Python包的两个版本。我需要从当前版本的子包中的模块中调用旧版本(过去已复制过)的旧版本内的函数
我现在的位置:
now/
package/
__init__.py
subpackage/
__init__.py
module.py -> "import package.subpackage.... <HERE>"
subpackage2/
...
...
旧版本:
past/
package/
__init__.py
subpackage/
__init__.py
module.py -> "import package.subpackage; from . import module2; .... def f(x) ..."
module2.py
subpackage2/
...
...
我需要在<HERE>
中导入“旧” f
并运行它。
理想
f
应该在旧程序包中生活,而不知道新版本的程序包f
执行之后,调用“ import package.subpackage2”她的东西应该运行“新”版本)基本思想是通过将我用于某些任务的代码与输出数据一起保存,然后能够运行其中的一部分来提高可重复性。
可悲的是,我知道这对于Python 3而言并不是一件简单的任务,因此我准备接受某种折衷方案。例如,我准备接受运行旧的f(x)
后,“新”代码中的名称package
将绑定到旧的代码。
编辑
我使用importlib
进行了两种尝试。这个想法是先创建一个对象mod
然后执行f = getattr(mod, "f")
,但这是行不通的
sys.path
更改为['.../past/package/subpackage']
,然后调用importlib.import_module('package.subpackage.module')
。问题在于,即使更改了sys.path
,它也会在“ now”中加载一个,这可能是因为package
中已经有名称sys.modules
spec = importlib.util.spec_from_file_location("module", "path..to..past..module.py"))
module = importlib.util.module_from_spec(spec)
spec.loader.exec_module(module)
在这种情况下,相对导入(from . import module2.py
)将不起作用,并显示错误“尝试了相对导入,没有已知的父包” 答案 0 :(得分:1)
有一种方法可以很简单地工作,但是您必须对旧软件包进行一些修改。
您可以简单地在now/package/old/__init__.py
中创建一个文件,其中包含:
__path__ = ['/absolute/path/to/old/package']
在新程序包中,您可以执行以下操作:
from package.old.package.subpackage.module import f as old_f
这里的问题是,旧软件包尝试使用绝对导入来导入其自己的软件包,它将改为从新软件包中加载内容。因此,当从自己的包中导入东西时,旧包将只需要使用相对的导入,否则您必须在旧包执行的所有绝对导入之前加上package.old。
如果您可以通过这种方式修改旧软件包,那应该没问题。如果该限制对您不起作用,请继续阅读。
如果确实如此,请确保出于某些原因不想修改旧软件包。然后,做一些不可思议的事情,您想用自己的版本替换builtins.__import__
,该版本根据谁进行导入而返回不同的模块。您可以通过检查调用堆栈来确定谁在进行导入。
例如,这是您可能会做的(在Python 3.6上测试):
import builtins
import inspect
import package.old
old_package_path = package.old.__path__[0]
OUR_PACKAGE_NAME = 'package'
OUR_PACKAGE_NAME_WITH_DOT = OUR_PACKAGE_NAME + '.'
def import_module(name, globs=None, locs=None, fromlist=(), level=0):
# only intercept imports for our own package from our old module
if not name.startswith(OUR_PACKAGE_NAME_WITH_DOT) or \
not inspect.stack()[1].filename.startswith(old_package_path):
return real_import(name, globs, locs, fromlist, level)
new_name = OUR_PACKAGE_NAME + '.old.' + name[len(OUR_PACKAGE_NAME_WITH_DOT):]
mod = real_import(new_name, globs, locs, fromlist, level)
return mod.old
# save the original __import__ since we'll need it to do the actual import
real_import = builtins.__import__
builtins.__import__ = import_module
builtins.__import__
在解释器遇到的任何导入语句上被调用,并且该调用不会被缓存,因此即使每次调用使用相同的名称,您也可以返回不同的内容。
以下是我的旧答案,此处仅出于历史目的
我不太了解您要做什么,但是在Python 3中使用importlib可能可以做到这一点。
您只需创建一个模块加载器,即可从显式文件路径加载模块。
还有一个invalidate_caches()
和reload()
函数可能有用,尽管您可能不需要它们。