Не думаю, что вы сможете найти какую-либо «приличную» реализацию с помощью оператора with
(менеджер контекста). Просто проверьте следующий вопрос: Пропуск выполнения -with- block
Менеджеры контекста выполняют код перед входом в блок кода и после его выхода, но они всегда выполняют блок кода, за исключением некоторых очень тяжелых взлом завершен.
Однако вы можете получить нечто похожее на то, что ищете, с помощью оператора if
и самоанализа или ключевого аргумента. Обратите внимание, что самоанализ очень, очень, медленный , поэтому не используйте его в критичном для производительности коде. Для отладки может быть хорошо, если циклы не выполняются слишком часто.
Вы реализуете функцию magi c, которая может определить, где в исходном коде она была вызвана, и ведет ли она бухгалтерский учет, что он возвращает True впервые для местоположения каждого звонящего и False в противном случае.
На мой взгляд, имя first_time()
может быть более понятным, чем once()
, но это всего лишь деталь.
использование будет выглядеть примерно так:
def f(a):
if first_time():
print("first time f was called (a=%s)" % a)
return 2 * a
def g(a):
if first_time():
print("first time g was called (a=%s)" % a)
return 3 * a
for v in (1, 2):
rslt = f(v)
print("F(%d) = %d" % (v, rslt))
g(3)
g(4)
Для python2 это будет:
import inspect
first_state = set()
def first_time():
curframe = inspect.currentframe()
calframe = inspect.getouterframes(curframe, 2)
calfi = calframe[1]
key = calfi[1], calfi[2]
# print("K", key) # uncomment for debugging
if key in first_state:
return False
first_state.add(key)
return True
Для python3 Это будет:
import inspect
first_state = set()
def first_time():
curframe = inspect.currentframe()
calframe = inspect.getouterframes(curframe, 2)
calfi = calframe[1]
key = calfi.filename, calfi.lineno
3 print("K", key) # uncomment for debugging
if key in first_state:
return False
first_state.add(key)
return True
Сравнение с решением Мартино:
- Мое предлагаемое решение будет намного медленнее. Это зависит от контекста, является ли это проблемой, но посмотрите на мое альтернативное решение ниже.
- Вы можете иметь несколько проверок для каждой функции даже в условном коде, они будут оцениваться индивидуально
- you не нужно украшать функцию для использования first_time ()
- first_time () можно использовать на уровне модуля, поэтому в основном в любом месте вашего кода.
- вы сможете выполнить сброс, выполнив first_state.clear ()
- добавив идентификатор потока к кортежу ключа, который можно разрешить вызывать один раз для потока
Более быстрая и уродливая альтернатива Для жесткой Приведенное ниже решение l oop примерно в 20.000 (двадцать тысяч) раз быстрее
Я только что измерил небольшой пример с python 3.6
import inspect
first_state = set()
def first_time(*key):
if not key:
curframe = inspect.currentframe()
calframe = inspect.getouterframes(curframe, 2)
calfi = calframe[1]
key = calfi[1], calfi[2]
# print("K", key) # uncomment for debugging
if key in first_state:
return False
first_state.add(key)
return True
Эта альтернативная реализация позволяет явно передавать уникальный ключ для кода, критичного к производительности. Таким образом, эти явные параметры избегают использования самоанализа, но делают вызывающий код более уродливым. Для кода, который не критичен по производительности, вы не передаете ключ, и он ведет себя как мое первоначально предложенное решение.
Пример:
import time
t0 = time.time()
c = 0
for v in range(1000):
if first_time(__file__, "critical_1"):
print("first time within performance critical loop")
c += v
t - time.time() - t0
print("Required time: %f" % t)
Реализация print_once:
Если это только для печати отладочных сообщений, то, возможно, реализуйте непосредственно print_once()
import inspect
print_once_state = set()
def print_once(msg, *args, key=None):
if not key:
curframe = inspect.currentframe()
calframe = inspect.getouterframes(curframe, 2)
calfi = calframe[1]
key = calfi[1], calfi[2]
if key not in print_once_state:
if msg is None:
print("entered %s first time" % (key,))
else:
print(msg, *args)
print_once_state.add(key)
и пример:
import time
t0 = time.time()
c = 0
for v in range(1000):
print_once("in loop: c = ", c, key=(__file__, "critical_1"))
c += v
t - time.time() - t0
print("Required time: %f" % t)