具有函数的有效方法只能在循环中执行一次
目前,我正在做类似以下的事情,这是越来越乏味:
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的一部分和没有它的部分,并保存两个循环。