执行前模块命名空间初始化

时间:2017-10-19 10:43:29

标签: python module namespaces

我正在尝试通过使用importlib.reload重新加载模块来在运行时动态更新代码。但是,我需要在执行模块代码之前设置一个特定的模块变量。我可以在重新加载后轻松地将其设置为属性,但每个模块都已经执行了它的代码(例如,定义了它的默认参数)。

一个简单的例子:

# module.py

def do():
  try:
    print(a)
  except NameError:
    print('failed')
# main.py

import module

module.do() # prints failed

module.a = 'succeeded'

module.do() # prints succeeded

所需的伪代码:

import_module_without_executing_code module

module.initialise(a = 'succeeded')

module.do()

有没有办法控制模块命名空间初始化(比如使用元类的类)?

2 个答案:

答案 0 :(得分:1)

除了交互式调试之外,使用reload通常不是一个好主意。例如,它可以轻松创建两个module.A类型的对象不是同一类型的情况。

你想要的是execfile。传递一个全局字典(你不需要一个显式的本地词典)来保持每个执行被隔离;你提前存放的任何东西就像预先设置的那样"你想要的变量。如果你想要一个真实的"模块接口更改,您可以拥有一个包装器模块,该模块从您更改的文件中调用(或仅作为属性保存)最近加载的函数。

当然,由于您使用的是Python 3,因此您必须使用one of the replacements for execfile

答案 1 :(得分:0)

严格地说,我不相信有一种方法可以在Python中本地描述你所描述的内容。但是,假设您拥有要导入的模块,那么需要一些初始化输入的Python模块的常用方法是使用init函数。

如果您只需要设置一些内部变量,例如上面示例中的a,那就很简单:只需声明一些模块全局变量并将其设置在init函数中:

演示:https://repl.it/MyK0

<强>模块:

## mymodule.py

a = None

def do():
  print(a)


def init(_a):
  global a
  a = _a

主要

## main.py

import mymodule

mymodule.init(123)
mymodule.do()

mymodule.init('foo')
mymodule.do()

<强>输出:

123
foo

如果您需要实际重新定义某些函数,那么事情会变得棘手,因为某些动态内部函数依赖于您提供的输入。这是一个借鉴https://stackoverflow.com/a/1676860的解决方案。基本上,我们的想法是通过使用魔术变量__name__索引到系统模块字典sys.modules来获取对当前模块的引用,然后定义或覆盖需要它的函数。我们可以在本地将函数定义为内部函数,然后将它们添加到模块中:

演示:https://repl.it/MyHT/2

<强>模块:

## mymodule.py

import sys

def init(a):
  current_module = sys.modules[__name__]
  def _do():
    try:
      print(a)
    except NameError:
      print('failed')
  current_module.do = _do