2012-07-22 78 views
14

如果我在返回結果後可以在函數中做更多的工作,我有點好奇。基本上我做了一個網站,使用金字塔框架(這是簡單的編碼在Python中)處理輸入後我返回變量來呈現頁面,但有時我想在我呈現頁面後做更多的工作。在退貨聲明之後有辦法做更多工作嗎?

例如,您來到我的網站並更新您的個人資料,所有您關心的是它的成功,所以我輸出一條消息說'成功'!但在此之後,我想要更新我的活動日誌並更新您的活動日誌,更新您的朋友活動流等。現在,我正在做所有這些,然後再返回您關心的結果狀態,我好奇,如果我可以做到這一點後,所以用戶更快地得到他們的反應。

我已經做過多處理之前和最壞的情況下,我可能只是叉一個線程來完成這項工作,但如果有一種方法來做一個返回語句後工作,那將更簡單。

例如:

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

我對python的線程並不熟悉,但是大多數線程模型中,啓動一個線程就像調用一個函數一樣簡單。複雜性來自於確保線程中完成的工作與線程異步發生的工作正確同步。在我看來,複雜性會與你在'#do維護處理現在......'後返回步驟中想要發生的任何程度一樣存在。如果不需要同步,那麼線程中就不需要任何同步。但是,反過來也是如此。 – 2012-07-23 16:09:36

回答

9

不,不幸的是,一旦你打的return語句,從功能/方法(有或沒有返回值)返回。

docs for return

返回離開當前函數調用的表達式列表(或無)的返回值。

你可能要考慮發電機的功能和yield聲明,這是從一個函數返回一個值,繼續處理和準備另一個值可以返還的方式,當函數被調用,下一次。

+0

有趣,沒有想到yield。我認爲它被用來返回一個值給函數,而不是它的迴應。基本上我以字典的形式返回一個http響應。我無法更改HTTPS如何接受退貨,但是我會進行一些挖掘並查看是否可以用yield來替換我的命令。有趣的想法,謝謝。 – Lostsoul 2012-07-22 23:11:51

+0

我不認爲收益會對此有所幫助。使用yield的函數會生成一系列值,因爲它們是通過對調用結果進行迭代所需的值。調用者將不得不被修改以期望函數中的序列,將第一個項目作爲響應,然後請求更多的值以允許第一個yield之後的代碼運行。 – Ben 2012-07-23 01:55:44

+0

@Ben但是很明顯,調用者必須指明何時應該運行維護的東西,因爲如果每次都罰款,我們可以在返回之前運行它。無論如何,我們需要兩個調用,這就是協程需要的。 – Voo 2012-07-23 02:36:48

13

爲什麼不使用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電話去停止迭代(和更清晰的語法)的優勢,但如果你想返回多個值什麼的,你也可以做這種方式,但你可以看到,屈服後聲明並沒有真正得到調用,直到發電機引發的next通話的StopIteration


如果由於某種原因,你需要一個更高的程度(當它得到StopIteration for循環結束)的控制比@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 
+0

@JoelCornett,很好的編輯。這是一個很好的觀點,「@ contextmanager」只是更復雜的類的語法糖。 – 2012-07-22 23:33:59

+0

我想你的例子中'q'應該是'[0,1,2,3,4,5,6,7,8,9]'不是'[0,1,2,3,4]' – 2012-07-23 00:06:31

+0

@ AlexandrPriymak是的,我在我的機器上運行時更改了它,我會修復它。 – 2012-07-23 00:26:55

6

你仍然可以做回後的一些工作,如果你從try塊返回,最終塊將仍然來執行,例如:

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

報價the docs

當返回傳遞出控制在try語句的一個終於 子句,該子句在真正離開 函數之前執行。

+5

但是在調用者收到返回值之前,finally仍會運行。這不會幫助您允許調用方接收響應並通知用戶,但*然後*運行更多代碼。 – Ben 2012-07-23 01:58:01

+2

根據用例,這可能是好的。它可能是一種方便的流程控制技術,可以在函數的內容中進行包裝,並且有各種不同的返回語句,所有這些語句都會將流程直接拖放到finally塊以執行維護。 – andyortlieb 2014-04-23 15:41:59

3

不,返回將值返回給調用者並停止。

如果主叫方(S)也是你的控制(而不是金字塔框架的一部分)下,你可以改變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確定之後需要執行什麼代碼(並且保留這​​些細節隱藏並從較高級別封裝),但允許更高級別確定向用戶傳遞響應的流程,然後執行後臺處理。

+0

+1好主意,我會做一些測試,但非常非常有趣的思考這個問題的方式! – Lostsoul 2012-07-23 03:57:20

+0

呵呵。我不知道Python可以做到這一點。非常感謝你! – 2017-12-03 06:14:44

-1

可以用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語句將被執行,甚至。

3
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的線程功能。

首先我們調用update函數(如果需要的話也可以在線程中使用這個函數,如果響應不依賴於這個函數,並且你需要立即給出響應)。

然後我們創建了一個線程,完成維護功能並在完成後停止。但是,該功能完成前,響應不會被延遲。

ie:返回「它工作」將返回,然後該線程維護功能的維護工作如果ts位處理。