50

我有点好奇在返回结果后是否可以在函数中做更多的工作。基本上,我在处理输入后使用金字塔框架(它只是在 python 中编码)创建一个站点,我返回变量以呈现页面,但有时我想在呈现页面后做更多的工作。

例如,您来到我的网站并更新您的个人资料,而您只关心它是否成功,所以我输出一条消息说“成功!” 但在那之后,我想获取您的更新并更新您的活动日志,更新您的朋友活动流等。现在我正在做所有这些,然后返回您关心的结果状态,但我'我很好奇我是否可以这样做,以便用户更快地得到他们的回复。

我之前做过多处理,最坏的情况下我可能只是分叉一个线程来完成这项工作,但如果有一种方法可以在 return 语句之后完成工作,那就更简单了。

例子:

def profile_update(inputs):
  #take updates and update the database 
  return "it worked"
  #do maintenance processing now.
4

11 回答 11

39

如果你从 try 块返回,你仍然可以在返回后做一些工作,finally 块仍然会被执行,例如:

def fun(x):
    try:
        return x * 20
    finally:
        print("Yay! I still got executed, even though my function has already returned!")

print(fun(5))

预期输出:

Yay! I still got executed, even though my function has already returned!
100

引用文档

当 return 将控制权从带有 finally 子句的 try 语句中传递出来时,该 finally 子句在真正离开函数之前执行。

于 2012-07-23T00:09:02.387 回答
20

你为什么不使用contextmanager?它基本上完全符合您的要求。

这是 Python 文档中的规范示例。

from contextlib import contextmanager

@contextmanager
def tag(name):
    print "<%s>" % name
    yield
    print "</%s>" % name

因此,对于您的功能,您只需执行以下操作:

@contextmanager
def profile_update(inputs):
  #take updates and update the database 
  yield "it worked"
  #do maintainence processing now..

要调用它,您只需执行以下操作:

with profile_update(inputs) as result: #pre-yield and yield here
    # do whatever while in scope
# as you move out of scope of with statement, post-yield is executed

编辑:我只是在测试,结果发现,使用 yield 语句,函数仍然执行到最后。这是一个愚蠢的例子,说明了要点和事情何时执行。

def some_generator(lst):
    for elem in lst:
        yield elem
    lst[0] = "I WAS CHANGED POST-YIELD!!!!"

>>> q = [0, 1, 2, 3, 4, 5, 6, 7, 8, 9]
>>> gen = some_generator(q)
>>> for e in gen:
...    print e, q

0 [0, 1, 2, 3, 4, 5, 6, 7, 8, 9]
1 [0, 1, 2, 3, 4, 5, 6, 7, 8, 9]
2 [0, 1, 2, 3, 4, 5, 6, 7, 8, 9]
3 [0, 1, 2, 3, 4, 5, 6, 7, 8, 9]
4 [0, 1, 2, 3, 4, 5, 6, 7, 8, 9]
5 [0, 1, 2, 3, 4, 5, 6, 7, 8, 9]
6 [0, 1, 2, 3, 4, 5, 6, 7, 8, 9]
7 [0, 1, 2, 3, 4, 5, 6, 7, 8, 9]
8 [0, 1, 2, 3, 4, 5, 6, 7, 8, 9]
9 [0, 1, 2, 3, 4, 5, 6, 7, 8, 9]

print q
['I WAS CHANGED POST YIELD!!!', 1, 2, 3, 4, 5, 6, 7, 8, 9]

contextmanager 的优点是不需要两次next调用即可到达停止迭代(和更简洁的语法),但是如果您想返回多个值或其他内容,您也可以这样做,但是您可以看到 post yield 语句直到生成器在调用中引发 StopIteration 才真正被next调用(for 循环在它 get 时结束StopIteration


如果由于某种原因,您需要比 offer 更高程度的控制@contextmanager,您还可以使用__enter____exit__方法定义一个类:

class MyContextClass(object):
    # ...

    def __enter__(self):
        # do some preprocessing
        return some_object

    def __exit__(self, exc_type, exc_value, traceback):
        # do some post processing
        # possibly do some processing of exceptions raised within the block
        if exc_type == MyCustomErrorType:
            return True #don't propagate the error
于 2012-07-22T23:15:27.443 回答
16

不,不幸的是,一旦你点击了return语句,你就会从函数/方法返回(无论有没有返回值)。

文档中返回

return 以表达式列表(或无)作为返回值离开当前函数调用。

您可能需要查看生成器函数和yield语句,这是一种从函数返回值并继续处理并准备在下次调用函数时返回的另一个值的方法。

于 2012-07-22T23:06:10.963 回答
9
import threading

def profile_update(inputs):

    # call function to take updates and update the database 
    update_function(inputs)

    # call the maintainence_function here
    t = threading.Thread(target=maintainence_function, args=[input1, input2])
    # setDaemon=False to stop the thread after complete
    t.setDaemon(False)
    # starting the thread
    t.start()

    # return response/anything-else you want to return
    return "it worked"



def update_function(inputs):
    # updating the database process here

def maintainence_function(input1, input2):
    #do maintainence processing now..

这里我们使用python的线程功能。

首先我们调用更新函数(如果需要,如果响应不依赖于该函数并且需要立即给出响应,您也可以在线程中使用此函数)。

然后我们创建了一个线程,它将完成maintainence_function函数并在完成后停止。但响应不会延迟,直到该功能完成。

即: return "it working" 将被返回,然后如果 ts 是一个位过程,线程也会维护函数maintainence_function 的工作。

于 2017-12-27T11:12:50.267 回答
5

不,返回将值返回给调用者并停止。

如果调用者也在您的控制之下(不是金字塔框架的一部分),您可以更改profile_updates为如下所示:

def profile_update(inputs):
    #take updates and update the database 
    def post_processing_task():
        #do maintainence processing now..
    return ("it worked", post_processing_task)

然后对调用者进行编码以期望一对(response, task),而不仅仅是响应。它可以立即对部件执行某些操作response(将其传达给用户),然后调用task()以处理后处理。

这允许profile_update确定之后需要执行哪些代码(并将这些细节隐藏并从更高级别封装),但允许更高级别确定将响应传达给用户然后执行后处理的流程背景。

于 2012-07-23T02:06:02.990 回答
3

我最喜欢神道约瑟夫的回答,我们使用线程来拆分另一个进程。然而,我相信这个例子更好地体现了他的想法:

import threading
from time import sleep

def math_fun(x):
    # The sleep here is simply to make it clear that this happens in the background
    sleep(1)
    print(x*20)


def fun(x):
    # Create thread to run math_fun for each argument in x 
    t = threading.Thread(target=math_fun, args=[x])
    t.setDaemon(False)
    t.start()

    print("Function has returned!")

fun(5)

预期输出:

Function has returned!
100
于 2020-08-07T20:11:42.560 回答
2

您可以使用Timer来安排事件在某个时间点异步发生。您还可以给出事件发生的时间间隔。我希望下面的代码和输出有所帮助。

import time
from threading import Timer

def func():
    print("Inside func at", time.time())

def schedule_an_event():
    print(time.time())
    Timer(3, func).start()
    return "Done"

print(schedule_an_event())

输出:

1579682455.5378997
Done
Inside func at 1579682458.5382733
于 2020-01-22T08:43:06.767 回答
0

可以用try-except-finally结构作弊。例子:

def function():
  try:
    #do some stuff here
    return x
  except Exception:
    #do something when an error occures
  finally:
    #do here whatever you wanna do after return

请注意,finally即使捕获到异常,也会执行该语句。

于 2016-10-17T21:14:55.333 回答
0

我认为这就是您要搜索的内容:

def foo(): # do stuff try: return something finally: # 后返回

于 2019-08-03T22:08:32.250 回答
0

您要问的问题是不可能的,因为当您给出 return 语句时,函数范围在此时终止,但您可以尝试不同的方法

def show_status():
  return "it worked"

def profile_update(inputs):
  #take updates and update the database 
  show_status
  #do maintainence processing now..
  return  # when you are done with the maintainence processing 
于 2020-06-10T04:51:20.890 回答
0

意识到这是一个老话题并且已经有了很好的答案,但也许新用户也觉得我的想法很有吸引力:

def master_profile_update(inputs):
    # since input parameters are global inside the upper scope
    # i omit them from the arguments of lower nested function:
    def profile_update()
        #take updates and update the database 
        return "it worked"

    profile_update()
    #do maintenance processing now..

我发现它比decoratorcontextmanager更传统。

于 2021-08-15T15:50:58.717 回答