К тому времени, когда ваш декоратор получает объект функции f
, он уже скомпилирован - в частности, он был скомпилирован с учетом того, что x
является локальным (потому что он назначен с присваиванием +=
), обычный оптимизация (в 2.*
вы можете победить оптимизацию по ошеломительной цене в производительности, начав с f
с exec ''
; в 2.*
вы не сможете победить оптимизацию). По сути, чтобы использовать нужный вам синтаксис, вы должны перекомпилировать f
(восстановив его источники, если вы знаете, что они будут доступны во время выполнения, или, что намного сложнее, путем взлома байт-кода) с каким-то образом измененными источниками - один раз вы решили пойти по этому пути, простейший подход, вероятно, состоит в том, чтобы заменить x
на f.x
по всему телу f
.
Лично, если и когда я оказываюсь настолько упорным против языка (или другой технологии), что я пытаюсь подчиниться своей воле, чтобы навязать свои желания, я признаю, что я использую не тот язык (или другие технологии), если эти желания абсолютно необходимы, и тогда решение должно состоять в том, чтобы изменить технологию; или, если эти желания не так важны, откажись от них.
В любом случае, я перестаю пытаться исказить язык слишком далеко от его очевидных замыслов: даже если бы я натолкнулся на какой-то хакерский, хрупкий клудж, это, несомненно, было бы несостоятельным. В этом случае желательные намерения Python очень ясны: голые имена, которые связываются внутри функции, являются локальными для этой функции, если явно не обозначены как глобальные - точка. Таким образом, ваша попытка сделать голые имена (которые переопределяются внутри функции) означает что-то совершенно иное, чем «локальные» - именно этот вид борьбы.
Редактировать : Если вы готовы отказаться от настойчивости в использовании голых имен для своей "статики", то вдруг вы больше не сражаетесь с Python, но скорее "идти в ногу" с языком (несмотря на сбой дизайна global
[и nonlocal
], но это отдельная напыщенная речь ;-). Так, например:
class _StaticStuff(object):
_static_stack = []
def push(self, d):
self._static_stack.append(d)
def pop(self):
self._static_stack.pop()
def __getattr__(self, n):
return self._static_stack[-1][n]
def __setattr__(self, n, v):
self._static_stack[-1][n] = v
import __builtin__
__builtin__.static = _StaticStuff()
def with_static(**variables):
def dowrap(f):
def wrapper(*a, **k):
static.push(variables)
try: return f(*a, **k)
finally: static.pop()
return wrapper
return dowrap
@with_static(x=0)
def f():
static.x += 1
print static.x
f()
f()
Это работает так же, как вы хотите, печатая 1 и затем 2. (Я использую __builtin__
, чтобы упростить использование with_static
для украшения функций, живущих в любом модуле, конечно). У вас может быть несколько разных реализаций, но ключевой момент любой хорошей реализации состоит в том, что «статическими переменными» будут квалифицированные имена, не голые имена - делая это ясно, что они не являются локальными переменными, играют с зерном языка и так далее. (Подобные встроенные контейнеры и квалифицированные имена на их основе, должен использоваться в дизайне Python вместо сбоев дизайна global
и nonlocal
, чтобы указать другие виды переменных, которые не ' t локальные и, следовательно, не должны использовать голые имена ... ну, вы можете реализовать специальный контейнер globvar
в тех же строках, что и выше static
, даже без необходимости декорирования, хотя я не уверен это вполне выполнимо для случая nonlocal
[возможно с некоторым украшением и малейшим количеством черной магии ...; =)]).
Редактировать : комментарии указывают на то, что указанный код не работает, когда вы украшаете только функцию, которая возвращает замыкание (вместо того, чтобы украшать само замыкание). Это верно: конечно, вы должны украсить конкретную функцию, которая использует static
(а может быть только одна, по определению переменных функции - static
!), А не случайную функцию, которая на самом деле не использует static
, а скорее просто находится в некоторой лексической связи с тем, что делает . Например:
def f():
@with_static(x=0)
def g():
static.x += 1
print static.x
return g
x = f()
x()
x()
это работает, в то время как перемещение декоратора на f
вместо g
не (и не может быть возможно).
Если фактические desiderata имеют дело не со статическими переменными (видимыми и пригодными для использования только в пределах одной функции), а с некоторой гибридной вещью, которую можно использовать в определенном специфическом наборе функций, это должно быть указано очень точно (и, несомненно, реализовано совсем по-другому) в зависимости от того, какие фактические спецификации являются ) - и в идеале это должно происходить в новых и отдельных вопросах SO, потому что это (а именно, о static ), и этот ответ на этот конкретный вопрос уже достаточно большой.