2012-09-04 9 views
5

Recientemente he estado haciendo cosas como esta:estilo de código - 'esconde' funciones dentro de otras funciones

import Tkinter 
class C(object): 
    def __init__(self): 
     self.root = Tkinter.Tk() 
     def f(): 
      print 'hello' 
     self.button = Tkinter.Button(master=self.root, command=f, text='say hello') 

en contraposición a algo como esto:

import Tkinter 
class C(object): 
    def __init__(self): 
     self.root = Tkinter.Tk() 
     self.button = Tkinter.Button(master=self.root, command=self.f, text='say hello') 
    def f(self): 
     print 'hello' 

La cuestión no es específica a Tkinter, pero es un buen ejemplo. La función f solo se utiliza como devolución de llamada para el botón, por lo que he decidido definirla dentro de __init__. De esta forma, solo el código dentro de __init__ sabe incluso sobre la existencia de f: los ámbitos externos no se saturan de nombres, y el usuario no necesita preocuparse por una carga de métodos diseñados para ser internos.

Mi pregunta es: ¿se considera esto como un buen estilo? Me preocupa porque tengo una clase de GUI con un montón de botones: __init__ está empezando a parecer muy larga, con muchas definiciones de funciones locales. ¿Hay alguna alternativa más apropiada que debería usar?

Respuesta

8

La forma típica de hacer algo como esto en el contexto de Tkinter es utilizar la función lambda.

self.button = Tkinter.Button(master=self.root, 
          command=lambda:sys.stdout.write("Hello!\n"), 
          text='say hello') 

En su núcleo, esto es realmente lo mismo que el primer ejemplo, así que si usted prefiere la primera forma - ir con ella. Creo que generalmente no es idiomático crear un nuevo método en este caso (a menos que realmente necesite que la instancia pase a la devolución de llamada, en cuyo caso, debería hacerlo de la segunda manera).

Hay dos cosas de las que preocuparse aquí. El primero es la legibilidad. Si __init__ se llena demasiado para leer, entonces usted tiene un problema. Puede mover esas funciones al nivel del módulo (con un prefijo _ para evitar que se importen en el contexto from module import *) y use lambda para vincular las variables locales como argumentos a esas funciones si es necesario.

Si __init__ no se abarrota, no dude en poner las funciones allí. Tener las funciones allí significa que se crea una nueva función cada vez que se crea una nueva instancia (lo mismo con lambda) que supongo que podría ser un desperdicio en lo que a memoria se refiere, pero no debería ser tan grande trato (especialmente en una GUI).

La segunda cosa de la que preocuparse es el espacio de nombres abarrotado. Sin embargo, si su módulo es tan grande que mover esas funciones locales a funciones de nivel de módulo crea un problema de espacio de nombres, entonces su módulo es demasiado grande para empezar. Siempre que prefija las funciones con guiones bajos (consulte la sugerencia anterior), tampoco debería tener problemas de espacio de nombres importando este módulo en otros.

+2

Tienes razón, por supuesto, pero en la versión del mundo real de mi ejemplo, 'f' está haciendo algo más que 'imprimir' hola''. No cabe dentro de una función lambda. –

+0

@poorsod: si no está utilizando la instancia, entonces no debería ser un método. Puede convertirlo en una función de nivel de módulo, o puede seguir haciéndolo tal como está ahora, pero no usaría un método (o incluso un método estático) si no necesita la instancia en la función. – mgilson

+1

@mgilson: Creo que se pierde el sentido de la pregunta, que si las devoluciones de llamada deberían ser funciones no locales. La razón por la que ves lambda con tanta frecuencia en las devoluciones de llamada de Tkinter es unir los argumentos locales a la devolución de llamada. La devolución de llamada a menudo se define en otro lugar. –

2

En este caso, es inofensivo. En general, sin embargo, me gustaría evitarlo por dos razones:

1) La definición de las devoluciones de llamada como un miembro de la clase es algo más fácil de leer (sobre todo cuando se está utilizando para investigar pydoc)

2) Crear la función dentro de otro introduce más cierres (variables heredadas del contexto de llamada).

+1

No estaría de más llevar esto más allá y definir las devoluciones de llamada en otro módulo, separando la interfaz gráfica de usuario de la lógica de la aplicación. –

Cuestiones relacionadas