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. 39 : Trabajando con diccionarios…

Voy a ver otro contenedor que puedo utilizar que me abrirá un mundo de posibilidades. Se trata de el contenedor más útil: el diccionario.

Python los llama «dicts». Otros lenguajes los llaman «hashes». Lo que impota es lo que hacen cuando se comparan con las listas. Una lista puede hacer cosas como las siguientes:

Ej. 39 - Python ej lista

Podemos usar los números para indicar el número de una lista, lo que significa que podemos usar números para encontrar lo que hay en ella (ver ejercicio 34).

Lo que hace un diccionario es permitirme usar cualquier cosa, no sólo números. Un diccionario asocia una cosa a otra, sin importar lo que sea. Ejemplo:

Ej. 39 - Python ej lista 2

En lugar de usar sólo números, estoy usando cadenas para decir que elementos quiero. También puedo añadir elementos nuevos al diccionario usando las cadenas de texto, los nombres.

También puedo hacer lo siguiente:

Ej. 39 - Python ej lista 3 error

Vaya. ¡No me deja!

Mmmmmm. Es que he cerrado Python, y debería de haber continuado desde la pantalla anterior…

Ej. 39 - Python ej lista 3

Ahora si. En éste código he usado números y luego puedo ver que hay números y cadenas como claves en el diccionario cuando lo muestro en la pantalla.

Por supuesto, un diccionario en el que sólo se pueden poner cosas es muy aburrido. Se borra con la palabra clave del de la siguiente forma:

Ej. 39 - Python ej lista 4

Bien. Ahora escribo un programa interesante.

Ej. 39 - Programa 1

Me da el siguiente error en la línea 3.

Ej. 39 - Programa 1 error

He cambiado cosas (he puesto lo del código ASCII en la línea 1) y no doy con la tecla.

El error está ahora en la línea 4. 😦

No lo puedo ejecutar todavía. Jur. Dejo el resto del código aquí, aunque como todavía no lo he ejecutado, no se si tiene errores o no.

Ej. 39 - Programa 2

Corrijo cosas básicas que estaban mal desde el ppio. Los diccionarios usan LLAVES, NO CORCHETES.

Ej. 39 - Programa con errores

La línea 20 y 21 las cambio también, no estaban bien planteadas. Igual hay otra forma más parecida a lo que había en el anterior programa, pero yo he tirado de lo conocido (arriba).

Al ejecutar, voy depurando y me va saliendo lo siguiente, hasta el siguiente atasco:

Ej. 39 - Programa errores

Error en la línea 25. Cambio a llaves, pero me sale lo mismo. Me temo que es la estructura, aunque me diga invalid syntax. ¿Está mal planteado? Lo que sigue a continuación también estará mal…porque es lo mismo… 😛

Bien. Ya he salido del bache. Dando vueltas y con nuestro método favorito: pueba-error mientras sigo observando. Love it. Éste es el programa corregidoEj. 39 - Programa 1 corregidoEj. 39 - Programa 2 corregido

Que ejecutando resulta:

Ej. 39 - Terminal corregido

Nice. 🙂

PREGUNTAS

P: ¿Qué diferencia hay entre  una lista y un diccionario?

R: Una lista es una lista ordenada de elementos. Un diccionario (o dict) es para crear una relación entre unos elementos (llamados «clave») y otros (llamados «valores»).

P: ¿Para qué debería de usar un diccionario?

R: Úsalo siempre que tengas que tomar un valor «consultando» otro valor. De hecho, podrían llamarse «tablas de consulta».

P: ¿Para qué debería de usar una lista?

R: Una lista es para cualquier secuencia de cosas que necesitan estar ordenadas y sólo necesitas consultarlas por un índice numérico.

P: ¿Y si necesito un diccionario, pero necesito que esté ordenado?

R: Echa un vistazo a la siguiente estructura de datos de Python: collections.OrderedDict. Búscalo en internet y lee documentación sobre ésto.

Por curiosidad, también lo miro en pydoc y me sale ésto:

Ej. 39 - Pydoc collections.OrderedDict

Ej. 38 : Practicando con listas…

Cuando escribo el código Python mysuff.append(‘hello’), en  realidad se están desencadenando una serie de procesos dentro de Python para que suceda algo a la lista mystuff. Así funciona:

  1. Python ve que indiqué mystuff y revisa esa variable. Puede que revise si anteriormente se creó con un =, si se trata de un parámetro de una función o si es una variable global. En cualquier caso, tiene que encontrar donde aparece por primera vez mystuff.
  2. Una vez encuentra mystuff, llega al operador . (punto) y empieza a revisar las variables que son parte de mystuff. Desde el momento que sabe que mystuff es una lista, también sabe que tiene muchas funciones asociadas.
  3. Luego encuentra append y lo compara con todas las funciones y variables de mystuff. Si append está ahí, la usa.
  4. A continuación, Python ve los paréntesis y dice «¡Oh! Ésto debe de ser una función». En ese momento, llama o ejecuta la función con un parámetro extra.
  5. El parámetro extra es…¡¡mystuff!! Lo sé, es raro, ¿verdad? Pero así es como funciona Python, así que lo mejor es simplemente recordarlo y asumir que eso es lo correcto. Lo que ocurre es que la llamada a la función, en el fondo se procesa de una forma similar a append (mystuff, ‘hello’), en lugar de mystuff.append(‘hello’).

No es necesario saber que ésto está pasando pero me será de ayuda para cuando tenga mensaje de error como éste:

>>> class Thing(object) :

…  def test (hi) :

…  print «hi»

>>> a = Thing ()

>>> a.test («hello»)

Traceback (most recent call last): 

File «<stdin>», line 1, in <module>

TypeError: test () takes exactly 1 argument  (2 given)

>>>

Más adelante lo veré, pero el error significa que Python cambió a.test(«hello») por test(a,»hello») y que en algún lugar alguien se confundió y no añadió el argumento o parámentro para a.

Dedicaré algunos ejercicios para que éste concepto quede claro. Haré éste ejercicio que mezcla cadenas y listas para divertirme un poco.

Ej. 38 - Editor Ej. 38 - Terminal

PREGUNTAS

P: ¿No dijimos que no era recomendable usar bucles while?

R: Las reglas se pueden romper si tienes una buena razón. No hay que ser esclavos de las reglas.

P: ¿Qué hace stuff[3:5]?

R: Obtiene una parte de la lista, del elemento 3 al 4, lo que significa que no incluye el 5. Es similar a como funciona range (3,5).  Aunque no es lo mismo, ¿eh?. Con stuff[3:5] se obtiene una sublista de stuff, tenga lo que tenga, range sólo devuelve números.

P: ¿Por qué no funciona join(‘ ‘, stuff)?

R: Esa forma no tiene sentido. No funciona de esa manera, ya que join es un método asociado a una cadena. Reescríbelo así: ‘ ‘.join(stuff).

+ EJERCICIOS

  1. Toma cada llamada a una función y revísala siguiendo los pasos descritos anteriormente para traducirlo a lo que hace Python. Por ejemplo,   ‘ ‘.join(cosas) es join(‘ ‘ , cosas).
  2. Traduce estas dos formas de ver las llamadas a funciones. Por ejemplo, ‘ ‘.join(cosas) se puede leer como «join cosas aplicado a ‘ ‘ «. Otra forma es «Llamada a join con ‘ ‘ y cosas». En realidad es lo mismo.
  3. Busca en internet sobre PROGRAMACIÓN ORIENTADA A OBJETOS (OOP).
  4. Busca que es class en Python, no en otros lenguajes, que puede dar lugar a confusión.
  5. ¿Cuál es la relación entre dir(algo) y la class de algo?

Ej. 35 : Tomando decisiones con funciones…

Ya he aprendido a hacer la sentencia if, funciones y listas. Ahora ha llegado el momento de mezclarlas. Voy a escribir el siguiente código e intentaré averiguar qué hace.

Ej. 35 - Programa 1Ej. 35 - Programa 2Ej. 35 - Programa 3

Ejecutando, y eligiendo opciones, me sale lo siguiente:

Ej. 35 - Terminal 1 opción

Obviamente, éste programa es tremendamente interesante para darle millones de vueltas.

Lo retomaré en un par de post para:

  1. Dibujar un mapa del juego y ver si he entendido el recorrido.
  2. Escribir comentarios en el programa
  3. Darle vueltas al juego, para ampliarlo o simplificarlo.

PREGUNTAS

P: ¿Qué hace while True:?

R: Crea un bucle infinito.

P: ¿Qué hace exit(0)?

R: En muchos sistemas operativos un programa puede abortar la ejecución con exit(0), indicando con el número pasado por parámetro si hubo un error o no. Si ejecutas exit(1), se aborta con el error, pero si se hace con exit(0), se terminará correctamente. El motivo por el que es al revés de la lógica booleana normal (con 0==False), es que así se puede usar diferentes números para indicar diferentes errores. Puedes terminar el programa con exit(100) para errores con resultados diferentes que con exit(2) o exit(1).

Ej. 34 : Accediendo a los elementos de las listas…

Las listas son muy útiles, pero sólo si podemos acceder a lo que contienen. Ya se recorrer los elementos de una lista en orden, pero, ¿y si quiero acceder concretamente al quinto elemento? Necesito saber cómo acceder a los elementos de una lista. Así es como accedo al primer elemento:

animals = [‘bear’, ‘tiger’, ‘penguin’, ‘zebra’]

bear = animals [0]

¿Parto de una lista de nombres de animales y para obtener el primer elemento uso un cero?¿cómo puede ser?

Así es como funcionan las matemáticas, Python comienza sus listas en 0 en lugar de en 1. Parece raro, pero tiene muchas ventajas.

La mejor manera de explicar por qué es motrando la diferencia entre cómo usamos los números el resto de los mortales, y cómo los usan los programadores.

En general, el orden de los animales es importante. En una carrera, no puedo tener el segundo animal sin el primero, ni el tercero sin el segundo. También es imposible tener un animal «cero», ya que el cero no significa nada. Llamamos a éste tipo de números «ordinales» porque indican un orden.

Los programadores, sin embargo, no piensan de esa manera, porque pueden tomar cualquier elemento de una lista en cualquier momento. Para ellos, la lista anterior es más como una baraja de cartas. Si quieren el tigre, lo cogen. Si quieren la zebra, también pueden cogerla. Ésta necesidad de coger los elementos de la lista al azar significa que necesitan una forma de indicar o hacer referencia a los elementos a través de una dirección o un «índice», y la mejor forma de hacerlo es que el índice empiece en 0. Las mateméticas son la manera más fácil, para éste tipo de accesos. Éste tipo de número es un número «cardinal» y significa que puede escoger al azar y es necesario que haya un elemento cero.

¿De qué sirve ésto en las listas? En pocas palabras, cada vez que quiero coger el tercer animal, traduzco ese número «ordinal» en número «cardinal», restándole 1. El «tercer»  animal está en el índice 2 y es el pingüino. Debo hacer ésto porque me he pasado toda la vida usando los números ordinales y ahora hay que pensar en cardinal. Simplemente resto uno y todo irá bien. Recuerda:

ordinal == ordenado, 1º cardinal == cartas al azar, 0

Voy a practicar.

Recordar que si digo «primero», «segundo» y siguientes, entonces estoy usando ordinales y debo restar 1. Si indico un cardinal (0, 1, 2…), entoces lo uso directamente.

animals = [‘bear’, ‘python’, ‘peacock’, ‘kangaroo’, ‘whale’, ‘platypus’]

  1. El animal de la posición 1  …   es el segundo animal, y es una pitón.
  2. El tercer animal.  …  está en la posición 2, y es un pavo real (un auténtico pavo XD).
  3. El primer animal   …   está en la posición 0, y es un oso.
  4. El animal de la posición 3   …   es el cuarto animal y es un canguro.
  5. El quinto animal   …   está en la posición 4, y es una ballena.
  6. El animal de la posición 2   …   es el tercer animal y es un pavo real.
  7. El sexto animal   …   está en la posición 5 y es un ornitorrinco.
  8. El animal de la posición 4   …  es el quinto animal y es una ballena.

Puedo usar Python para comprobar si las respuestas son correctas.

+EJERCICIOS

  1. Ampliar info sobre números cardinales y ordinales
  2. Y ahora una pregunta interesante. ¿Por qué en el año 2010 en «1 de enero de 2010» realmente es 2010 y no 2009? Pista: no se pueden tomar años al azar.

Ej. 32 : Aprendiendo con bucles y listas…

Ahora puedo hacer programas mucho más interesantes.

Sin embargo, los programas también necesitan hacer cosas de manera repetitiva y muy rápida.

En éste ejercicio voy a utilizar un bucle para generar e imprimir varias listas.

Antes de usar un bucle for, necesitamos una forma de almacenar en algún lugar los resultados del bucle. La mejor manera es hacerlo es una lista. Una lista es exactamente lo que dice su nombre, un contenedor de cosas que están organizadas en orden. No es complicado. Sólo hay que aprender una sintaxis nueva. Ésta es la forma de crear una lista:

hairs = [‘brown’, ‘blond’, ‘red’]

eyes = [‘brown’, ‘blue’, ‘green’]

weights =[1, 2, 3, 4]

Lo que hago es empezar la lista con el corchete izquierdo para abrir la lista. Luego escribo cada uno de los elementos que deseo que contenga la lista. Igual que hacía con los parámetros de las funciones. Por último, termino y cierro con corchete derecho. Python toma ésta lista y todo su contenido, y lo asigna a una variable.

Creo algunas listas usando bucles y las muestro en pantalla:


Ej. 32 - Editor Ej. 32 - Terminal

NOTA: A muchos cerebros les han enseñado que el mundo es plano. En el ejercicio anterior, tenía sentencias del tipo if dentro de otro if. Para algunos, «anidar» unas cosas dentro de otras puede dar lugar a confusión. En programación ésto está por todos lados. Encontraré funciones que llaman a otras funciones que tienen sentencias if y listas con listas dentro de listas. Ante problemas, sugiero papel y lápiz, para un análisis manual.

PREGUNTAS

P: ¿Cómo se hace una lista de dos dimensiones?

R: De una forma similar a ésta: [[1, 2, 3], [4, 5, 6]].

P: ¿Es lo mismo una lista que un array?

R: Interesante. Depende del lenguaje y la implementación. Estrictamente, las listas son muy diferentes a los arrays debido a la forma de implementación. En Ruby, las listas se denominan arrays. En Python, nos referimos a ellas como listas. Por ahora las llamamos listas, ya que Python las llama así.

P: ¿Cómo puede un bucle for usar una variable que no había sido definida antes?

R: La variable queda definida en el bucle y toma un valor en cada iteración.

P: Mmmmm. ¿Y qué es una iteración?

R: De momento, voy a remitirme a las palabras de Wikipedia…

En programación, Iteración es la repetición de un proceso dentro de un programa de computadora. Puede usarse tanto como un término genérico (como sinónimo de repetición) así como para describir una forma específica de repetición con un estado mutable (el ejercicio es el segundo caso).

Cuando se usa en el primer sentido, la recursividad es un ejemplo de iteración, pero que usa su propia notación (notación recursiva), que no es el caso de iteración.

Sin embargo, cuando se usa en el segundo sentido (caso más restringido), como vemos en el ejercicio, la iteración describe el estilo de programación usado en lenguajes de programación imperativa. Esto está en contraposición de la recursividad, la cual tiene un enfoque más declarativo.

He aquí un ejemplo de iteración basándose en asignación destructiva, en pseudocódigo imperativo:

 var i=0, a := 0        // inicializo a antes de comenzar la iteración
 for i from 1 to 3 {  // ciclo 3 veces
     a = a + i       // incremento a con el valor actual de i
     print a              // se imprime el número 6
     }

En este fragmento de programa, el valor de la variable i cambia a medida que la ejecución del programa progresa, tomando los valores 1, 2 y 3. Este cambio de valor —o estado mutable— es característico de una iteración.

Es un tema que da para mucho. Lo retomaré.

P: ¿Qué hace la función range exactamente?

R: Vamos a preguntar a pydoc:

Pydoc - range

P: ¿Por qué for i in range (1, 3): sólo tiene dos iteraciones en lugar de tres?

R: La función range () va del primer al último número, pero no incluye el último. Así que se detiene en dos, no en tres. Es la forma más común de funcionamiento de éste tipo de bucles.

P: ¿Qué hace elements.append () ?

R: Simplemente añade algo al final de la lista. Abre la línea de comandos de Python y prueba ejemplos con cada lista que crees. Así se ve mejor.

PENDIENTE

  1. Buscar info en la documentación de Python sobre las listas. ¿Qué otras operaciones se pueden usar para añadir elementos a las listas?
  2. Profundizar sobre iteración y recursividad.