MÉTODOS DE CLASE, ESTÁTICOS Y @PROPERTY

COSTA DE ANAGA, TRAS LA LLUVIA, CON SUS RETAMAS BLANCAS EN FLOR Y EL PITÓN VOLCÁNICO DE TABORNO SOBRESALIENDO EN EL CENTRO DE LA IMAGEN, EN UN DÍA DE MUCHO VIENTO DEL NORTE, COMO MUESTRA EL GROSOR DE LOS RIZOS BLANCOS DE ESPUMA  CONTRA EL ACANTILADO. MACIZO DE ANAGA, NORESTE DE TENERIFE.

       

      MÉTODOS DE CLASE


      Vamos a imaginarnos que tenemos una clase Ropa y que, en ella, creamos una serie de métodos adecuados para hacer uso de los diferentes objetos o instancias que podamos crear/instanciar. Por ejemplo, creamos un método "ponerse" que podemos aplicar a un objeto/instancia sombrero o gorra; podemos crear/instanciar un método "abrochar" que podemos aplicar a un objeto/instancia blusa, rebeca, camisa, abrigo, etc.; podemos crear/instanciar un método "anudar" que podemos aplicar a un objeto/instancia pañuelo, para cubrirnos la garganta del frío, o a un objeto/instancia botas o zapatos. Pero ¿y si creamos/instanciamos un método "ajustar a la muñeca" que podamos aplicar a un objeto reloj? Vale, sí, ¿por qué no? Pero... ¿Un reloj es ropa? ¿ Un reloj viste?
Tengamos en cuenta que cualquiera de los objetos que hemos instanciados dentro de la clase Ropa sirven, básicamente, para cubrirnos y abrigarnossombrero, camisa, abrigo, zapatos,... Pero un reloj apenas nos cubre, (dependiendo del tamaño de la correa y la caja) y apenas nos abriga (quizás por el material y el grueso de la correa). Y, sin embargo, no sólo nos lo podemos poner encima (en la muñeca) como cualquiera de los objetos que hemos instanciado con los métodos citados sino que, según como vistamos, podemos tildarlo de complemento (como un bolso o un foulard, que también comparten con el objeto reloj, su misma condición fronteriza) contribuyendo a un "modo de vestir" que, al fin y al cabo, es lo que perseguimos con la clase Ropa: que nos vista.




Resumiendo, tenemos una clase Ropa que integra varios métodos propios para vestir cierta clase de adminículos y aditamentos: "ponerse", "abrochar", "anudar", "subir cremallera", "abotonar", "calzar", etc., mientras que también hemos incluido otros, como  "ajustar a la muñeca" o, por ejemplo, "ceñir" una faja o cinturón (que tampoco viste, quedando en lo fronterizo, por así decirlo, pero que nos sirve para adaptar la ropa a nuestro cuerpo) y que podemos separar o distinguir perfectamente de los primeros.


RECORDEMOS QUE PODEMOS DISTINGUIR PERFECTAMENTE A UN MÉTODO USANDO INFINITIVOS VERBALES, ASÍ COMO A LAS PROPIEDADES O ATRIBUTOS CON NOMBRES CUYA RESPUESTA PUEDAN SER ADJETIVOS: ¿COLORES? ROJO, VERDE, AZUL, AMARILLO,...


Vistas así las cosas, podríamos plantearnos cómo organizar esta estructura de clase de una manera más coherente. Pues bien, podríamos hacer lo siguiente: haríamos depender directamente de la clase Ropa todos aquellos métodos que, efectivamente, cumplan "escrupulosamente" con los requisitos de vestir, de la manera habitual que ya conocemos, mientras que aquéllos otros métodos que no nos vistan exactamente, pero nos aporten una funcionalidad añadida interesante, como "ajustar a la muñeca" para el caso de un reloj o una pulsera, o "ceñir", que incluso, devendría necesario para ajustarnos a la cintura unos pantalones o una falda, puedan aparecer "en otro sitio". ¿Dónde? ¿Cómo?
Pues (dónde) dentro de la propia clase haciéndola depender directamente de ella misma, e (cómo) instanciándolos como métodos de clase.





Vamos con la teoría: Los métodos de clase se caracterizan porque pueden ser invocados directamente desde la clase sin que tengamos que instanciar ningún objeto, lo que nos permite ahorrarnos la autorreferencia self, ya que ésta a punta al objeto que se instancia "a partir de" la clase. En su lugar, la sustituimos por cls, de (class), denominación que, al igual que self, se aplica por convención entre los desarrolladores, ya que a estos métodos de clase podemos invocarlos "desde" la clase misma dado que, como hemos dicho, no lo hacemos desde ninguna instancia de objeto.
También podemos implementar parámetros adicionales para nuestro método de clase como podemos hacer con cualquier método normal y corriente dentro de una clase cualquiera (item, en el ejemplo que mostramos a continuación), aunque siempre tendremos que introducir la autorreferencia cls como argumento principal y necesario que, en el caso de los métodos de clase, sustituye a la consabida autorreferencia self, el argumento principal y necesario de cualquier método normal y corriente que instanciemos, que creemos, dentro de una clase cualquiera. Aún más: dentro de un método de clase también podemos implementar todos los parámetros opcionales (recordemos, aquéllos que toman el formato clave/valor, como por ejemplo, color = "azul" o colores = listadecolores) que estimemos oportunos.




La autorreferencia cls es, realmente, una convención entre programadores para los métodos de clase, como lo es self para los métodos "normales". En realidad, podríamos usar cualquier otra que se nos ocurra, pero en arreglo a la coherencia y a las recomendaciones de estilo, debemos utilizar siempre cls.
Podemos recurrir a los métodos de clase cuando modelizamos (creamos) una clase simple, sencilla, con poco contenido, como por ejemplo, una clase Capitulo donde instanciamos un método que devuelva una string, una cadena de caracteres literales, que ejerza de encabezado para una serie de apartados dentro de un libro de cuentas: ingresos primarios, ingresos secundarios, cobros, impagos, débitos, etc.
Igual que sucede con @property, como veremos en su momento, los métodos de clase demandan del concurso de un decorador (decorator, v. DECORATORS), que en este caso será @classmethod, que nos permite sin más definir el método como método de clase. Para conseguir esto, tan sólo tenemos que colocar el decorador justo por encima del método que queremos definir como método de clase y ya está. Tan simple y sencillo como éso.
Veamos un ejemplo.


En el ejemplo superior, para demostrar cabalmente que el autorreferente obligatorio cls es, al fin y al cabo, una simple convención entre programadores, hemos optado por usar el término clase en su lugar, devolviendo los resultados esperados.
Veamos ahora también un ejemplo con argumentos opcionales.


Fijémonos que la manera de invocar a un método de clase es, como no puede ser de otra manera en Python, a través del método o sintaxis del punto (dott method): llamamos primero a la clase por su nombre (classname), colocamos nuestro punto, y a continuación llamamos al método de clase, pasando los argumentos necesarios en el caso de que nuestro método de clase lleve parámetros en su zona de parámetros o zona de argumentos, que de ambas maneras se puede llamar al espacio que queda entre los paréntesis de inicio y cierre con que termina todo método (paso de parámetros), eso sí, sin la autorreferencia cls, que no podemos utilizar ya que nos lanzaría una excepción de tipo NameError: name 'cls' is not defined.
Así, como acabamos de decir, los métodos de clase se pueden invocar de forma muy sencilla como se puede llamar a cualquier otro método, todo en una misma línea de código. Lo vemos a continuación.


Incluso, es posible también instanciar un objeto e invocar al método de clase, todo, en una misma línea de código.



LA COSTA ACCIDENTADA DE ANAGA Y SUS ACANTILADOS DESDE EL MAR, MACIZO DE ANAGA, NORESTE DE TENERIFE.

      MÉTODOS ESTÁTICOS


      Los métodos estáticos cuentan con la peculiaridad de que, a diferencia de los métodos de clase, no necesitan acceder a ningún atributo de objeto previamente instanciado en la clase, por lo que, subsidiariamente, prescinden de la autorreferencia self. Por este motivo, no tienen acceso a ningún atributo o propiedad de la clase.
Para crearlos, del mismo modo que en el caso de los métodos de clase necesitábamos del concurso de un decorador, @classmethod, el método estático cuenta con el suyo propio, @staticmethod, que de manera similar al anterior debe insertarse en nuestro código antes de su definición, (como ponerle un sombrero, vamos) justo en la línea anterior de código.
Como los métodos estáticos funcionan independientemente de los objetos desde los que se invocan, dichos métodos forman parte de la clase, igual que los métodos de clase, aunque debemos tener en cuenta que son métodos distintos.
Esta condición de pertenencia posibilita que, a pesar de que es posible definir un método capaz de hacer exactamente lo mismo, si tenemos la intención de repetirlo a lo largo de nuestro programa, en diferentes partes o zonas de nuestra clase, o a veces, un simple trocito de código que vayamos a utilizar en otros métodos (reiteración), es preferible definir un método estático para reutilizar código y cumplir con el principio de abstracción de la Programación Orientada a Objetos.


      EN EL APARTADO QUE HEMOS LLAMADO SUPUESTO1 TENEMOS UN CÓDIGO DONDE MOSTRAMOS UN EJEMPLO CLARO DE CÓMO USAR Y CUÁNDO RECURRIR A UN MÉTODO ESTÁTICO. ADEMÁS, PODREMOS VER (Y SE EXPLICA TAMBIÉN EN LA PROPIA PÁGINA) CUÁLES SON LAS LIMITACIONES (QUE LAS HAY, BÁSICAMENTE, POR CUESTIONES DE FUNCIONALIDAD, ES DECIR, DE LOS RESULTADOS QUE QUEREMOS CONSEGUIR,  Y NO TANTO POR LA SINTAXIS) DE USO DE LOS MÉTODOS ESTÁTICOS.
AQUÍ OS DEJO EL ENLACE: SUPUESTO1.

Vamos a ilustrar estos conceptos con un ejemplo:



Tal y como podemos ver en el ejemplo, Python lanzará una excepción si intentamos acceder a una propiedad o atributo de objeto de tipo NameError: name self is not defined. El error se suscita por el hecho de que la autorreferencia self apunta a un atributo o propiedad (x) con su valor, "hola", y que pertenece, así nos lo refrenda la autorreferencia self, a la clase, por lo que el método estático no puede acceder a ella.



      @property

   

     En síntesis y a modo de introducción, el decorador @property no es otra cosa que una adaptación pythónica del concepto de Getters (conocidos también como "accesores" o "captadores", en tanto que nos permiten recuperar o acceder al valor de un atributo o propiedad instanciado en una clase, de to get, "obtener") y Setters (conocidos también como "modificadores", en tanto que nos permiten modificar el valor original de un atributo o propiedad instanciado en una clase, estableciendo un valor nuevo, diferente, distinto al anterior, de to set, "establecer") en el ecosistema propio y particular de la Programación Orientada a Objetos.
¿Cuál es la razón de ser de los Getters y Setters? Básicamente, ambos conceptos tiene que ver con la ENCAPSULACIÓN. El concepto de ENCAPSULACIÓN es, sucintamente, el mismo que el de VISIBILIDAD que acabamos de ver y estudiar en el capítulo anterior. Y en concreto, tiene que ver con garantizar y hacer más segura las encapsulaciones (visibilidad) de los datos aunque, en este capítulo de nuestro manual, le daremos un uso más amplio.
Podemos aducir que una buena razón para encapsular las propiedades o atributos de una clase o, lo que es lo mismo, restringir o no su visibilidad, es la de hacerlos privados (con un guión bajo delante del nombre del atributo o propiedad, si queremos un rango medio de privatización, o con doble guión bajo, si queremos un nivel de privatización más alto (o de visibilidad más bajo)) para incrementar su nivel de protección frente a la manipulación de otros programadores, o evitar injerencias no deseadas de otro código.



      EN NUESTRO CASO, COMO PROGRAMADORES, SOMOS NOSOTROS EN ÚLTIMA INSTANCIA QUIENES DECIDIMOS SI UN VALOR PROPORCIONADO A UNA PROPIEDAD O ATRIBUTO INSTANCIADO A PARTIR DE UNA CLASE ES PRIVADO O NO. SI TENEMOS UN PROGRAMA QUE MANEJA INFORMACIÓN QUE NO CONVIENE MODIFICAR (DESDE DATOS PERSONALES O HISTORIALES MÉDICOS A CONSTANTES FÍSICAS, POR EJEMPLO), PODRÍA RESULTARNOS ÚTIL. TAMBIÉN PODRÍAMOS VALORAR LA POSIBILIDAD DE ENCAPSULAR DETERMINADOS DATOS SI ESTAMOS CONSTRUYENDO UN PROGRAMA AMPLIO, PROFUSO, DONDE SE MANEJAN UNA ELEVADA CANTIDAD DE PROPIEDADES O ATRIBUTOS Y DEBEMOS CUIDARNOS DE QUE NO SE MANIPULEN O QUE SE MANIPULEN DE UNA FORMA ESPECÍFICA Y CONTROLADA. EN ÚLTIMA INSTANCIA, LA DECISIÓN ES NUESTRA.
 
Veamos las tres razones principales, a nuestro juicio y desde este enfoque en particular (recordamos que nosotros le daremos una amplitud mayor al ámbito de aplicación de @property), por los que usamos Getters y Setters:

  1. Evitar un acceso franco, directo, bien de un usuario/programador externo o bien de una porción de código diferente al ámbito actual,  a los valores especificados de manera original para determinados atributos o propiedades instanciados a partir de una clase y, por ende, poder modificarlos engrosando el proceso de encapsulación (visibilidad).
  2. Nos permite añadir, por decirlo así, una capa de validación para obtener, recuperar, capturar un valor determinado de una propiedad o atributo (Getters) o cambiar, modificar esos mismos valores y establecer valores nuevos para sus respectivas propiedades o atributos (Setters).
  3. Implementar niveles de acceso a los valores de los atributos o propiedades.



Vamos a ilustrar estos conceptos con un ejemplo que, además, nos permitirá repasar los conceptos desarrollados en el capítulo anterior.
Modelizaremos una clase a la que llamaremos ActaAlumno que inicializaremos (__init__(self)) instanciando cuatro propiedades o atributos de clase: nombre, apellidos, estudios y calificación. Ésta última será un campo privado, con doble guión bajo, por lo que invocaremos al "inspector" de Python para que controle los accesos al valor de la propiedad desde un segundo plano.
Partimos de un código base y comprobamos que funciona:

Verificado nuestro código, vamos a introducir Getters y Setters para ver cómo funcionan. Para ello insertaremos dentro del ámbito de la clase, dado que es la forma correcta de hacerlo, mediante dos nuevos métodos definidos ad hoc que apuntarán a la propiedad __calificacion que hemos establecido como campo privado: gettersCalificacion y settersCalificacion. En esta ocasión, y a diferencia del código anterior, proporcionaremos un valor a cada una de estas propiedades.
Antes de hacer esto, vamos a ver qué sucede cuando instanciamos un objeto, alumno2, para la clase ActaAlumno y pedimos que se nos imprima en pantalla los valores que instanciamos originalmente para nuestras propiedades nombre, apellidos, estudios y calificación:



Como podemos ver, tenemos acceso fácil a todas los valores de las propiedades indicadas salvo la última, el campo privado, __calificacion, de forma que el inspector de Python nos lanza una excepción de error de atributo.
Sin embargo, recordemos, esto NO IMPLICA que el valor de la variable consignada como campo privado sea INTOCABLE, y que dicho valor no se pueda modificar. Python, al contrario que otros lenguajes de programación, al menos de momento, no nos ofrece esta opción. Veamos cómo podemos modificar el valor original del campo privado __calificacion desde dentro de una función main:


Ahora sí, podemos entender que una o más propiedades o atributos consignados como campos privados, a pesar de su nombre, podemos no sólo recuperarlos sino modificar también su valor. Vamos a verlo:



Podemos darnos cuenta cómo la implementación en el ámbito de la clase ActaAlumno de dos métodos como gettersCalificacion y settersCalificacion nos permite manipular fácilmente un campo privado, tanto para obtener su valor como para modificarlo.

VARIANTES DE COLOR EN LAS CRESTERIAS DE LAVA CON LA CAÍDA DE LA TARDE EN EL PARQUE NACIONAL DE LAS CAÑADAS DEL TEIDE, CENTRO DE TENERIFE. 

Sin embargo, fijémonos que debemos llamar al método gettersCalificacion como tal, como lo hacemos siempre, es decir, con su zona de parámetros delante, esto es, con los dos paréntesis, (), en su caso, vacíos, porque no lleva parámetro alguno que debamos pasar a argumento (proporcionar un valor concreto) ni, por supuesto, la autorreferencia self. para que el código surta efecto.
Sin embargo, veamos qué es lo que ocurre cuando situamos el decorador @property como cabecera (podemos imaginar que es como ponerle un sombreo al método) del método gettersCalificacion. Además, hemos introducido una pequeña modificación en el método de Setters, settersCalificacion, donde insertamos un condicional para bifurcar entre aprobados y suspendidos a partir de una condición que imponemos sobre el valor que toma la variable calificacion en la zona de parámetros del método.



Vamos a ver a continuación  qué ocurre cuando a nuestro método gettersCalificacion le ponemos el "sombrero" @property.



Lo que sucede es que se nos lanza una excepción como la copa de un pino que nos advierte de que se ha producido un error de tipo de dato. Nos dice que un dato de tipo float no puede ser llamado (callable) dado que sólo puede ser invocado (tiene la condición de callable) un método o una clase. Podemos ahondar más en este concepto de callable en la siguiente entrada: CALLABLE. Incluso aquí, en la entrada que acabamos de subrayar, encontraremos alguna referencia a @property.
¿Y por qué sucede esto? Porque al colocar el decorador @property, la llamada ya no apunta al método en sí (gettersCalificacion) sino al valor de la propiedad directamente, ignorando al propio método: pasa por encima del método, que se ejecuta, por decirlo así, en segundo plano, y va derechita como un flecha a la propiedad (de ahí property, claro).
Lo que sucede en resumidas cuentas es que "transforma" el método en una propiedad tomando directamente como valor de esa misma propiedad el resultado de la ejecución del método.
Veámoslo con un nuevo ejemplo donde, dentro del método gettersCalificacion, el Getters, vaya, añadimos la opción de sumar una cifra, en este caso, 1.3, lo que modificará el valor original que le proporcionamos a la variable __calificacion cuando la declaramos en el inicializador/constructor __init__(self) de la clase ActaAlumno. Como nuestra método gettersCalificacion, por mor del decorador @property se ha "transformado" en una propiedad, no podremos llamarlo como tal método, esto es, con sus dos paréntesis, (), su zona de parámetros delante, sino que debemos hacerlo de la misma manera en la que llamamos a un atributo o propiedad: sin ellos. Así evitaremos la excepción y obtendremos los resultados esperados.


Resuelto el misterio.



Vamos ahora a "ampliar" los horizontes de nuestro decorador @property.
Las variables de instancia son propiedades o atributos que especifican una serie de características que deben poseer aquellos objetos que instanciemos (obviamente) a partir de una clase cualquiera. Python cuenta para estas misma propiedades en caso de que requieran de un tratamiento previo a su llamada en un método, por ejemplo, con un decorador (decorator, v. DECORATORS) exclusivo, @property, que modifica (decora) un método mostrando una versión de dicho método distinta, diferente.
Veamos de qué va la cosa.
Supongamos que modelizamos una clase para representar un círculo. Ahora nos interesa contar con una propiedad que acoja el área del mencionado círculo, para lo que sólo necesitamos colocar el radio.



Podemos obtener un resultado similar si utilizamos en su lugar el método integrado property(), que nos obliga a asignarla a una variable. Es un recurso algo más laborioso, sólo un poco más, que aplicar @property directamente, pero a cambio, es más eficiente desde el punto de vista de la optimización y el uso de recursos.


Veámoslo en un ejemplo:



Aún así, si ponemos el acento en la manejabilidad de nuestro código, es preferible recurrir al decorador @property a la propia función property().
En nuestro ejemplo, @property nos permite acceder fácilmente al valor de la propiedad o atributo radio en la línea de código return pow(self.radio, 2) * pl, pero no es posible modificarlo sobre la marcha (asignarle un nuevo valor una vez hemos accedido a la propiedad), dado que su cambio implicaría subsidiariamente la modificación de la propiedad área_círculo que calculamos en base al valor original de la propiedad radio.
Para poder efectuar estas alteraciones de los valores primero debemos modificar nuestra clase. Primero "sacamos" nuestra propiedad radio del inicializador o constructor, como queramos llamarlo, __init__(self), a quien, simplemente, como cuerpo de la función, podemos dejar la palabra clave (keyword) pass y, a continuación, agregamos a la clase Círculo un método nuevo con la propiedad o atributo radio pasado como campo privado mediante, como ya sabemos, un doble guión bajo antes del nombre.

                                                                               @property
                                                                               def radio(self):
                                                                                     return self.__radio

A continuación, le asignamos al nombre de la función anterior el decorador @ (por eso se llama decorador: porque engalanamos a una funcionalidad del programa anteponiendo al nombre que le asignemos una arroba monísima) y, por la sintaxis del punto, el modificador setter, que nos proporciona un método integrado de Python que nos faculta para modificar el valor de una propiedad o atributo sobre la marcha:

                                                                                @radio.setter
                                                                                def radionuevo(self):
                                                                                      self.__radio = radio

Y ya podemos modificar divinamente el valor de nuestro radio tantas veces como queramos:



Resumiendo: imaginemos que modelizamos una clase Coche. A esa clase Coche le asignaremos una serie de propiedades o atributos (características → adjetivos, recordemos la regla) con sus valores correspondientes como, por ejemplo, color (propiedad o atributo) = (operador de asignación) "rojo" (valor), altura = 1.7, peso = 800, marca = "Seat", modelo = "Arona", etc. Al mismo tiempo, a esta clase Coche le asignamos algunos métodos, es decir, aquéllo que un objeto cualquiera instanciado a partir de la clase Coche es capaz de hacer (capacidades, cosas que puede hacer, acciones → verbos, recordemos la regla una vez más): acelerar, girar, frenar, cargar, activar el parabrisas, etc.
Una vez hayamos instanciado estas propiedades y estos métodos dentro de la clase Coche podremos extenderlos, proporcionárselos, "prestárselos", a cualquier objeto que instanciemos a partir de la clase (coche1, coche2, coche3, etc.) a través de la autorreferencia self.
Vamos a imaginarnos ahora que instanciamos un método nuevo, que llamamos, circular (circular es un verbo, ergo tenemos un método). Pero, quizás, a nosotros nos interesaría que funcionara más como una propiedad, de tal forma que el resultado, la devolución, lo que nos entrega el return, vaya, de su ejecución, sea un valor propietario al que otorgarle valores, como por ejemplo, circular = "Carretera de montaña", circular = "Autopista", circular = "Circuitos urbanos", circular = "Pistas de carreras", etc.
Pues bien, para eso sirve @property: para transformar un método o, para ser más exactos, su devolución, (circular) en una propiedad susceptible de recibir valores nuevos a posteriori (nuestro objeto coche1 instanciado a partir de la clase Coche que antes tenía el valor de la devolución de la ejecución del método circular en "Autopista", ahora podemos asignarle sin mayores problemas el nuevo valor "Circuitos urbanos".
Tan sencillo como éso.

TABAIBA DULCE (EUPHORBIA BALSAMÍFERA), EN CUALQUIER ZONA DE TENERIFE, DE MANERA NATURAL, EN LAS VERTIENTES SUR Y OESTE DE LA ISLA, POR DEBAJO DE LOS 400 METROS.



4 comentarios:

  1. Hola Gauber...
    Buenas Tardes...
    Desde Chile....

    Después de tanto tiempo sin saludar...Sigo con este manual.

    Una consulta...¿Algún día subirás un instructivo para pruebas unitarias en Python?
    Baje todos los tutoriales de Don: Jesús Conde, y como entusiasta en el tema, recurro a
    tu blog, para solicitar un espacio dedicado a pruebas unitarias en Python...
    Gracias...

    PD: No estoy criticando a Don: Jesús Conde al respecto, pero, para algunos, se entiende mejor leyendo...y este es un buen blog...

    ResponderEliminar
  2. Hola, PIPE, buenos días desde Tenerife. Por cierto, hace poco leí de tu bellísimo país una tesis doctoral ya publicada en abierto sobre la relación entre mapuches y colonialismo hasta principios del siglo XIX. Lo primero, agradecerte que sigas acordándote de nosotros. En cuanto a si voy a subir material sobre pruebas unitarias, lo cierto es que de momento no. Siento defraudarte pero para este blog llevo una línea de trabajo marcada donde todo lleva una correlación en base a favorecer la curva de aprendizaje. Ahora estamos en la POO y, antes que nada, deben llegar artículos como Herencia, La función super(), Namedtuple, Patterns (patrones), y algún otro más. Además, el tiempo del que dispongo para dedicarle al blog no es muy grande: trabajo, familia, ocio, etc. Lo que puedo ofrecerte de momento es que pulses sobre el libro que aparece en el margen superior derecho de este blog bajo el epígrafe HERRAMIENTAS Y UTILIDADES, y navegues entre sus opciones más directas (sitios web, videotutoriales, etc.) para localizar algo relacionado con ello. De todas formas, intentaré por mi parte ir preparando el tema que me sugieres para subirlo en su momento, pero ya te aviso que sería, en todo caso, para finales del año próximo, lamentándolo mucho. Siento no darte buenas noticias, PIPE, pero mientras los días sigan teniendo sólo veinticuatro horas... Saludos de nuevo y muchísimas gracias por seguirnos.

    ResponderEliminar
  3. Hola Gauber....
    Gracias por responder....
    Gracias por las referencias..
    Por otra parte, no tienes nada que agradecer....Es al contrario.....Los agradecimientos van hacia ti y a tu espacio dedicado a la ""Entrega de la Información y Aprendizaje"" gratuito y en español....
    Lo de tu entrega en Python, lo esperaré.....
    Te cuento, que he hecho mención (como material bibliográfico) de tu blog en cursos y redes en la web, como también la de Don Jesús Conde......
    Como tu me escribiste una vez....""Vas a descubrir muchas cosas en Python""....y es verdad...
    Por el momento, y echándole un ojo cada vez a tu blog...
    Gracias por todo y a la espera de tus actualizaciones....
    Atentamente.
    Felipe R. (PIPE).
    Laguna Verde.
    Valparaíso.
    Chile.



    ResponderEliminar
  4. Muchísimas gracias, PIPE. No tengo palabras. Conmmovido, sinceramente: nunca creí ni remotamente que este humilde blog pudiera llegar tan lejos y pudiera recibir tanto aprecio por parte de sus lectores. Puedo informarte que ya acumulamos más de cien mil visitas y que, bendito sea Dios, TODA América salvo algunas repúblicas anglófonas caribeñas y Haití, desde Canadá a Chile/Argentina están representadas en este este blog. Todo un honor por las menciones que haces de mi blog. Permíteme un abrazo desde una ciudad, la mía, donde vivo, que no es verde en el nombre pero sí por los bosques que la rodean y que, como la tuya, se llama Laguna (San cristóbal de La Laguna). Saludos.

    ResponderEliminar