2010-01-06 92 views
11

的例子,我以前問怎麼nested functions工作,但不幸的是我還是不太明白。爲了更好地理解它,有人可以展示一些嵌套函數的實際使用實例嗎?現實世界的嵌套函數

非常感謝

+1

內部函數是否將封閉範圍內的變量值保留下來,這是事實嗎? – Skilldrick 2010-01-06 23:32:32

回答

10

你的問題讓我很好奇,所以我看了一些現實世界中的代碼:Python標準庫。我發現了67個嵌套函數的例子。這裏有一些解釋。

一個很簡單的理由來使用嵌套功能很簡單,就是你定義的功能並不需要是全球性的,因爲只有封閉函數使用它。從Python的quopri.py標準庫模塊一個典型的例子:

def encode(input, output, quotetabs, header = 0): 
    ... 
    def write(s, output=output, lineEnd='\n'): 
     # RFC 1521 requires that the line ending in a space or tab must have 
     # that trailing character encoded. 
     if s and s[-1:] in ' \t': 
      output.write(s[:-1] + quote(s[-1]) + lineEnd) 
     elif s == '.': 
      output.write(quote(s) + lineEnd) 
     else: 
      output.write(s + lineEnd) 

    ... # 35 more lines of code that call write in several places 

在這裏有是encode函數中一些常見的代碼,所以筆者簡單地分解出來進入write功能。


另一個常見用途爲嵌套函數是re.sub。下面是來自json/encode.py標準庫模塊的一些代碼:

def encode_basestring(s): 
    """Return a JSON representation of a Python string 

    """ 
    def replace(match): 
     return ESCAPE_DCT[match.group(0)] 
    return '"' + ESCAPE.sub(replace, s) + '"' 

這裏ESCAPE是一個正則表達式,ESCAPE.sub(replace, s)發現的ESCAPE所有比賽中s和替換每一個與replace(match)


事實上,任何API,像re.sub,它接受一個函數作爲參數可能會導致出現嵌套函數是便利的情況。例如,在turtle.py有一些愚蠢的演示代碼,這是否:

def baba(xdummy, ydummy): 
     clearscreen() 
     bye() 

    ... 
    tri.write(" Click me!", font = ("Courier", 12, "bold")) 
    tri.onclick(baba, 1) 

onclick希望你傳遞一個事件處理函數,所以我們定義一個,並通過它在

+4

這些都是很好的例子,但它們都可以寫成全局函數而不會丟失任何功能。嵌套函數不僅僅是語法糖!你應該在需要的地方給出一些例子 – Claudiu 2010-06-05 13:57:14

+0

也值得注意的是,嵌套函數會增加開銷。每次調用外部函數時,Python都會創建一個全新的內部函數實例。在很多情況下,這並不重要,但有時候確實如此。 – 2016-05-13 14:59:02

8

Decorators是嵌套函數非常流行的應用。這是一個裝飾器的例子,它在對裝飾函數進行任何調用之前和之後都會打印語句。

def entry_exit(f): 
    def new_f(*args, **kwargs): 
     print "Entering", f.__name__ 
     f(*args, **kwargs) 
     print "Exited", f.__name__ 
    return new_f 

@entry_exit 
def func1(): 
    print "inside func1()" 

@entry_exit 
def func2(): 
    print "inside func2()" 

func1() 
func2() 
print func1.__name__ 
+0

該死的!你擊敗了我:P – Skilldrick 2010-01-06 23:29:46

1

Python Decorators

其實,這是另外一個話題去學習,但如果你看的東西上「使用功能的裝飾」,你會看到的嵌套函數的一些例子。

3

嵌套函數避免混亂與其他函數和變量只能使局部意義上的程序的其他部分。

返回斐波那契數的函數可以被定義如下:

>>> def fib(n): 
     def rec(): 
      return fib(n-1) + fib(n-2) 

     if n == 0: 
      return 0 
     elif n == 1: 
      return 1 
     else: 
      return rec() 

>>> map(fib, range(10)) 
[0, 1, 1, 2, 3, 5, 8, 13, 21, 34] 

編輯:在實踐中,發電機將是這一個更好的解決方案,但該示例展示瞭如何利用嵌套函數的優勢。

+1

可能值得注意的是,每次調用fib時,都會創建一個新的rec函數對象。在代碼清理的情況下,這通常是不可取的。對於裝飾者來說,這基本上是必要的。 – 2010-01-06 23:49:28

2

我只有創建裝飾時使用嵌套函數。嵌套函數基本上是向函數中添加某些行爲的一種方式,而無需知道要添加行爲的函數。

from functools import wraps 
from types import InstanceType 



def printCall(func): 
    def getArgKwargStrings(*args, **kwargs): 
     argsString = "".join(["%s, " % (arg) for arg in args]) 
     kwargsString = "".join(["%s=%s, " % (key, value) for key, value in kwargs.items()]) 
     if not len(kwargs): 
     if len(argsString): 
      argsString = argsString[:-2] 
     else: 
     kwargsString = kwargsString[:-2] 
     return argsString, kwargsString 

    @wraps(func) 
    def wrapper(*args, **kwargs): 
     ret = None 
     if args and isinstance(args[0], InstanceType) and getattr(args[0], func.__name__, None): 
     instance, args = args[0], args[1:] 
     argsString, kwargsString = getArgKwargStrings(*args, **kwargs) 
     ret = func(instance, *args, **kwargs) 
     print "Called %s.%s(%s%s)" % (instance.__class__.__name__, func.__name__, argsString, kwargsString) 
     print "Returned %s" % str(ret) 
     else: 
     argsString, kwargsString = getArgKwargStrings(*args, **kwargs) 
     ret = func(*args, **kwargs) 
     print "Called %s(%s%s)" % (func.__name__, argsString, kwargsString) 
     print "Returned %s" % str(ret) 
     return ret 
    return wrapper 


def sayHello(name): 
    print "Hello, my name is %s" % (name) 

if __name__ == "__main__": 
    sayHelloAndPrintDebug = printCall(sayHello) 
    name = "Nimbuz" 
    sayHelloAndPrintDebug(name) 

忽略了「printCall」功能的所有魔神的現在和重點只有「sayHello的」功能及以下。我們在這裏做的是我們想要打印出每次調用「sayHello」函數時如何調用,而不知道或改變「sayHello」函數的作用。因此,我們通過將「sayHello」函數傳遞給「printCall」來重新定義「sayHello」函數,該函數返回一個NEW函數,該函數執行「sayHello」函數的功能並打印「sayHello」函數的調用方式。這是裝飾者的概念。

把「@printCall」的定義的sayHello上面完成同樣的事情:

@printCall 
def sayHello(name): 
    print "Hello, my name is %s" % (name) 

if __name__ == "__main__": 
    name = "Nimbuz" 
    sayHello(name) 
+0

我意識到「getArgKwargStrings」也是一個嵌套函數。它是嵌套的,因爲它只需要在「printCall」函數中使用,否則不需要訪問。 – manifest 2010-01-06 23:52:52

+0

正如Fooz先生在另一個例子中提到的那樣,每次調用「printCall」函數時,「printCall」中嵌套的「getArgKwargStrings」函數都會被定義,這可能也可能不合意。 – manifest 2010-01-06 23:57:46

2

他們使用的時候非常有用。將其他功能作爲輸入的功能。說你是在一個函數,並且要排序的字典基礎上,項目的價值的項目清單:

def f(items): 
    vals = {} 
    for i in items: vals[i] = random.randint(0,100) 
    def key(i): return vals[i] 
    items.sort(key=key) 

你可以只定義關鍵在那裏,並用它瓦爾斯,一個局部變量。

另一個用例是回調函數。

1

好的,除了裝飾者:假設你有一個應用程序,你需要根據不斷變化的子串排序一個字符串列表。現在,sorted函數採用key=參數,該參數是一個參數的函數:要排序的項目(在本例中爲字符串)。那麼如何判斷這個子函數需要排序呢?封閉或嵌套功能,非常適合:

def sort_key_factory(start, stop): 
    def sort_key(string): 
     return string[start: stop] 
    return sort_key 

簡單的eh?您可以通過在元組或片段對象中封裝開始和停止,然後將這些序列或迭代傳遞給sort_key_factory來擴展此功能。

2

又一個(非常簡單)的例子。一個返回另一個函數的函數。請注意內部函數(返回的)如何使用外部函數範圍內的變量。

def create_adder(x): 
    def _adder(y): 
     return x + y 
    return _adder 

add2 = create_adder(2) 
add100 = create_adder(100) 

>>> add2(50) 
52 
>>> add100(50) 
150