2010-01-06 65 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