Ej. 42 : Practicando con objetos y clases…

Un concepto importante que hay que entender es la diferencia entre una clase y un objeto. El problema es que no existe una diferencia real entre ambos. Pero en realidad son lo mismo en diferentes puntos del tiempo.

Por ejemplo, ¿cuál es la diferencia entre un pez y un salmón?

Un salmón es un tipo particular de pez.

Voy a llamar al salmón Agustín.

¿Cuál es la diferencia entre Agustín y un salmón?

Ninguna. Agustín es una INSTANCIA de un salmón. Agustín ha sido «creado» a partir de algún otro salmón, y ahora es una cosa real que tiene atributos como salmón.

Avanzo.

El pez es una clase, el salmón es una clase y Agustín es un objeto.

Mmmmm

Un pez es una clase, lo que significa que es no es algo real, sino más bien una palabra ABSTRACTA que ATRIBUIMOS a INSTANCIAS de cosas con atributos similares. ¿Tiene aletas? ¿Branquias? ¿Vive en el agua? Bien. Probablemente es un pez.

Entonces, una persona con un doctorado viene y nos dice: «No, mi joven amiga, éste pez es en realidad un Salmo salar, vulgarmente conocido como salmón», Éste profesor sólo ha hecho una aclaración más del pez y ha creado una nueva clase llamada «salmón» que tiene atributos específicos. ¿Tiene una nariz larga, carne rojiza, es grande, vive en el mar o en agua dulce y es sabroso? Bien. Probablemente sea un salmón.

Por último, viene un cocinero y le dice al doctor: «Voy a llamar a éste salmón Agustín, y lo cocinaré a la papillote. Es una de mis especialidades. Quedará exquisito». Ahora tengo esa instancia de un salmón, que también es una instancia de un pez, llamado Agustín, que se convirtió en algo real y fué fileteado. XD Se ha convertido en un objeto.

Así que AGUSTÍN ES UN OBJETO, UN TIPO DE SALMÓN DE LA CLASE PEZ.

A ver. Entonces, ¿Un objeto es una clase pero no todas las clases son objetos? Mmmmm.

En éste caso:

  • Pez = clase
  • Salmón = clase
  • Agústín = objeto, instanciado(creado) por la clase salmón

Perfect!!

CÓMO ES ÉSTO EN CÓDIGO

¿Clase u objeto?

Un truqui:

ES-UN para objetos y/o clases que se relacionan entre si por una relación de clase (relación pez-salmón).

TIENE-UN para objetos y clases que sólo están relacionados porque hacen referencia entre si (relación salmón-branquia).

Vamos a ver si lo he entendido.

Ej. 42 - Editor 1Ej. 42 - Editor 2 Ej. 42 - Editor 3Todos mis comentarios pueden no estar bien. Son suceptibles de cambio.

ACERCA DE LAS CLASS NOMBRE (OBJECT)

Como ya he visto las diferencias entre clase y objeto, sólo añadir que Python siempre requiere (object) cuando crea una clase.

En realidad, una clase hereda de otra clase llamada object para crear su clase, pero no es objeto. Algo confuso.

Ésto viene de la interpretación original que Python le dió a la clase, y de la fusión entre las clases viejas y las nuevas.

No digo más que me lío sola. 😛

 PREGUNTAS

P: ¿Qué es el punto de self.pet = None?

R: Asegura que el atributo self.pet de esa clase se le asigna el valor None.

P: ¿Qué hace super(Employee, self).__init__(name)?

R: Así es como se ejecuta el método __init__de una clase padre con seguridad. Busca «python super» y los diversos consejos sobre su uso.

P: ¿Es posible usar una clase como un objeto?

R: Interesante pregunta. ¿Tú qué crees?

P: Mmmmm

Ej. 41 : Aprendiendo a hablar orientado a objetos…

En éste ejercicio voy a aprender cómo hablar «orientado a objetos». Voy a practicar con un pequeño conjunto de palabras que necesito conocer junto con sus definiciones. Luego practicaré con una serie de frases con huecos y tendré que entender su significado. Por último, escribiré un gran conjunto de ejercicios que tengo que completar para crear así frases sólidas en mi vocabulario.

PRACTICANDO CON LAS PALABRAS

  • class – Le dice a Python que cree un nuevo tipo de cosa.
  • objeto – Tiene dos significados: el tipo de cosa más simple y cualquier instancia de alguna cosa.
  • instancia – Lo que obtengo cuando le digo a Python que cree una clase.
  • def – Para definir una función dentro de una clase.
  • self – Dentro de las funciones de una clase, self es una variable de la instancia/objeto al que se acaba de acceder.
  • herencia – el concepto de que una clase puede heredar aspectos de otra clase, al igual que usted de sus padres.
  • composición – El concepto de que una clase puede estar compuesta de otras clases, al igual que un coche tiene ruedas.
  • atributo – Una propiedad que tienen las clases, y suelen ser variables.
  • es-un – Una frase empleada para decir que algo hereda de otra cosa, como un «salmón» es-un «pez».
  • tiene-un  – Una frase para decir que algo está compuesto de otras cosas o tiene un rasgo, como «un salmón tiene-una boca».

PRACTICANDO CON LAS FRASES

A continuación, tengo una lista de fragmentos de código Python, seguido de su significado.

  • class X(Y) «Crea una clase llamada X que tiene-un Y».
  • class X(object): def __init__(self, J) «La clase X tiene-un __init__ que recibe por parámetro self y J.»
  • class X(object): def M(self, J)  «La clase X tiene-una función llamada M que recibe por parámetro self y J».
  • foo = X()  «Asigna a foo una instancia de la clase X»
  • foo.M(J)  «Desde foo se obtiene la función M y la llama pasándole por parámetro self,J.»
  • foo.K = Q «Desde foo se obtiene el atributo K y le asigna Q.»

En cada una de esas frases, puedo tratar X, Y, M, J, K, Q y foo como espacios en blanco. Por ejemplo, podría escribir las mismas frases de la siguiente manera.

  • «Crea una clase llamada ??? que tiene-un Y.»
  • «La clase ??? tiene-un __init__ que recibe por parámetro self y ??? .»
  • «La clase ??? tiene-una función llamada ??? que recibe por parámetro self y ???.»
  • «Asigna a foo una instancia de la clase ???»
  • «Desde foo se obtiene la función ??? y la llama pasándole por parámetro self, ???.»
  • «Desde foo se obtiene el atributo ??? y le asigna ??? «

PRACTICANDO CON AMBAS

Ahora voy a combinar los ejercicios de palabras con los ejercicios de las frases. Tengo que hacer lo siguiente:

  1. Tomar una de las notas de las frases y practicar con ella.
  2. Darle la vuelta, leer la frase y cada una de las palabras de la frase que esté en los ejercicios de las palabras, recuperar las notas de las palabras.
  3. Practicar con esas palabras de esas frases.
  4. Seguir haciéndolo

UNA PRUEBA DE LECTURA

Voy a hacer un pequeño programa Python con el que practicar las palabras que ya conozco. Lo único que hace es usar una librería llamada urllibr para descargar una lista de palabras que tengo. Éste es el código. Debería entrar en opp_test.py para trabajar con él.

1    import random
2    from urllib import urlopen
3    import sys
4    
5    WORD_URL = "http://learncodethehardway.org/words.txt"
6    WORDS = []
7    PHRASES = {
8        "class %%%(%%%):":
9        "Make a class named %%% that is-a %%%.",
10       "class %%%(object):\n\tdef __init__(self, ***)" :
11       "class %%% has-a function named *** that takes self and *** parameters.",
12       "class *** (object):\n\tdef ***(self, @@@)":
13       "class %%% has-a fuction named *** that takes self and @@@ parameters",
14       "*** = %%%()":
15       "Set *** to an instance of class %%%.",
16       "***.***(@@@)":
17       "From *** get the *** function, and call it with parameters self, @@@.",
18       "***.*** = '***'":
19       "From *** get the *** attribute and set it to '***'."
21   }
22
23   # Si quiero practicar primero con frases
24   PHRASE_FIRST = False 
25   if len(sys.argv) == 2 and sys.argv[1] == "english":
26   PHRASE_FIRST = True
27   
28   # Carga las palabras de la pagina web
29   for word in urlopen(WORD_URL).readlines():
30   WORDS.append(word.strip())
31   
32 
33   def convert (snippet, phrase):
34       class_names = [w.capitalize() for w in 
35           random.sample(WORDS, snippet.count("***"))]
36       other_names = random.sample(WORDS, snippet.count("***"))
37       results = []
38       param_names = []
39   for i in range(0, snippet.count("@@@")):
40   param_count = random.randint(1,3)
41   param_names.append(', '.join(random.sample(WORDS, param_count)))
42
43
44   for sentence in snippet, phrase:
45       result = sentence[:]
46   # Nombres falsos de clase
47   for word in class_names:
48       result = result.replace("%%%", word, 1)
49   
50   # Otros nombres falsos
51   for word in other_names:
52       result = result.replace("***", word, 1)
53
54 
55   # Lista de parametros falsos
56   for word in param_names:
57       result = result.replace("@@@", word, 1)
58   
59       results.append(result)
60 
61   return results
62 
63
64   # Se ejecuta hasta pulsar CTRL- D
65   try:
66       while True:
67           snippets = PHRASES.keys()
68           random.shuffle(snippets)
69
70       for snippet in snippets:
71           phrase = PHRASES[snippet]
72           question, answer = convert (snippet, phrase)
73           if PHRASE_FIRST:
74                   question, answer = answer, question
75
76           print question
77
78           raw_input("> ")
79           print "ANSWER: %s\n\n" % answer
80   except EOFError:
81       print "\nBye"

Ejecuto el código e intento traducir las «frases orientadas a objetos» a nuestro idioma.

Y tras depurar varios errores me sale lo siguiente:Ej. 41 - Terminal 1

Nota: El diccionario PHRASES tiene ambas formas y hay que usar la correcta.

PRACTICANDO DE NUESTRO IDIOMA A CÓDIGO

A continuación, debo ejecutar el código con la «english» para practicar la opción inversa.

Éstas frases están utilizando palabras sin sentido. Parte del aprendizaje para leer código consiste en dejar de poner mucho significado en los nombres usados para variables y clases.

LEYENDO MÁS CÓDIGO

Voy a leer más código, y ésta vez, para leer las frases que acabo de aprender. Tengo que buscar todos los ficheros con clases y hacer lo siguiente:

  1. Para cada clase, anotar su nombre y escribir qué otras clases heredan de ella.
  2. Debajo de ésto, anotar un listado de todas las funciones que tiene y todos los parámetros que reciben.
  3. Hacer una lista de todos los atributos uados con self.
  4. Para cada atributo, escribir a qué clase pertenece.

El objetivo es revisar código real y empezar a aprender a «reconocer los patrones» de las frases que he aprendido y cómo se usan. Si practico lo suficiente, se supone que debería de empezar a ver esos patrones en el código, entre tanto, parecerán esos espacios en blanco que desconozco.

PREGUNTAS

P: ¿Qué hace result = sentence [:]?

R: Es la manera de copiar una lista en Python. Se usa la sintaxis [:] para obtener desde el primer elemento hasta el último.

Ej. 40 : Practicando con módulos, clases y objetos…

Y ahora si.

Python es un lenguaje de programación orientado a objetos. Lo que significa que hay una construcción en Python llamada clase que nos permite estructurar el código de una manera en particular. Usando clases podemos dotar de consistencia a nuestros programas para que puedan ser utilizados de una manera más limpia. O al ménos, esa es la teoría.

A continuación, voy a tratar de definir los principios de la programación orientada a objetos. Utilizaré lo que ya conozco como diccionarios y módulos con clases y objetos.

Allá vamos.

LOS MÓDULOS SON COMO LOS DICCIONARIOS

Ya sé como crear y usar un diccionario (bueno, estoy en ello) y que éstos me permiten hacer referencia a un elemento a partir de un nombre, lo que significa que si tengo un diccionario con la clave ‘apple’ y quiero obtenerla, hago ésto:

mystuff = {'apple': "I AM APPLES!"   (NO ME DEJA ESCRIBIR LA LLAVE DE CIERRE NI CON EL TECLADO EN PANTALLA...WHAT A...????)
 print mystuff['apple']

Mantengo en mi cabeza la idea de «obtener X a partir de Y» y ahora pienso en los módulos. Hasta el momento he hecho algunos y los he utilizado de acuerdo al siguiente proceso:

  1. Se que un módulo es un fichero Python con funciones y variables.
  2. Entonces, importo ese fichero.
  3. Puedo acceder a las funciones o variables que hay en el módulo escribiendo el operador ‘.‘ (punto).

Imagina que tienes un módulo llamado mystuff.py y contiene la siguiente función llamada apple:

# Esto está dentro de mystuff.py
 def apple():
     print "I AM APPLES!"

Después, puedo usar ese módulo con import y acceder a la función apple:

import mystuff
mystuff.apple()

También podría incluir una variable llamada tangerine, así:

def apple():
   print "I AM APPLES!"
# Esto sólo es una variable
tangerine = "Living reflection of a dream"

A continuación, puedo acceder a ésta de la forma:

import mystuff
 mystuff.apple()
 print mystuff.tangerine

Volviendo a los diccionarios, debería empezar a darme cuenta de que ésto es similar a usar un diccionario, aunque la síntaxis es diferente. Compara:

mystuff ['apple']       # Obtiene apple del diccionario
mystuff.apple()         # Obtiene apple del módulo
mystuff.tangerine       # Lo mismo, pero con una variable

Observo entonces que hay un patrón común en Python:

  1. Hay relación entre una clave y un valor (clave=valor).
  2. Obtengo un dato a partir del nombre de la clave.

En el caso del diccionario, la clave es una cadena y la síntaxis es [clave].

En el caso del módulo, la clave es un identificador y la síntaxis es .key. A parte de ésto, son casi iguales.

LAS CLASES SON COMO LOS MÓDULOS

Una forma de ver un módulo es como un diccionario especializado que puede almacenar código Python que puede usar con el operador ‘.’. Python también tiene otra construcción empleada con un propósito similar y que se llama clase. Una clase es una forma de agrupar funciones y datos y guardarlos en un contenedor de manera que se accede a ellos con el operador ‘.’.

Si tuviera que crear una clase con el módulo mystuff, haría algo así:

class MyStuff(object) :
     def _init_(self) :
     self.tangerine = "And now a thousand years between"
      def apple(self) :
     print "I AM CLASSY APPLES!"

Puede parecer complicado comparado con los módulos y aunque queda pendiente ver algunas diferencias, deberías de ser capaz de apreciar que es como un «mini-módulo» MyStuff que tiene una función apple().

Pueden resultar algo confusos también la función _init_() y el uso de self.tangerine para dar un valor a la variable tangerine.

Esta es la razón por qué las clases se utilizan en lugar de los módulos: se puede tomar la clase anterior y utilizarla para crear muchas de ellas, muchísimas, y que no interfieran entre sí. Con los módulos, al importarlo, sólo hay uno para todo el programa, a no ser que se hagan malabarismos.

Antes de entenderlo, necesito saber qué es un objeto y cómo trabajar con MyStuff tal y como lo hago con el módulo mystuff.py.

LOS OBJETOS SON COMO MINI-IMPORTS

Si una clase es como un mini-módulo, entonces tiene que haber un concepto similar para import, pero aplicado a clases. Este concepto es llamado «instanciar», que es de una forma elegante como decir «crear». Cuando se instancia una clase se obtiene lo que conocemos como un objeto.

La manera de hacerlo es llamar a la clase igual que a una función:

thing = MyStuff ()
thing.apple ()
print thing.tangerine

La primera línea es la operación «instancia» y es muy parecido a llamar a una función. Sin embargo, cuando llamamos a ésta, hay una secuencia de eventos que Python coordina. Lo veo usando el código anterior:

  1. Python busca MyStuff() y ve que es una clase que he definido.
  2. Python crea un objeto vacío, con todas las funciones que he especificado en la clase usando def.
  3. Python comprueba si he creado una función mágica _init_  y si está, llama a esa función para inicializar el objeto vacío recién creado.
  4. En la función _init_ de MyStuff tomo la variable extra self, que es el objeto vacío que Python creó, y asigno valores a las variables utilizándolo igual que hacía con los módulos y otros objetos.
  5. En éste caso, asigno como valor a self.tangerine la letra de una canción y luego inicializo ese objeto.
  6. Ahora, Python puede tomar ese objeto y asignarlo a la variable thing para que trabaje con él.

Esto es lo básico de como Python crea los «mini-imports» cuando se llama a una clase como una función. Hay que recordar que ésto no crea una clase, sino que está utilizando una clase como un molde para crear copias de cosas de ese tipo.

En realidad ésto es una idea un tanto inexacta de cómo trabajan las clases y objetos para que se pueda empezar a comprender basándome en lo que ya conozco sobre módulos. La verdad es que las clases y lo objetos divergen de los módulos a partir de éste punto. Es 100% cierto lo siguiente:

  • Las CLASES son como moldes o definiciones que permiten crear nuevos mini-módulos.
  • Las INSTANCIAS son la forma de crear uno de éstos mini-módulos e importarlos en el mismo momento.
  • El mini-módulo creado se llama OBJETO y lo asigno a una variable para trabajar con él.

Las clases y los objetos son muy diferentes de los módulos y lo anterior sólo deve servirme para entender el concepto de clase.

OBTENIENDO COSAS DE THINGS

Hay 3 formas de obtener cosas de things:

# Estilo diccionario
mystuff['apples']   (En relidad serían llaves!!!)
# Estilo módulo
mystuff.apples()
print mystuff.tangerine
# Estilo clase
thing = MyStuff ()
thing.apples ()
print thing.tangerine

PRIMER EJEMPLO DE CLASES

Debería comenzar a ver las similitudes entre estos tres tipos de contenedores clave = valor y, además, tengo bastantes preguntas que iré resolviendo a partir del próximo ejercicio. Voy a terminar escribiendo algo de código, para ir practicando antes de continuar.

Ej. 40 - Programa con Errores

Ej. 40 - Terminal con Errores

Me da un error en la línea 12 que mareo y cambio cosas (pongo corchetes, espacios, borro, vuelvo a escribir…) y sigue dando error.

……

Al final descubro que aunque dice que el error está en la línea 12, es un problema de definición de los parámetros, el error parece estar al principio…

Necesitaba poner doble guión bajo en la línea 3, de éste modo:   def __ init__(self, lyrics):

Ej. 40 - Programa con Errores 2Ej. 40 - Terminal con Errores 2 peq

Ejecutando me da error, ésta vez en la línea 15, pero de nuevo me fijo en las líneas del principio…

En fin. Por hoy termino. Mañana retomo.

Buenas noches!!

Ej. 40 - Programa corregidoEj. 40 - Terminal final

Por fiiin. Que no lo veía. Eran los ([]) y no [()]. Hay veces que por una tontería el atasco es absurdo…bueno. Así aprendo para la próxima vez. La verdad es que estaba fijándome en lo que decía el terminal:

TypeError: 'type' object has no attribute ' __getitem__'

¿No es confuso? En fin. A veces sólo hay que fijarse mejor en los detalles del programa. La respuesta estaba ahí.

🙂

PREGUNTAS

P: ¿Por qué necesito self cuando creo _init_ u otra función de la clase?

R: Si no usas self, el código cheese = ‘Frank’ es ambiguo. En ese código no está claro si nos referimos al atributo cheese de la instancia o si es una variable local (?) llamada cheese. Con self.cheese = ‘Frank’ queda muy claro que nos estamos refiriendo al atributo de la instancia self.cheese.