2010-01-06 22 views
11

Pregunté anteriormente cómo funciona el nested functions, pero desafortunadamente todavía no lo entiendo. Para entenderlo mejor, ¿alguien puede mostrar algunos ejemplos prácticos de uso real de funciones anidadas?ejemplos del mundo real de funciones anidadas

Muchas gracias

+1

¿Es el hecho de que la función interna conserva los valores de las variables en el alcance adjunto que no obtiene? – Skilldrick

Respuesta

10

Su pregunta me causó curiosidad, así que parecía en algún código en el mundo real: la biblioteca estándar de Python. Encontré 67 ejemplos de funciones anidadas. Aquí hay algunos, con explicaciones.

Una razón muy simple para usar una función anidada es simplemente que la función que está definiendo no necesita ser global, porque solo la función envolvedora la usa. Un ejemplo típico de quopri.py módulo de la biblioteca estándar de Python:

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 

Aquí había un poco de código común dentro de la función encode, por lo que el autor se limita a factorizar a cabo en una función write.


Otro uso común para las funciones de anidado es re.sub.Aquí hay algo de código desde el módulo json/encode.py biblioteca estándar:

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) + '"' 

aquí ESCAPE es una expresión regular, y ESCAPE.sub(replace, s) encuentra todos los partidos de ESCAPE en s y reemplaza cada uno con replace(match).


De hecho, cualquier API, como re.sub, que acepta una función como un parámetro puede conducir a situaciones en las funciones anidadas son convenientes. Por ejemplo, en turtle.py hay algo de código de demostración tonta que hace esto:

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

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

onclick que espera para pasar una función de controlador de eventos, por lo que definen uno y lo pasan en

+4

estos son buenos ejemplos, pero todos podrían escribirse como funciones globales sin pérdida de funcionalidad. las funciones anidadas son más que azúcar sintáctico! debería dar algunos ejemplos donde son necesarios – Claudiu

+0

También vale la pena señalar que las funciones anidadas agregan sobrecarga. Cada vez que se llama a la función externa, Python crea una nueva instancia de la función interna. En muchos casos esto no importa, pero a veces lo hace. –

8

Decorators son un uso muy popular para las funciones anidadas. Aquí hay un ejemplo de un decorador que imprime una declaración antes y después de cualquier llamada a la función decorada.

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

¡Maldición! Me ganaste: P – Skilldrick

1

Python Decorators

Esto es en realidad otro de los temas que aprender, pero si nos fijamos en las cosas en 'Uso de las funciones como decoradores', verá algunos ejemplos de funciones anidadas.

3

Las funciones anidadas evitan saturar otras partes del programa con otras funciones y variables que solo tienen sentido localmente.

Una función que la de regreso números de Fibonacci se podría definir como sigue:

>>> 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] 

EDIT: En la práctica, los generadores sería una mejor solución para esto, pero el ejemplo se muestra cómo aprovechar las funciones anidadas.

+1

Probablemente valga la pena señalar que cada vez que se llama a fib, se crea un nuevo objeto de función rec. En casos de limpieza de código, esto a menudo puede ser indeseable. Para cosas como decoradores, es básicamente necesario. –

2

Solo he tenido que usar funciones anidadas al crear decoradores. Una función anidada es básicamente una forma de agregar algún comportamiento a una función sin saber a qué función le está agregando comportamiento.

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) 

ignorar toda la jerigonza en la función "printCall" para este momento y se centran sólo la función "sayHello" y por debajo. Lo que estamos haciendo aquí es que queremos imprimir cómo se llamaba a la función "sayHello" cada vez que se llama sin conocer o alterar lo que hace la función "sayHello". Así que redefinimos la función "sayHello" pasándola a "printCall", que devuelve una NUEVA función que hace lo que hace la función "sayHello" Y imprime cómo se llamó a la función "sayHello". Este es el concepto de decoradores.

Poner "@printCall" por encima de la definición sayHello consigue el mismo efecto:

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

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

Me di cuenta de que "getArgKwargStrings" también es una función anidada. Está anidado porque solo es necesario desde la función "printCall" y no necesita accederse de lo contrario. – manifest

+0

Como el Sr. Fooz mencionó anteriormente en otro ejemplo, la función anidada "getArgKwargStrings" dentro de "printCall" se definirá cada vez que se llame a la función "printCall", que puede o no ser deseable. – manifest

2

Son útiles cuando se utilizan. funciones que toman otras funciones como entrada. Digamos que estás en una función, y desea ordenar una lista de elementos basados ​​en el valor de los ítems en un diccionario:

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

Sólo puede definir clave allí mismo y lo han utilizar Vals, una variable local.

Otro caso de uso es devoluciones de llamada.

1

OK, además de decoradores: Supongamos que tiene una aplicación donde necesita ordenar una lista de cadenas basadas en subcadenas que varían de vez en cuando. Ahora las funciones sorted toman un argumento key= que es una función de un argumento: los elementos (cadenas en este caso) que se ordenarán. Entonces, ¿cómo decirle a esta función qué subcadenas ordenar? Un cierre o función anidada, es perfecto para esto:

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

simple eh? Puede expandir esto al encapsular el inicio y la detención en una tupla o un objeto de corte y luego pasar una secuencia o iterable de estos a sort_key_factory.

2

Otro ejemplo más (muy simple). Una función que devuelve otra función. Observe cómo la función interna (que se devuelve) puede usar variables del alcance de la función externa.

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 
Cuestiones relacionadas