Эффективный способ наличия функции только выполняется однажды в цикле

В данный момент я делаю материал как следующее, которое становится утомительным:

run_once = 0
while 1:
    if run_once == 0:
        myFunction()
        run_once = 1:

Я предполагаю, что существует еще некоторый принятый способ обработать этот материал?

То, что я ищу, имеет функцию, выполняются однажды, по требованию. Например, в нажатии определенной кнопки. Это - интерактивное приложение, которое имеет много пользовательских управляемых переключателей. Наличие переменной спама для каждого переключателя, только для того, чтобы отслеживать то, было ли это выполнено или нет, казалось довольно неэффективным.

62
задан 6 June 2014 в 10:34

6 ответов

В зависимости от ситуации альтернатива декоратору могла быть следующей:

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 нс.

0
ответ дан 31 October 2019 в 13:36

Я проявил более гибкий подход, вдохновленный 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 

захватывающая часть об этом подходе это может использоваться где угодно и не требует фабрик - это - просто маленькое средство отслеживания памяти.

0
ответ дан 31 October 2019 в 13:36

Простая функция можно снова использовать во многих местах в коде (на основе других ответов здесь):

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.'
0
ответ дан 31 October 2019 в 13:36

У Вас есть все те 'переменные спама' за пределами Вашей магистрали 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(), называют.

0
ответ дан 31 October 2019 в 13:36

Другая опция состоит в том, чтобы установить 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() ничего не сделают.

Эта техника менее гибка, чем подход декоратора, предложенный в принятый ответ , но может быть более краткой, если у Вас только есть одна функция, Вы хотите работать однажды.

17
ответ дан 31 October 2019 в 13:36

Вот ответ, который не включает переназначение функций, и все же предотвращает потребность, на которую ужасный "первая" проверка.

__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
2
ответ дан 31 October 2019 в 13:36

Другие вопросы по тегам:

Похожие вопросы: