使函数的有效方式仅在循环中执行一次

Mar*_*son 62 python

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

run_once = 0
while 1:
    if run_once == 0:
        myFunction()
        run_once = 1:
Run Code Online (Sandbox Code Playgroud)

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

我正在寻找的是按需执行一次函数.例如,按下某个按钮.它是一个交互式应用程序,具有许多用户控制的开关.每个交换机都有一个垃圾变量,只是为了跟踪它是否已经运行,似乎效率低下.

aar*_*ing 102

我会在函数上使用一个装饰器来处理它运行的次数.

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
Run Code Online (Sandbox Code Playgroud)

现在my_function只运行一次.其他调用将返回None.如果您希望它返回其他内容,只需添加一个else子句即可if.从您的示例中,它不需要返回任何内容.

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

action = run_once(my_function)
while 1:
    if predicate:
        action()
Run Code Online (Sandbox Code Playgroud)

这将留给my_function其他用途.

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

action = run_once(my_function)
action() # run once the first time

action.has_run = False
action() # run once the second time
Run Code Online (Sandbox Code Playgroud)

  • 如何使用装饰器的经典示例.做得很好! (5认同)
  • @Marcus Ottosson` @`char使它成为装饰者.评论太长,无法解释这是什么.在这里,有[link](http://www.artima.com/weblogs/viewpost.jsp?thread=240808).无论如何,您可能只想使用我提供的第二种形式,而不是手动应用它.您应该仍然知道装饰器是什么. (4认同)
  • 值得一提的是,这也适用于类的方法。 (2认同)

j b*_*j b 17

另一种选择是设置func_code 代码对象为你的函数是一个什么也不做的函数代码对象.这应该在函数体的末尾完成.

例如:

def run_once():  
   # Code for something you only want to execute once
   run_once.func_code = (lambda:None).func_code
Run Code Online (Sandbox Code Playgroud)

这里run_once.func_code = (lambda:None).func_code使用lambda:None的代码替换函数的可执行代码,因此所有后续调用都run_once()不会执行任何操作.

这种技术不如接受的答案中提出的装饰器方法灵活,但如果您只想运行一次,则可能更简洁.

  • 在 Python 3 上,`run_once.__code__ = (lambda: None).__code__`。 (6认同)

Raf*_*ler 7

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

myFunction()
while True:
    # all the other code being executed in your loop
Run Code Online (Sandbox Code Playgroud)

这是显而易见的解决方案.如果不仅仅满足于眼睛,那么解决方案可能会更复杂一些.

  • 这不会被削减是因为OP声明"例如,按下某个按钮.它是一个交互式应用程序,有很多用户控制的开关." 所以OP希望函数可以在主运行循环内运行一次. (5认同)

mar*_*eau 6

我想到了另一种\xe2\x80\x94有点不寻常,但非常有效的\xe2\x80\x94方法来做到这一点,不需要装饰器函数或类。相反,它只使用可变关键字参数,这应该适用于大多数版本的 Python。大多数情况下,这些都是需要避免的,因为通常您不希望默认参数值从 call-to-call\xe2\x80\x94 更改,但在这种情况下可以利用这种能力并将其用作廉价存储机制。这是它的工作原理:

\n\n
def my_function1(_has_run=[]):\n    if _has_run: return\n    print("my_function1 doing stuff")\n    _has_run.append(1)\n\ndef my_function2(_has_run=[]):\n    if _has_run: return\n    print("my_function2 doing some other stuff")\n    _has_run.append(1)\n\nfor i in range(10):\n    my_function1()\n    my_function2()\n\nprint(\'----\')\nmy_function1(_has_run=[])  # Force it to run.\n
Run Code Online (Sandbox Code Playgroud)\n\n

输出:

\n\n
def my_function1(_has_run=[]):\n    if _has_run: return\n    print("my_function1 doing stuff")\n    _has_run.append(1)\n\ndef my_function2(_has_run=[]):\n    if _has_run: return\n    print("my_function2 doing some other stuff")\n    _has_run.append(1)\n\nfor i in range(10):\n    my_function1()\n    my_function2()\n\nprint(\'----\')\nmy_function1(_has_run=[])  # Force it to run.\n
Run Code Online (Sandbox Code Playgroud)\n\n

通过执行 @gnibbler 在他的答案中建议的操作并使用迭代器(在 Python 2.2 中引入),可以进一步简化这一点:

\n\n
from itertools import count\n\ndef my_function3(_count=count()):\n    if next(_count): return\n    print("my_function3 doing something")\n\nfor i in range(10):\n    my_function3()\n\nprint(\'----\')\nmy_function3(_count=count())  # Force it to run.\n
Run Code Online (Sandbox Code Playgroud)\n\n

输出:

\n\n
my_function1 doing stuff\nmy_function2 doing some other stuff\n----\nmy_function1 doing stuff\n
Run Code Online (Sandbox Code Playgroud)\n


Rya*_*rom 5

我假设这是一个你想要最多执行一次的动作,如果满足某些条件.由于您不会总是执行操作,因此无法在循环外无条件执行操作.如果你收到请求,就像懒惰地检索一些数据(并缓存它),但是否则不会检索它.

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()
Run Code Online (Sandbox Code Playgroud)

  • 这太过于复杂.但整洁的技巧.我的主要抱怨,我认为是一个严肃的问题,就是'do_something`需要知道它会被"行动"所取代,这似乎违反了某种关注点.我重申,巧妙的伎俩. (3认同)
  • -1.这很麻烦,在较大的代码库中可能会损害可读性.全局变量,*name*`action`,每次都调用一个函数(而不是只调用一次的原始问题). (3认同)
  • `action(); action = lambda:None`在没有额外函数的情况下实现了类似的结果 (2认同)

tzo*_*zot 5

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

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

needs_to_run= expensive_function
while 1:
    …
    if needs_to_run: needs_to_run(); needs_to_run= None
    …
Run Code Online (Sandbox Code Playgroud)