2009-10-19 19 views
63

¿Qué beneficios o consecuencias podríamos obtener con el código Python como esto:función anidada en Python

class some_class(parent_class): 
    def doOp(self, x, y): 
     def add(x, y): 
      return x + y 
     return add(x, y) 

He encontrado esto en un proyecto de código abierto, haciendo algo útil dentro de la función anidada, pero sin hacer absolutamente nada fuera excepto llamarlo. (El código real se puede encontrar en here). ¿Por qué alguien podría codificarlo así? ¿Hay algún beneficio o efecto secundario para escribir el código dentro de la función anidada en lugar de en la función externa normal?

+4

Es este el código real que encontró, o simplemente un ejemplo simplificado se construye? – MAK

+0

Es un ejemplo simplificado. El código real se puede encontrar aquí: http://bazaar.launchpad.net/%7Eopenerp/openobject-server/trunk/annotate/head%3A/bin/report/render/rml2pdf/trml2pdf.py#L685 –

+2

Tu enlace (que apunta a HEAD) ahora no es preciso. Pruebe: http://bazaar.launchpad.net/~openerp/openobject-server/trunk/annotate/1861/bin/report/render/rml2pdf/trml2pdf.py#L685 –

Respuesta

85

Normalmente lo haces para hacer closures:

def make_adder(x): 
    def add(y): 
     return x + y 
    return add 

plus5 = make_adder(5) 
print(plus5(12)) # prints 17 

funciones internas pueden acceder a las variables del ámbito de inclusión (en este caso, la variable local x) . Si no está accediendo a ninguna variable desde el alcance adjunto, en realidad son solo funciones ordinarias con un alcance diferente.

+4

Para eso prefiero parciales: 'plus5 = functools.partial (operator.add, 5)'. Los decoradores serían un mejor ejemplo para cierres. –

+3

Gracias, pero como puede ver en el fragmento que publiqué, ese no es el caso aquí: simplemente se llama a la función anidada en la función externa. –

8

No puedo imaginar ninguna buena razón para un código como ese.

Quizás haya una razón para la función interna en las revisiones anteriores, como otras Ops.

Por ejemplo, esto hace ligeramente más sentido:

class some_class(parent_class): 
    def doOp(self, op, x, y): 
     def add(x, y): 
      return x + y 
     def sub(x,y): 
      return x - y 
     return locals()[op](x,y) 

some_class().doOp('add', 1,2) 

pero entonces la función interna debería ser ("privado") métodos de la clase en su lugar:

class some_class(object): 
    def _add(self, x, y): 
     return x + y 
    def doOp(self, x, y): 
     return self._add(x,y) 
+0

Sí, quizás doOp tomó una cadena para especificar qué operador usar en los argumentos ... – Skilldrick

+4

no debería usar clases en python simplemente para organizar funciones ... – aehlke

+0

@aehlke - ¿por qué no? – ArtOfWarfare

6

La idea detrás de los métodos locales es similar a las variables locales: no contamine el espacio de nombre más grande. Obviamente, los beneficios son limitados, ya que la mayoría de los idiomas tampoco proporcionan dicha funcionalidad directamente.

1

¿Estás seguro de que el código era exactamente así? La razón normal para hacer algo como esto es crear una función parcial, una función con parámetros integrados. Llamar a la función externa devuelve un llamativo que no necesita parámetros, por lo que puede almacenarse y usarse en algún lugar donde es imposible pasar parámetros. Sin embargo, el código que ha publicado no hará eso; llama a la función inmediatamente y devuelve el resultado, en lugar del llamante. Puede ser útil publicar el código real que vio.

+1

He agregado un enlace al código original en un comentario sobre mi pregunta. Como pueden ver, el mío es un ejemplo simplificado, pero sigue siendo casi el mismo. –

44

Además de los generadores de función, donde la creación de funciones internas es casi la definición de un generador de funciones, la razón por la que creo funciones anidadas es para mejorar la legibilidad. Si tengo una pequeña función que solo será invocada por la función externa, entonces alinearé la definición para que no tenga que saltar para determinar qué está haciendo esa función. Siempre puedo mover el método interno fuera del método de encapsulado si encuentro la necesidad de reutilizar la función más adelante. ejemplo

juguete:

import sys 

def Foo(): 
    def e(s): 
     sys.stderr.write('ERROR: ') 
     sys.stderr.write(s) 
     sys.stderr.write('\n') 
    e('I regret to inform you') 
    e('that a shameful thing has happened.') 
    e('Thus, I must issue this desultory message') 
    e('across numerous lines.') 
Foo() 
+2

El único problema es que a veces esto puede dificultar la prueba de la unidad, porque no puede acceder a la función interna. – Challenger5

+1

¡Se ven tan hermosos! – taper

21

Una posible ventaja de usar métodos internos es que le permite usar variables locales de método externo sin pasarlas como argumentos.

def helper(feature, resultBuffer): 
    resultBuffer.print(feature) 
    resultBuffer.printLine() 
    resultBuffer.flush() 

def save(item, resultBuffer): 

    helper(item.description, resultBuffer) 
    helper(item.size, resultBuffer) 
    helper(item.type, resultBuffer) 

se puede escribir de la siguiente manera, que lee sin duda mejor

def save(item, resultBuffer): 

    def helper(feature): 
    resultBuffer.print(feature) 
    resultBuffer.printLine() 
    resultBuffer.flush() 

    helper(item.description) 
    helper(item.size) 
    helper(item.type) 
Cuestiones relacionadas