Python 让函数在循环中只执行一次的有效方法
声明:本页面是StackOverFlow热门问题的中英对照翻译,遵循CC BY-SA 4.0协议,如果您需要使用它,必须同样遵循CC BY-SA许可,注明原文地址和作者信息,同时你必须将它归于原作者(不是我):StackOverFlow
原文地址: http://stackoverflow.com/questions/4103773/
Warning: these are provided under cc-by-sa 4.0 license. You are free to use/share it, But you must attribute it to the original authors (not me):
StackOverFlow
Efficient way of having a function only execute once in a loop
提问by Marcus Ottosson
At the moment, I'm doing stuff like the following, which is getting tedious:
目前,我正在做以下事情,这变得很乏味:
run_once = 0
while 1:
if run_once == 0:
myFunction()
run_once = 1:
I'm guessing there is some more accepted way of handling this stuff?
我猜有一些更被接受的方式来处理这些东西?
What I'm looking for is having a function execute once, on demand. For example, at the press of a certain button. It is an interactive app which has a lot of user controlled switches. Having a junk variable for every switch, just for keeping track of whether it has been run or not, seemed kind of inefficient.
我正在寻找的是让函数按需执行一次。例如,按下某个按钮。它是一个交互式应用程序,有很多用户控制的开关。每个开关都有一个垃圾变量,只是为了跟踪它是否已运行,似乎效率低下。
采纳答案by aaronasterling
I would use a decorator on the function to handle keeping track of how many times it runs.
我会在函数上使用装饰器来跟踪它运行的次数。
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
Now my_functionwill only run once. Other calls to it will return None. Just add an elseclause to the ifif you want it to return something else. From your example, it doesn't need to return anything ever.
现在my_function只会运行一次。对它的其他调用将返回None. 如果您希望它返回其他内容,只需else在 the 中添加一个子句即可if。从你的例子来看,它不需要返回任何东西。
If you don't control the creation of the function, or the function needs to be used normally in other contexts, you can just apply the decorator manually as well.
如果您不控制函数的创建,或者该函数需要在其他上下文中正常使用,您也可以手动应用装饰器。
action = run_once(my_function)
while 1:
if predicate:
action()
This will leave my_functionavailable for other uses.
这将my_function可用于其他用途。
Finally, if you need to only run it once twice, then you can just do
最后,如果你只需要运行一次两次,那么你可以这样做
action = run_once(my_function)
action() # run once the first time
action.has_run = False
action() # run once the second time
回答by Rafe Kettler
Run the function before the loop. Example:
在循环之前运行该函数。例子:
myFunction()
while True:
# all the other code being executed in your loop
This is the obvious solution. If there's more than meets the eye, the solution may be a bit more complicated.
这是显而易见的解决方案。如果有更多的东西,那么解决方案可能会更复杂一些。
回答by Asar
I'm not sure that I understood your problem, but I think you can divide loop. On the part of the function and the part without it and save the two loops.
我不确定我是否理解您的问题,但我认为您可以划分循环。函数部分和没有它的部分并保存两个循环。
回答by Ryan Ginstrom
I'm assuming this is an action that you want to be performed at most one time, if some conditions are met. Since you won't always perform the action, you can't do it unconditionally outside the loop. Something like lazily retrieving some data (and caching it) if you get a request, but not retrieving it otherwise.
如果满足某些条件,我假设这是您最多希望执行一次的操作。由于您不会总是执行操作,因此您不能在循环外无条件地执行操作。如果您收到请求,则类似于懒惰地检索某些数据(并缓存它),否则不会检索它。
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()
回答by John La Rooy
Assuming there is some reason why myFunction()can't be called before the loop
假设有一些原因myFunction()不能在循环之前调用
from itertools import count
for i in count():
if i==0:
myFunction()
回答by J K
If the condition check needs to happen only once you are in the loop, having a flag signaling that you have already run the function helps. In this case you used a counter, a boolean variable would work just as fine.
如果条件检查只在您处于循环中时才需要进行,则使用标志表示您已经运行了该函数会有所帮助。在这种情况下,您使用了计数器,布尔变量也可以正常工作。
signal = False
count = 0
def callme():
print "I am being called"
while count < 2:
if signal == False :
callme()
signal = True
count +=1
回答by tzot
There are many ways to do what you want; however, do note that it is quite possible that —as described in the question— you don't have to call the function inside the loop.
有很多方法可以做你想做的事;但是,请注意,很可能 - 如问题中所述 - 您不必在循环内调用该函数。
If you insist in having the function call inside the loop, you can also do:
如果你坚持在循环内调用函数,你也可以这样做:
needs_to_run= expensive_function
while 1:
…
if needs_to_run: needs_to_run(); needs_to_run= None
…
回答by martineau
One object-oriented approach and make your function a class, aka as a "functor", whose instances automatically keep track of whether they've been run or not when each instance is created.
一种面向对象的方法,使您的函数成为一个类,也称为“函子”,其实例会在创建每个实例时自动跟踪它们是否已运行。
Since your updated question indicates you may need many of them, I've updated my answer to deal with that by using a class factorypattern. This is a bit unusual, and it may have been down-voted for that reason (although we'll never know for sure because they never left a comment). It could also be done with a metaclass, but it's not much simpler.
由于您更新的问题表明您可能需要其中的很多,我已经更新了我的答案以使用类工厂模式来处理这个问题。这有点不寻常,并且可能因此而被否决(尽管我们永远不会确定,因为他们从未发表评论)。也可以用元类来完成,但并没有简单得多。
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
Output:
输出:
MyFunction1.stuff_done_once() called
MyFunction2.stuff_done_once() called
Note: You could make a function/class able to do stuff again by adding a reset()method to its subclass that reset the shared has_runattribute. It's also possible to pass regular and keyword arguments to the stuff_done_once()method when the functor is created and the method is called, if desired.
注意:您可以通过reset()向其子类添加一个重置共享has_run属性的方法,使函数/类能够再次执行操作。如果需要,也可以在stuff_done_once()创建函子并调用方法时将常规和关键字参数传递给方法。
And, yes, it would be applicable given the information you added to your question.
而且,是的,鉴于您添加到问题中的信息,它是适用的。
回答by martineau
Here's an explicit way to code this up, where the state of which functions have been called is kept locally (so global state is avoided). I don't much like the non-explicit forms suggested in other answers: it's too surprising to see f() and for this not to mean that f() gets called.
这是一种明确的编码方式,其中已调用函数的状态保持在本地(因此避免了全局状态)。我不太喜欢其他答案中建议的非显式形式:看到 f() 太令人惊讶了,这并不意味着 f() 被调用。
This works by using dict.pop which looks up a key in a dict, removes the key from the dict, and takes a default value to use in case the key isn't found.
这通过使用 dict.pop 来工作,它在字典中查找键,从字典中删除键,并在找不到键的情况下使用默认值。
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)()
回答by martineau
I've thought of another—slightly unusual, but very effective—way to do this that doesn't require decorator functions or classes. Instead it just uses a mutable keyword argument, which ought to work in most versions of Python. Most of the time these are something to be avoided since normally you wouldn't want a default argument value to change from call-to-call—but that ability can be leveraged in this case and used as a cheap storage mechanism. Here's how that would work:
我想到了另一种——有点不寻常,但非常有效——的方法来做到这一点,不需要装饰器函数或类。相反,它只使用了一个可变的关键字参数,它应该适用于大多数 Python 版本。大多数情况下,这些都是需要避免的,因为通常您不希望默认参数值在一次次呼叫中发生变化——但在这种情况下可以利用这种能力并将其用作廉价的存储机制。这是如何工作的:
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()
print('----')
my_function1(_has_run=[]) # Force it to run.
Output:
输出:
my_function1 doing stuff
my_function2 doing some other stuff
----
my_function1 doing stuff
This could be simplified a little further by doing what @gnibbler suggested in his answerand using an iterator (which were introduced in Python 2.2):
这可以通过执行@gnibbler 在他的回答中建议的操作并使用迭代器(在 Python 2.2 中引入)来进一步简化:
from itertools import count
def my_function3(_count=count()):
if next(_count): return
print("my_function3 doing something")
for i in range(10):
my_function3()
print('----')
my_function3(_count=count()) # Force it to run.
Output:
输出:
my_function3 doing something
----
my_function3 doing something

