具有函数的有效方法只能在循环中执行一次

目前,我正在做类似以下的事情,这是越来越乏味:

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

我猜测有一些更可接受的方式来处理这个东西?

我正在寻找的是一个function执行一次,按需。 例如,在按下某个button。 这是一个交互式的应用程序,它有很多用户控制的开关。 每个交换机都有一个垃圾variables,只是为了跟踪是否已经运行,似乎效率低下。

我会在函数上使用一个装饰器来处理跟踪它运行多less次。

 def run_once(f): def wrapper(*args, **kwargs): if not wrapper.has_run: wrapper.has_run = True return f(*args, **kwargs) wrapper.has_run = False return wrapper @run_once def my_function(foo, bar): return foo+bar 

现在my_function将只运行一次。 其他调用将返回None 。 只要添加一个else子句,如果你想要它返回别的东西。 从你的例子来看,它不需要返回任何东西。

如果您不控制函数的创build,或者需要在其他上下文中正常使用函数,则也可以手动应用装饰器。

 action = run_once(my_function) while 1: if predicate: action() 

这将使my_function可用于其他用途。

最后,如果你只需要运行两次,那么你可以做

 action = run_once(my_function) action() # run once the first time action.has_run = False action() # run once the second time 

另一种select是将函数的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用你run_once.func_code = (lambda:None).func_code的代码replace你的函数的可执行代码,所有后续调用run_once()都不会做任何事情。

这种技术比接受的答案中提出的装饰器方法更不灵活,但如果只有一个函数要运行一次,则可能更简洁。

在循环之前运行该函数。 例:

 myFunction() while True: # all the other code being executed in your loop 

这是显而易见的解决scheme。 如果不仅仅是满足眼睛,解决scheme可能会更复杂一些。

如果满足一些条件,我假设这是一个最多需要执行一次的操作。 既然你不会一直执行这个动作,你不能在循环之外无条件地执行。 就像懒惰地检索一些数据(并caching它),如果你得到一个请求,但不检索它。

 def do_something(): [x() for x in expensive_operations] global action action = lambda : None action = do_something while True: # some sort of complex logic... if foo: action() 

有很多方法可以做你想做的事情; 但是,请注意,这是很有可能的,如问题中所述,您不必在循环内调用函数。

如果你坚持在循环中调用函数,你也可以这样做:

 needs_to_run= expensive_function while 1: … if needs_to_run: needs_to_run(); needs_to_run= None … 

我想到了另一个非常有效的方法来做到这一点,不需要装饰函数或类。 相反,它只是使用一个可变关键字参数,它应该在大多数Python版本中工作。 大多数情况下,这些都是可以避免的,因为通常情况下,您不希望默认参数值能够从呼叫转移中更改 – 但是该function可以作为便宜的存储机制使用。 这是如何工作的:

 def my_function1(_has_run=[]): if _has_run: return print "my_function1 doing stuff" _has_run.append(1) def my_function2(_has_run=[]): if _has_run: return print "my_function2 doing some other stuff" _has_run.append(1) for i in range(10): my_function1() my_function2() my_function1(_has_run=[]) # force it to run # output: # my_function1 doing stuff # my_function2 doing some other stuff # my_function1 doing stuff 

这可以通过@gnibbler在他的回答中使用一个迭代器(Python 2.2中引入的)来进一步简化。

 from itertools import count def my_function3(_count=count()): if _count.next(): return print "my_function3 doing something" for i in range(10): my_function3() # my_function3 doing something 

这是一个不涉及函数重新分配的答案,但仍然可以防止需要进行这个丑陋的“首先”检查。

Python 2.5及以上版本支持__missing__

 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 

假设为什么myFunction()不能在循环之前调用是有原因的

 from itertools import count for i in count(): if i==0: myFunction() 

这里有一个明确的方法来对它进行编码,其中哪些函数的状态被保存在本地(所以避免了全局状态)。 我不太喜欢在其他答案中提出的非显式forms:看到f()并不意味着f()被调用,这太令人吃惊了。

这可以通过使用在dict中查找关键字的dict.pop来实现,从dict中移除关键字,并且在没有find关键字的情况下采用缺省值。

 def do_nothing(*args, *kwargs): pass # A list of all the functions you want to run just once. actions = [ my_function, other_function ] actions = dict((action, action) for action in actions) while True: if some_condition: actions.pop(my_function, do_nothing)() if some_other_condition: actions.pop(other_function, do_nothing)() 

为什么这与你的代码有什么不同?

 myFunction() while 1: # rest of your code pass 

如果我正确理解更新的问题,这样的事情应该工作

 def function1(): print "function1 called" def function2(): print "function2 called" def function3(): print "function3 called" called_functions = set() while True: n = raw_input("choose a function: 1,2 or 3 ") func = {"1": function1, "2": function2, "3": function3}.get(n) if func in called_functions: print "That function has already been called" else: called_functions.add(func) func() 

一种面向对象的方法,使你的函数成为一个类,也就是“函子”,当每个实例被创build时,它们的实例会自动logging它们是否已经运行。

由于您的更新问题表明您可能需要其中很多,我已经更新了我的答案来处理,通过使用类工厂模式。 这有点不同寻常,而且可能因为这个原因而被低估(尽pipe我们永远不会知道,因为他们从未留下评论)。 它也可以用元类来完成,但并不简单。

 def RunOnceFactory(): class RunOnceBase(object): # abstract base class _shared_state = {} # shared state of all instances (borg pattern) has_run = False def __init__(self, *args, **kwargs): self.__dict__ = self._shared_state if not self.has_run: self.stuff_done_once(*args, **kwargs) self.has_run = True return RunOnceBase if __name__ == '__main__': class MyFunction1(RunOnceFactory()): def stuff_done_once(self, *args, **kwargs): print("MyFunction1.stuff_done_once() called") class MyFunction2(RunOnceFactory()): def stuff_done_once(self, *args, **kwargs): print("MyFunction2.stuff_done_once() called") for _ in range(10): MyFunction1() # will only call its stuff_done_once() method once MyFunction2() # ditto 

输出:

 MyFunction1.stuff_done_once() called MyFunction2.stuff_done_once() called 

注意:通过在其子类中添加一个reset()方法来重置共享has_run属性,可以使函数/类再次执行has_run 。 如果需要,也可以在创buildstuff_done_once()函数和调用方法时将常规参数和关键字parameter passing给stuff_done_once()方法。

而且,是的,根据你添加到你的问题的信息,这是适用的。

解决这个问题。 你只需要改变一些代码:

 run_once = 0 while run_once < 1: print("Hello") run_once = 1 

如果条件检查只需要在循环中进行一次,那么有一个标志表示已经运行了该function。 在这种情况下,你使用了一个计数器,一个布尔variables就可以工作。

 signal = False count = 0 def callme(): print "I am being called" while count < 2: if signal == False : callme() signal = True count +=1 

我不确定我是否理解你的问题,但我认为你可以分开循环。 在function的一部分和没有它的部分,并保存两个循环。