如何为函数的全局变量dict重写__setitem__?

时间:2012-11-06 21:31:15

标签: python override

我希望能够拦截函数中的变量赋值并执行自定义代码。我尝试按如下方式创建自定义字典:

class userdict(dict):
    def __setitem__(self, key, value):
        print 'run my code'
        dict.__setitem__(self, key, value)

如果我使用它作为全局字典执行代码,那么我的自定义代码将在每个变量赋值上运行。 e.g:

UserDict = userdict()
exec 'x = 1' in UserDict
#outputs 'run my code'

但是如果我的代码在函数内部,它就不起作用:

code = """
def foo():
    global x
    x = 1
"""
exec code in UserDict
UserDict['foo']()

在这种情况下,会分配“x”,但我的自定义代码不会运行。我假设在一个函数中,全局字典在某种程度上被修改而不调用setitem。它是否正确?有没有办法拦截函数中的变量赋值并执行自定义代码?

我想这样做是为了将函数内部可用的某些对象与程序中的其他对象同步。换句话说,当某个变量的赋值发生在函数内部时,该变化应该传播到我程序中的其他变量。

1 个答案:

答案 0 :(得分:3)

问题可能是内置dict方法在CPython中的子类方法中没有调用覆盖。 Pypy,Jython调用自定义__setitem__(),以便他们在设置x时立即看到。

dis模块显示STORE_GLOBAL用于设置x

>>> def f():
...     global x
...     x = 1
...
...
>>> import dis
>>> dis.dis(f)
  4           0 LOAD_CONST               1 (1)
              3 STORE_GLOBAL             0 (x)
              6 LOAD_CONST               0 (None)
              9 RETURN_VALUE

它在ceval.c中实现为:

    TARGET(STORE_GLOBAL)
        w = GETITEM(names, oparg);
        v = POP();
        err = PyDict_SetItem(f->f_globals, w, v);
        Py_DECREF(v);
        if (err == 0) DISPATCH();
        break;

如果PyDict_SetItem()PyObject_SetItem()替换,那么CPython也可以工作,即调用自定义__setitem__()