 Vale, bueno, soy Raúl Cumplido, estoy buscando voluntarios para el Auro Python, así que luego habláis conmigo. No he venido a dar una charla, he venido a decir que busco voluntarios, tampoco, tampoco, pero bueno. No, me llamo Raúl, soy de Barcelona, vivo en Londres y trabajo en tequila y voy a hablar un poco de metacoprogramación en Python. Un poco lo que voy a hacer es explicar algunos básicos al principio y luego intentaré poner algún ejemplo un poco más complicado, pero vamos, que hay muchos que sabréis esto y no os voy a enseñar nada que no sepáis y tal, así que vamos. Vale, primero, bueno, para esto se necesita Python 3.4, vale, para muchas cosas, para que funcione. Hay cosas que si utilizas otras versiones anteriores, pues no van a funcionar. Ya os iré explicando en qué partes, por ejemplo, pues Python 3.3 ya no funciona o demás. Voy a hacer alguna introducción, como que es la metaprogramación y demás, yo no soy informático, entonces para mí hay algunas palabras que son un poco raras y a mí esto de metaprogramación me sonaba un poco como muy etéreo. Y al final, cuando me he puesto a trabajar con ello, pues básicamente es código que manipula código, que te permite generar otro tipo de código y pues Python tiene mogollón de herramientas para poder hacerlo. Entre ellos, pues lo que son decoradores, metaclases, descriptors, abstract syntax tree, exec, inspect, disasembre, o sea, muchísimo el módulo. Yo básicamente voy a hablar de decoradores y metaclases, vale, y poco más. ¿Por qué aprender un poco qué es la metaprogramación o cómo funciona o demás? Bueno, los que utilicéis frameworks o librerías de terceros, como Django, otro tipo de cosas, seguro que hayas utilizado y hayas creado tus modelos para generar la base de datos o para crear tus campos y demás, pues ahí se utiliza internamente, se hace un uso extensivo de metaprogramación, ¿vale? Entonces para declarar tus APIs y hacer librerías o frameworks y demás, para que sean mucho más... ¿Cómo diríamos? Elegantes, pues se hace un uso muy extensivo. Luego, yo creo que te ayuda a conocer mucho mejor cómo funciona Python internamente. Yo me lo paso bien y son cosas divertidas y pues el código repetido no mola y bueno. Entonces, primero os voy a explicar algunas cosas un poco básicas que mucha gente sabe, pero por empezar, en Python todo es un objeto, módulos, clases, funciones, todo. Se pueden asignar a variables, se puede manipular. Por ejemplo, aquí estoy asignando el building typing a una variable y podría añadirle un atributo o hacer otro tipo de cosas. Tenemos los típicos métodos mágicos como el doble underscore o como dicen los ingleses underscore qualname o new o demás, ¿vale? Luego puedes asignar funciones a variables, esto probablemente todos lo sabemos y puedes generar funciones y devolverlas en tus propias funciones, ¿vale? Básicamente pues crear closures donde las variables que pasas al inicio se quedan en el scope de la función interna, ¿vale? Luego puedes acceder a estas variables y demás. Entonces, como veis aquí, por ejemplo, estoy creando dos funciones, le paso una serie de variables y luego cuando las ejecuto como el estado se ha mantenido, ¿vale? Bastante básico. Vamos a empezar con el ejemplo típico de decoradores que todo el mundo conoce y que es muy sencillo. Queremos ver por dónde estamos pasando, debugar, imprimir por dónde pasamos las variables, tenemos mucho código repetido y bueno, a mí me parece bastante feo, yo no sé qué opináis vosotros, pero bueno, el ejemplo es feo por sí, ¿no? Decorador, inicio, más grande, ¿vale? Sí, irme parando porque si no me embalo. Bueno, básicamente pues un decorador sencillo, básicamente lo que hemos visto antes con una closure, creas una función que te devuelve otra función y básicamente únicamente estamos imprimiendo el nombre, ¿vale? Estamos utilizando el Qualified Name que, por ejemplo, si estuviéramos ejecutando dentro de una clase, pues te pondría el nombre de la clase y el método, o demás, en vez de solo el nombre de la función, ¿vale? Bastante, bastante sencillo, ¿vale? Lo podríamos ejecutar así, al final lo que haces es... ¿El qué? Sí, es que luego no... Bueno, básicamente lo podemos ejecutar así, que básicamente le estás pasando a tu decorador la función y luego lo ejecutas como vemos aquí, pues pasas tu función y luego la estás ejecutando o pues con el sintácteis sugarno, con la arroba para ejecutar el decorador y demás, ¿vale? Lo que pasa es que ahora tengo que ir jugando un poco... Vale, lo que hemos visto básicamente un decorador es una función que genera un envoltorio para tu otra función, ¿vale? Y luego cuando ejecutas la función por lo que haces se ejecuta la función envoltorio. No os habéis fijado, había creado una serie de docker strings en mi función, ¿vale? Tenemos el docker string de la función suma y el nombre de la función y en el decorador tenemos el docker string de la función envoltorio. Cuando ejecuta el decorador, intento mirar los metadatos del nombre de la función y el docker string, básicamente perdemos los datos de qué es lo que estamos ejecutando, ¿vale? Lo único que aparece es mi función envoltorio y no aparece que estamos ejecutando suma realmente, ¿vale? Para arreglar esto, lo que hacemos es utilizar del módulo functools el decorador wraps y cuando intentamos imprimir los metadatos de nuestra función pues tenemos tanto el nombre como el docker string que se mantienen, ¿vale? Aquí un ejemplo muy sencillo poniendo dos decoradores que básicamente no hacen nada, ¿vale? Tenemos, bueno, imprimen por dónde está pasando la función he creado dos decoradores y nos imprime cuando ejecutamos aquí pues envoltorio 1 y envoltorio 2, ¿vale? básicamente un par de decoradores que imprimen y cuando ejecutamos vemos que aunque tengas varios decoradores el decorador wraps sigue funcionando y seguimos mantiendo los metadatos y lo que vemos es que al utilizar el wraps también se nos ha añadido como atributo a nuestra función el doble underscore wrap donde podemos acceder a la función original es decir, ¿la primera vez que la ejecuté? Sí, sí, sí, de fun tools básicamente es el que te permite que los metadatos de tu función se sigan manteniendo, es decir si te has fijado al principio cuando estaba ejecutando la función o sea cuando estaba intentando mirar cuál era el nombre de la función y el docker string me aparece que es el envoltorio no que es la función que yo he declarado inicialmente que es mi función suma, ¿sí? entonces el decorador wraps básicamente lo que hace es copia esos metadatos y mantiene los datos originales, ¿sí? entonces como ves aquí cuando vuelvo a imprimir me dice ahora que es mi función suma se te mantiene tanto el docker string como el nombre de la función como la firma de la función o sea si quisiera imprimir por ejemplo cuál es la firma de mi función en el anterior me diría que era asterisco argumentos, keyboard arcs y si ahora lo imprimo me dirá que son x e i, ¿sí? aparte el decorador wraps también añade el doble underscore wrapped que lo que hace es o sea, perdonar por ir saltando pero cuando lo ejecuto la primera vez me dice que estoy pasando por envoltorio 1 envoltorio 2 cuando ejecuto doble underscore wrapped está saltando un decorador está yendo a la función original de ese decorador sin ejecutar el decorador directamente y si ejecuto doble underscore wrapped doble underscore wrapped va directamente o sea puedo ir saltando decorador a decorador esto sobre todo pues para divulgar si no quieres ejecutar todo el código de tu decorador o cosas así esto por ejemplo solo funciona en Python 3.4 y en Python 3.3 no funcionaba si tú ejecutabas doble underscore wrapped y vas directamente a la función original no ibas saltando función a función voy a intentar hacer un ejemplo un poco más no el ejemplo básico aquí tengo una serie de codigos que tampoco, bueno, tengo una serie de funciones a la que le quiero pasar dos números y que se sumen una de string que básicamente suma el valor único de los diferentes carácteres y una custom a la que le pasa una función y luego pues hará algo sobre eso sumará básicamente entonces cuando ejecuto lo que vemos es que bueno no hace exactamente lo que queríamos porque yo quería que ad int solo sólo me ejecutara números solo sumara números pero me está concatenando carácteres por ejemplo luego a mi ad string pues le puedo pasar una lista que no era exactamente lo que quería o le puedo pasar directamente números y pues falla ¿no? como esperábamos porque en la función or pues no puedes pasar números ad custom pues si pasa una función pues muy bien pero si pasa algo que no es una función falla ¿vale? entonces como podemos arreglar un poco para comprobar que el tipo es el que esperamos lo que podemos hacer es algo bastante feo y que supongo que es empezar a añadir instants en todos lados sin imprimir si hay un error y no es exactamente lo que esperábamos y demás entonces comprobar que el tipo que me están pasando es exactamente el tipo que me esperaba ¿vale? bueno con esto por ejemplo pues para comprobar que es un int pues si instan el tipo int para comprobar que es una función pues utilizo del módulo types lambda type que te permite saber pues si las cosas son built-in types o son ejecutables y son funciones y demás y bueno más o menos pues hace lo que esperamos ¿no? o sea que cuando paso algo que no era lo que me esperaba pues me da el mensaje de error pero igualmente o sea vuelvo a poner el código pues hay mucho código repetido y antes hemos dicho que para eso pues utilizamos metaprogramación o decoradores o demás ¿vale? entonces aquí voy a hacer un pequeño inciso y ¿quién sabe lo que son las anotaciones? ¿vale? las anotaciones bueno en Python desde Python 3 se añadieron anotaciones y es que tú puedes anotar tu código básicamente para luego mediante metaprogramación o viendo cuál ha sido las anotaciones que has utilizado poder comprobar pues por ejemplo de qué tipo son o en este caso como veis estoy creando una función que realmente hace una suma y le estoy diciendo que x es de estomate ¿vale? que ves lechuga que el returna anotation es ensalada pero realmente no hace nada pues cuando ejecuto mi función realmente está haciendo una suma ¿vale? este ejemplo es sólo para ver que básicamente las anotaciones por sí solas no hacen nada bueno hay una charla después que va a hablar cuido sobre typehinting no quiero que os vayáis porque la siguiente charla aquí es súper interesante pero sí o sea ahora en Python 3.5 básicamente hay un nuevo PEP que se ha probado que va a utilizarse para hacer un poco lo que yo voy a hacer en este ejemplo como en mínimo pero bueno igualmente las anotaciones se introducieron en Python 3 no es que acaben de introducirse o demás el uso que se hacía pues no era muy... vale, una vez habiendo visto el ejemplo anterior y viendo que no hace nada realmente aunque yo aquí ponga int pues tampoco hace nada de momento pero también podemos ver es que dentro del módulo inspect puedo comprobar como es la firma de mi función ¿vale? vemos que hay un objeto firma puedo imprimir la firma y puedo ver que me dice que mi función tenía dos atributos y que estaban anotados con int y que el retorno annotation era int puedo comprobar cuál era el retorno annotation puedo acceder a los diferentes parámetros puedo ver las anotaciones puedo ver el tipo de parámetro entonces puedo saber si es sólo posicionar posicionar keyword, si es keyword only para los parámetros que son keyword only, arguments y demás entonces, vale luego también puedo hacer y coger y decir quiero unir mi firma con una serie de parámetros ¿vale? la firma que hemos visto que tenía dos valores o sea, a y b puede unirle 1 y 2 y lo que me dice ahora cuando he hecho el bound de los argumentos me dice el parámetro a ahora tiene el valor 1 y el parámetro b tiene el valor 2 ¿vale? una vez habiendo visto eso y el ejemplo anterior que teníamos un montón de is instants y demás pues vamos a intentar hacer un decorador que mediante anotaciones pues me compruebe si el tipo es el que me estaba esperando o no para evitarnos todo aquel código duplicado y demás entonces básicamente lo que hago es he creado un decorador que me cojo la firma de la función utilizo el decorador wraps para seguir manteniendo los metadatos de mi función y creo una función envoltorio que es la que se ejecutará cuando ejecute la función original hago el binding de los argumentos y compruebo que los parámetros son del tipo que toca ¿vale? o sea, lo que hago es recorro todos los parámetros de mi firma y compruebo que el tipo de el valor que he pasado es de la anotación que me estaba esperando en el caso de que no sea tal genero un mensaje de error y lo imprimo y en caso de que todo haya ido bien pues ejecuto mi función original entonces, tenemos un decorador nos hemos eliminado todo el código aquel repetido de is instants y el print y demás y mediante este decorador puedo comprobar que son del tipo que me espero entonces cuando ejecuto veo que si ejecuto con dos int no hay ningún problema pero que si ejecuto con otro tipo de variables pues me dice que no es lo que se estaba esperando que deberían ser int y demás ¿vale? de esta forma vos transformaste python en lugar de ser un typed language and typed language y entonces dejamos las grandes ventajas que tiene un lenguaje un typed la charla no trata de utilizar esto para hacer lo que estamos haciendo aquí en esta charla me estoy cargando un poco las ventajas de python pero es un ejemplo estoy enseñando como podemos utilizar los decoradores, las anotaciones y demás para comprobar por ejemplo el tipo el PEP que se ha aprobado sobre tipos anotaciones para hacer typehunting y demás no es para cambiarlo a que sea un lenguaje estático en cuanto a tipos y demás sino que es para hacer análisis de código y comprobar si puedes tener algún problema y demás es básicamente un ejemplo de decoradores no es que me quiera cargar python esto que me lo aprueben y me lo metan ahí porque ahora quiero que pueda decir vale bueno lo que voy a intentar hacer es cómo crear un decorador al que le quiero pasar un argumento entonces ahora voy a intentar imaginaros que lo que quiero es en vez de siempre imprimir el mensaje de error pues quiero que a veces cuando lo voy a ejecutar lance una excepción entonces cómo crear un decorador que le hace de aceptar argumentos básicamente para poder aceptar argumentos tengo que introducir lo que antes era mi función decorador original en una nueva función que va a ser la que le voy a pasar los argumentos básicamente lo que hago es voy a poder ejecutar mi decorador pasándole un argumento race exception luego ese decorador se le pasará la función y el resto del código funcionará exactamente igual lo único es que ahora si el race exception es true en vez de imprimir el mensaje como hacía antes lo que voy a hacer es lanzar un type error vale entonces básicamente lo que estoy haciendo o sea lo que está pasando aquí sin utilizar la roba para quitar el syntactic sugar es estoy ejecutando mi función pasando el argumento luego le estoy pasando mi función original y estoy pasando los variables que toquen vale bueno si lo quiero utilizar a false como funcionaría y true básicamente ahora pues nos queda así que tengo que pasar los argumentos a mi estos paréntesis aquí me molestan un poco porque lo que pasaba antes es que si lo quiero ejecutar sin pasar ningún argumento no le ponía los paréntesis y ahora al haber definido un argumento que es opcional porque si lo dejo vacío va a utilizar el race exception igual a false pues tengo que poner esos paréntesis porque de otra manera no va a funcionar primero tienes que pasar los argumentos luego se va a pasar la función y luego es cuando ejecutas el decorador entonces vamos a intentar cargarnos estos paréntesis entonces un ejemplo podría ser volver a cargarnos o sea la función decorador que hemos creado antes la eliminamos y mediante el uso de funtals partial lo que hacemos es si lo ejecuto sin los paréntesis la primera vez que esto se ejecute va a pasar la función directamente entonces directamente pues esta parte no se ejecutará iremos directamente lo que hacíamos antes, comprobamos la firma ejecutamos, hacemos el binding y demás si le paso el race exception lo que va a pasar es que voy a devolver un objeto partial que es la misma función con el race exception que luego se llamará con el argumento ¿vale? ¿quién conoce este tipo de notación y para qué sirve? o sea poner aquí el esterisco y luego ¿vale? básicamente aquí lo que estamos haciendo estamos definiendo que race exception siempre se tiene que pasar a la función es un keyword only argument entonces cuando ejecutas la función siempre si quieres pasar el race exception tienes que poner race exception igual a true o race exception igual a false no puedes pasar lo posicional sin poner race exception delante ¿me ha explicado? ¿vale? pues ahora podemos ejecutarlo sin poner los paréntesis queda más bonito ¿vale? y funciona o puedo pasarle el argumento y pues sigue funcionando ¿vale? ¿vale? vemos que el wraps con todas estas cosas sigue funcionando que no hemos roto nada y demás ¿vale? entonces imaginar ahora que lo que quiero hacer es yo tengo el decorador que hemos creado y quiero en medio de la ejecución poder cambiar esa variable que había creado inicialmente ¿vale? es decir quiero que yo lo defino y la primera vez no me lanzará ninguna excepción pero luego sin tener que volver a redefinir mi función quiero poder cambiar ese comportamiento y que ahora pues me lance una excepción ¿vale? como podemos hacer pues básicamente lo que podemos hacer es crear un decorador dentro de nuestro decorador ¿vale? porque los decoradores molan entonces básicamente nos vamos a fijar primero en esta parte de código que es la parte nueva lo que estoy haciendo es el resto es igual, tengo mi función en voltorio que mira la firma y demás y aquí lo que hago es creo una función que se le pasará un race exception level y mediante el uso de non local soy capaz de acceder a la variable race exception en el scope de wrapper ¿vale? entonces puedo cambiar el valor de race exception en el scope anterior es decir aquí dentro cuando lo ejecute al valor que estoy pasando ¿vale? y lo como veis estoy anotando esta función con otro decorador y este decorador básicamente lo que está haciendo es haciendo como atributo al objeto que es wrapper esta nueva función es decir cuando acabe de ejecutar esto mi función anotada va a tener un método que se va a llamar setRaceException que me va a permitir cambiar el valor de race exception ¿sí? queda más o menos ¿vale? entonces bueno puedo ejecutar esto que es como estaba funcionando al principio ¿vale? no me salta la excepción veo como tengo un método race exception ahora dentro de mi función y como puedo cambiarlo sin tener que volverla a redefinir y cambio el comportamiento ¿vale? ¿vale? pues un poco el código otra vez ¿vale? entonces ahora quiero que recordéis un poco el ejemplo con el que empezamos ¿vale? que básicamente era un ejemplo muy sencillo una que era atInt una que era atStr y otra que era atCustom ¿vale? y bueno pues imaginalos que estoy ahí con mi colega Fran ahí en el curro pues explicando ¿no? y luego tiene que venir mi amigo David a ver lo que hemos hecho ¿no? y se lo explicamos y su reacción en el code review pues es algo así ¿no? cuando ve el código vamos yo me encuentro este código de primera sin que alguien me lo explique no sé vosotros pero con dos partials por ahí un decorador dentro de otro decorador y cosas así como que no ¿vale? pues hay que ir un poco con cuidado porque a veces un ejemplo muy sencillo pues se te puede ir de las manos entonces hay que ir con cuidado ¿vale? entonces ahora voy a hablar un poco de metaclases ¿vale? no no mucho pero un poco como funciona además ahora estoy más tranquila en todas pues también es ¿vale? estoy aquí pues estoy declarando una clase normal, estoy creando una instancia estoy viendo cuál es el tipo de la instancia y básicamente estoy viendo que el tipo de la instancia es de la clase Tomate ¿vale? el tipo de Tomate es del tipo type pero bueno type se puede utilizar también no sé cuánto bueno type se puede utilizar también no sólo para el tipo de tu objeto pero para crear clases ¿vale? type es la clase la metaclase madre y es la que es capaz de crear clases ¿vale? entonces yo puedo crear una clase así puedo coger y decir type con el nombre Lechuga voy a crear una clase que va a ser Lechuga le voy a pasar las bases y esto es el contenido de mi clase que de momento es un diccionario vacío ¿vale? y veo que el tipo de Lechuga ahora es type lo mismo que era Tomate para crear instancias de Lechuga ¿vale? o sea un caso muy sencillo pero básicamente este código es exactamente lo mismo que esto ¿vale? sí es más técnico es que como has utilizado en el ejemplo Lechuga Iguala Lechuga y debajo de Lechuga el que está entre paréntesis podría llamar otra manera ¿cuál? perdona en la línea 57 no es decir pon el Lechuga Iguala Type ah si si puedes llamarlo Igual lo llamas Igual pero si lo llamas es distinto ¿cuál cambiarias para ver como sigue en el ejemplo? vale pues aquí pondría tipo de o sea aquí podría ponerle a y luego miraría cuál es el tipo de a y me diría que es de tipo pues lo dejo como ejercicio ¿vale? vale luego bueno el tipo de type sigue siendo type ¿vale? y luego aquí vamos a un ejemplo de un poco más complicado ¿no? lo que hago es me creo un código que es un string o sea estoy metiendo en un string ahí una variable a lo loco y una función y lo que hago es ejecuto ese código en un diccionario y luego estoy creando una clase con ese diccionario de código que os he dicho antes que podía pasarle ¿vale? y lo que veo es que mi nueva clase puede crear instancias y que este código que era un string ahora es ejecutable ¿vale? si? veo muchas caras un poco bueno básicamente ¿qué hemos visto? ¿qué hemos visto? las metaclases son las clases de las clases, se le dan de type controlan cómo se crea se crean las clases las metaclases son a una clase que es una clase a una instancia y luego tipo de la instancia hemos visto que era la clase tipo de la clase es la metaclase o type y tipo de la metaclases es type ¿vale? fácil entonces aquí un pequeño ejemplo de crear una metaclase no hace nada pero básicamente cómo generaría una metaclase para que cambie y no me diga que tomate es de tipo type sino que es de mi metaclase pues básicamente creo una clase que hereda de type y se la paso como metaclase este mi metaclase va a controlar exactamente cómo se crea tomate y ahora veremos un pequeño ejemplo aunque vamos mal de tiempo ahora tomate es de mi metaclase ¿vale? ¿cómo funciona esto un poco? cuando se crea una instancia probablemente estáis más utilizáis el double underscore new el double underscore init cuando estás creando instancias es decir en la definición de la clase básicamente lo que pasa es cuando creas una instancia se llama el método double underscore call de la metaclase luego se llama el método double underscore new de tu clase que aquí todavía tu clase tu instancia no está creada y luego se llama el método double underscore init de tu instancia que aquí ya la instancia sí que está creada y pues modificarla o lo que quieras estos diagramas los he sacado de aquí si lo queréis ver lo que me preguntáis y ya está ¿vale? entonces ¿qué pasa con una clase? que es lo que es un poco más pues aparece algo nuevo que es double underscore prepare que básicamente el double underscore prepare se encarga de generar ese diccionario que hemos visto que le hemos pasado antes a type el double underscore prepare es el que se encarga de crear ese diccionario ¿vale? y vamos a ver un ejemplo con ejemplos pues tampoco se entienden las cosas entonces vamos a hacer una meta clase que lo único que hace es comprobar que el orden de las cosas es el que quiero ¿vale? entonces voy a crear unas clases que van a ser buenas que van a tener siempre los atributos primero y luego los métodos ¿vale? y luego otra que va a ser mala que primero se van a crear esta me va a decir que no se puede crear ¿vale? porque a mí yo soy muy maniático y a mí el orden pues entonces primero tienen que estar todos los atributos ¿vale? no es que yo quiera implementar esto en Python pero bueno es un ejemplo igual que antes con los decoradores vale, entonces vamos a ver por ejemplo cuando creo mi meta clase el double underscore init que hemos dicho que se ejecutaba cuando estoy creando mi clase para ver qué atributos se han creado ¿vale? vale, lo único que estoy imprimiendo son los atributos y me está diciendo que hay un atributo a y un atributo función de momento parece que todo va bien que por defecto Python los diccionarios como sabréis no me tienen el orden en el que se han creado las cosas ¿vale? entonces cuando lo intento hacer con la mala me dice el mismo orden de las cosas aunque yo he utilizado un orden diferente entonces lo que puedo hacer es utilizar el método double underscore prepare que os he comentado antes para cambiar el diccionario que estoy recogiendo y en vez de utilizar un diccionario normal utilizo un orden addict entonces ahora si os fijáis el orden de lo que está creando lo que tiene y puedo comprobar que mis atributos se crean antes que mis funciones ¿sí? básicamente utilizando el double underscore prepare y cambiando que en vez de un diccionario normal que es el por defecto que utilizan las metaclases se utiliza un orden addict ¿vale? y bueno pues haciendo mi código para comprobar si están antes las funciones que los métodos tampoco el código es eso pues puedo comprobar que cuando creo mi clase buena pues no pasa nada pero cuando creo mi clase mala me dice a le de dar de mi clase ordenada que las funciones tienen que seguir los atributos clases en mi clase definition ¿vale? y con esto y un bizcocho pues ya hemos acabado gracias Raúl tenemos poco tiempo sólo para un par de preguntas si queréis ya me han dicho en medio muchas gracias realmente ha estado muy bien me gustaría preguntarte metaclases en producción ¿sí? ¿tienes algún ejemplo en el que lo hayas usado? yango no, quiero decir en el que tú estés creando código creas código y necesitas metaclases para resolver algo de negocio lo he utilizado una vez estuve trabajando para hacer pruebas de performance y ahí sí que lo utilicé pero yo creo que el uso es más para librerías y frameworks que no para otro tipo de código es más, ya he visto antes en el ejemplo del decorador aunque no se acabe entendiendo al final ver cómo rápidamente se te puede ir de las manos entonces hay que ir con cuidado en este tipo de cosas ¿alguna pregunta más? sí, sí hemos visto tan rápido lo podríamos consultar y mirarlo más calmaramente sí, sí, o sea, tengo en GitHub la presentación es Unipython Notebook y está todo el código ahí lo puedes consultar y si tienes dudas si lo quieres mejorar haces un pull request lo normal gracias