В данный момент я делаю материал как следующее, которое становится утомительным:
run_once = 0
while 1:
if run_once == 0:
myFunction()
run_once = 1:
Я предполагаю, что существует еще некоторый принятый способ обработать этот материал?
То, что я ищу, имеет функцию, выполняются однажды, по требованию. Например, в нажатии определенной кнопки. Это - интерактивное приложение, которое имеет много пользовательских управляемых переключателей. Наличие переменной спама для каждого переключателя, только для того, чтобы отслеживать то, было ли это выполнено или нет, казалось довольно неэффективным.
В зависимости от ситуации альтернатива декоратору могла быть следующей:
from itertools import chain, repeat
func_iter = chain((myFunction,), repeat(lambda *args, **kwds: None))
while True:
next(func_iter)()
идея основана на итераторах, которые приводят к функции однажды (или использование repeat(muFunction, n)
n
- времена), и затем бесконечно лямбда, делающая ничто.
основное преимущество состоит в том, что Вам не нужен декоратор, который иногда усложняет вещи, здесь все происходит на сингле (по моему мнению) читаемая строка. Недостаток - то, что у Вас есть ужасное next
в Вашем коде.
Производительность, мудрая там, кажется, не большая часть различия, на моей машине оба подхода имеют издержки приблизительно 130 нс.
Я проявил более гибкий подход, вдохновленный functools.partial
функция:
DO_ONCE_MEMORY = []
def do_once(id, func, *args, **kwargs):
if id not in DO_ONCE_MEMORY:
DO_ONCE_MEMORY.append(id)
return func(*args, **kwargs)
else:
return None
С этим подходом Вы можете иметь более сложные и явные взаимодействия:
do_once('foobar', print, "first try")
do_once('foo', print, "first try")
do_once('bar', print, "second try")
# first try
# second try
захватывающая часть об этом подходе это может использоваться где угодно и не требует фабрик - это - просто маленькое средство отслеживания памяти.
Простая функция можно снова использовать во многих местах в коде (на основе других ответов здесь):
def firstrun(keyword, _keys=[]):
"""Returns True only the first time it's called with each keyword."""
if keyword in _keys:
return False
else:
_keys.append(keyword)
return True
или эквивалентно (если Вам нравится полагаться на другие библиотеки):
from collections import defaultdict
from itertools import count
def firstrun(keyword, _keys=defaultdict(count)):
"""Returns True only the first time it's called with each keyword."""
return not _keys[keyword].next()
Демонстрационное использование:
for i in range(20):
if firstrun('house'):
build_house() # runs only once
if firstrun(42): # True
print 'This will print.'
if firstrun(42): # False
print 'This will never print.'
У Вас есть все те 'переменные спама' за пределами Вашей магистрали while True
цикл. Сделать код легче считать те переменные может быть принесено в цикле, прямо рядом с где они используются. Можно также настроить переменное соглашение о присвоении имен для этих переключателей программного управления. Так, например:
# # _already_done checkpoint logic
try:
ran_this_user_request_already_done
except:
this_user_request()
ran_this_user_request_already_done = 1
Примечание, что на первом выполнении этого кода переменная ran_this_user_request_already_done
не определяется до окончания this_user_request()
, называют.
Другая опция состоит в том, чтобы установить func_code
объект кода , чтобы Ваша функция была объектом кода для функции, которая ничего не делает. Это должно быть сделано в конце Вашего тела функции.
, Например:
def run_once():
# Code for something you only want to execute once
run_once.func_code = (lambda:None).func_code
Здесь run_once.func_code = (lambda:None).func_code
замены исполняемый код Вашей функции с кодом для lambda:None, таким образом, все последующие вызовы к run_once()
ничего не сделают.
Эта техника менее гибка, чем подход декоратора, предложенный в принятый ответ , но может быть более краткой, если у Вас только есть одна функция, Вы хотите работать однажды.
Вот ответ, который не включает переназначение функций, и все же предотвращает потребность, на которую ужасный "первая" проверка.
__missing__
поддерживается Python 2.5 и выше.
def do_once_varname1():
print 'performing varname1'
return 'only done once for varname1'
def do_once_varname2():
print 'performing varname2'
return 'only done once for varname2'
class cdict(dict):
def __missing__(self,key):
val=self['do_once_'+key]()
self[key]=val
return val
cache_dict=cdict(do_once_varname1=do_once_varname1,do_once_varname2=do_once_varname2)
if __name__=='__main__':
print cache_dict['varname1'] # causes 2 prints
print cache_dict['varname2'] # causes 2 prints
print cache_dict['varname1'] # just 1 print
print cache_dict['varname2'] # just 1 print
Вывод:
performing varname1
only done once for varname1
performing varname2
only done once for varname2
only done once for varname1
only done once for varname2