Python: как динамически установить среду закрытия функций - PullRequest
6 голосов
/ 22 июля 2011

Я хочу объявить функцию динамически, и я хочу обернуть любой доступ к глобальным переменным ИЛИ альтернативно определить, какие переменные являются свободными, и обернуть любой доступ к свободным переменным.

Яигра с кодом, подобным следующему:

class D:
    def __init__(self):
        self.d = {}     
    def __getitem__(self, k):
        print "D get", k
        return self.d[k]
    def __setitem__(self, k, v):
        print "D set", k, v
        self.d[k] = v
    def __getattr__(self, k):
        print "D attr", k
        raise AttributeError

globalsDict = D()

src = "def foo(): print x"

compiled = compile(src, "<foo>", "exec")
exec compiled in {}, globalsDict

f = globalsDict["foo"]
print(f)

f()

Это приводит к выводу:

D set foo <function foo at 0x10f47b758>
D get foo
<function foo at 0x10f47b758>
Traceback (most recent call last):
  File "test_eval.py", line 40, in <module>
    f()
  File "<foo>", line 1, in foo
NameError: global name 'x' is not defined

Что я хочу, так это как-то перехватить доступ к x с помощью моей dict-подобной оболочки D.Как я могу это сделать?

Я не хочу заранее определять все глобальные переменные (в данном случае x), потому что я хочу иметь возможность загружать их лениво.

Ответы [ 2 ]

2 голосов
/ 22 июля 2011

То, что вы ищете, это объект проксирования .

Вот рецепт для объектного прокси, который поддерживает перехват до и после вызова:

http://code.activestate.com/recipes/366254-generic-proxy-object-with-beforeafter-method-hooks/

Создайте подкласс, который фактически не загружает объект до тех пор, пока в первый раз не будет вызван хук _pre. Любое обращение к объекту приведет к загрузке реального объекта, и все вызовы будут обрабатываться реальным объектом напрямую.

1 голос
/ 22 июля 2011

Попробуйте это

class GlobalDict(object):

    def __init__(self, **kwargs):
        self.d = kwargs

    def __getitem__(self, key):
        print 'getting', key
        return self.d[key]

    def __setitem__(self, key, value):
        print 'setting', key, 'to', value
        if hasattr(value, '__globals__'):
            value.__globals__.update(self.d)
        self.d[key] = value
        for v in self.d.values():
            if v is not value:
                if hasattr(v, '__globals__'):
                    v.__globals__.update(self.d)

    def __delitem__(self, key):
        print 'deling', key
        del self.d[key]
        for v in self.d.values():
            if hasattr(v, '__globals__'):
                del v.__globals__[key]

>>> gd = GlobalDict()
>>> src = 'def foo(): print x'
>>> compiled = compile(src, '<foo>', 'exec')
>>> exec compiled in {}, gd
setting foo to <function foo at 0x102223b18>
>>> f = gd['foo']
getting foo
>>> f
<function foo at 0x102223b18>
>>> f() # This one will throw an error
Traceback (most recent call last):
  File "<input>", line 1, in <module>
  File "<foo>", line 1, in foo
NameError: global name 'x' is not defined
>>> gd['x'] = 1
setting x to 1
>>> f()
1
>>> del gd['x'] # removes 'x' from the globals of anything in gd
>>> f() # Will now fail again
Traceback (most recent call last):
  File "<input>", line 1, in <module>
  File "<foo>", line 1, in foo
NameError: global name 'x' is not defined
...