К тому времени, когда ваш декоратор получит объект функции f, он уже будет скомпилирован — в частности, он был скомпилирован с учетом того, что x является локальным (поскольку он назначается с присваиванием +=), обычная оптимизация (в 2.* вы можете отменить оптимизацию , по ошеломляющей цене производительности, начиная f с exec ''; в 2.* оптимизацию не победить). По сути, чтобы использовать желаемый синтаксис, вы должны перекомпилировать f (путем восстановления его исходных кодов, если вы знаете, что они будут доступны во время выполнения, или, что гораздо сложнее, с помощью взлома байт-кода) с каким-либо измененным исходным кодом — как только вы Если мы решили пойти по этому пути, самый простой способ, вероятно, состоит в том, чтобы изменить x на f.x во всем теле f.
Лично, если и когда я обнаруживаю, что так сильно борюсь с языком (или другой технологией), что пытаюсь подчиниться своей воле, чтобы навязать свои желания, я признаю, что либо использую неправильный язык (или другую технологию) , если эти желания абсолютно необходимы, то решение должно заключаться в изменении технологии; или, если эти желания не так важны, откажитесь от них.
В любом случае, я отказываюсь от попыток исказить язык слишком далеко от его очевидных дизайнерских намерений: даже если бы я придумал какой-нибудь хакерский, хрупкий кладж, его, несомненно, было бы невозможно поддерживать. В этом случае намерения Python очень ясны: bar-имена, которые повторно связываются внутри функций, являются локальными для этой функции, если только они явно не обозначены как глобальные — и точка. Таким образом, ваша попытка заставить barenames (которые перепривязываются внутри функции) означать что-то совершенно иное, чем «местные жители», — это именно такая борьба.
Редактировать: если вы готовы отказаться от настойчивого использования barnames для своей «статики», то внезапно вы больше не боретесь с 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 для обозначения других типов переменных, которые не являются локальными. и, следовательно, не следует использовать barenames... ну, вы можете реализовать специальный контейнер globvar на тех же строках, что и вышеперечисленные static, даже не нуждаясь в декорировании, хотя я не уверен, что это полностью осуществимо для nonlocal футляр [возможно, с каким-то украшением и малейшим количеством черной магии...;=)]).
Изменить: комментарий указывает, что приведенный код не работает, когда вы декорируете только функцию, возвращающую замыкание (вместо декорирования самого замыкания). Правильно: конечно, вы должны декорировать конкретную функцию, которая использует static (а может быть только одна, по определению переменных function-static!), а не случайную функцию, которая на самом деле не использует static, а скорее просто оказывается в какой-то лексической связи с тем, что делает. Например:
def f():
@with_static(x=0)
def g():
static.x += 1
print static.x
return g
x = f()
x()
x()
это работает, а перемещение декоратора на f вместо g не работает (и не может быть).
Если фактические пожелания связаны не со статическими переменными (видимыми и используемыми только в одной функции), а с какой-то гибридной вещью, которую можно использовать в определенном наборе функций, то это должно быть очень точно указано (и, несомненно, реализовано очень по-разному, в зависимости от каковы фактические спецификации ) - и в идеале это должно произойти в новом и отдельном вопросе SO, потому что этот (который вместо этого конкретно касается static), и этот ответ на этот конкретный вопрос уже достаточно велики.
person
Alex Martelli
schedule
23.10.2009