Cesar Systems

Herramientas Informaticas

13.6. Generalización

De algún modo, convertir de base 60 a base 10 y de vuelta es mas difícil que simplemente manejarse con las horas. La conversión de base es mas abstracta; nuestra intuición para tratar con las horas es mejor.

Pero si tenemos la comprensión para tratar las horas como números en base 60, y hacer la inversión de escribir las funciones de conversión (convierteASegundos y haceHora), obtenemos un programa que es mas corto, mas fácil de leer y
depurar y mas fiable.

También es mas fácil añadir funcionalidades mas tarde. Por ejemplo, imagine restar dos Horas para hallar el intervalo entre ellas. La aproximación ingenua sería implementar la resta con acarreo. Con el uso de las funciones de conversión será mas fácil y con mayor probabilidad, correcto.

Irónicamente, a veces hacer un problema mas complejo (o mas general) lo hace mas fácil (porque hay menos casos especiales y menos oportunidades de error).

13.7. Algoritmos

Cuando escribe una solución general para una clase de problemas, en contraste con una solución específica a un problema concreto, ha escrito un algoritmo.

Mencionamos esta palabra antes pero no la definimos con precisión. No es fácil de definir, así que probaremos un par de enfoques.

Primero, piense en algo que no es un algoritmo. Cuando usted aprendió a multiplicar números de una cifra, probablemente memorizo la tabla de multiplicar.

En efecto, memorizo 100 soluciones específicas. Ese tipo de conocimiento no es algorítmico.

Pero si usted era “haragán” probablemente hizo trampa aprendiendo algunos trucos.
Por ejemplo, para encontrar el producto de n por 9, puede escribir n-1 como el primer dígito y 10 – n como el segundo dígito. Este truco es una solución general para multiplicar cualquier numero de una cifra por 9.  ¡Eso es un algoritmo!

De forma similar, las tecnicas que aprendió para la suma y la resta con acarreo y la división larga son todas algoritmos. Una de las características de los algoritmos es que no requieren inteligencia para llevarse a cabo. Son procesos mecánicos en los que cada paso sigue al anterior de acuerdo a un conjunto simple de reglas.

En nuestra opinión, es un poco vergonzoso que los humanos pasen tanto tiempo en la escuela aprendiendo a ejecutar algoritmos que, de forma bastante similar, no exigen inteligencia.

Por otra parte, el proceso de diseñar algoritmos es interesante, un desafío intelectual y una parte primordial de lo que llamamos programar.

Algunas de las cosas que la gente hace naturalmente, sin dificultad ni pensamiento consciente, son las mas difíciles de expresar algorítmicamente. Entender el lenguaje natural es un buen ejemplo. Todos lo hacemos, pero hasta el momento nadie ha sido capaz de explicar como lo hacemos, al menos no en la forma de un algoritmo.

Nomas te vengo avisar

Nomas te vengo avisar, que no puedo venir a verte
y que un mañana quizás, yo sufra por no tenerte.
Te vengo a pedir perdón, perdón porque yo me alejo
Por causas tal vez ajenas, y que me marcan esta condena.

TE QUIERO TANTO Y TENGO MIEDO,
DE QUE ME OLVIDES POR QUE NO PUEDO
VENIR A VERTE COMO QUEDAMOS,
POR QUE MAÑANA MARCHAR ME DEBO.

Te vengo a pedir perdón, perdón porque yo me alejo
Por causas tal vez ajenas, y que me marcan esta condena.

TE QUIERO TANTO Y TENGO MIEDO,
DE QUE ME OLVIDES POR QUE NO PUEDO
VENIR A VERTE COMO QUEDAMOS,
POR QUE MAÑANA MARCHAR ME DEBO.

TE QUIERO TANTO Y TENGO MIEDO,
DE QUE ME OLVIDES POR QUE NO PUEDO
VENIR A VERTE COMO QUEDAMOS,
POR QUE MAÑANA MARCHAR ME DEBO.

 

13.8. Glosario

función pura: Una función que no modifica ninguno de los objetos que recibe como parámetros. La mayor³a de las funciones puras son rentables.

modificador: Una función que modifica uno o mas de los objetos que recibe como parámetros. La mayor³a de los modificadores no entregan resultado.

estilo funcional de programación: Un estilo de programación en el que la mayor³a de las funciones son puras.

desarrollo de prototipos: Una forma de desarrollar programas empezando con un prototipo y probándolo y mejorándolo gradualmente.

desarrollo planificado: Una forma de desarrollar programas que implica una comprensión de alto nivel del problema y mas planificación que desarrollo incremental o desarrollo de prototipos.

algoritmo: Un conjunto de instrucciones para solucionar una clase de problemas por medio de un proceso mecánico sin intervención de inteligencia.

Clases y métodos

14.1. Características de la orientación a objeto

Python es un lenguaje de programación orientado a objetos, lo que significa que proporciona características que apoyan la programación orientada a objetos.

No es fácil definir la programación orientada a objetos, pero ya hemos visto
algunas de sus características:

Los programas se hacen a base de definiciones de objetos y definiciones de funciones, y la mayor parte de la computación se expresa en términos de operaciones sobre objetos.

Cada definición de un objeto se corresponde con un objeto o concepto del mundo real, y las funciones que operan en ese objeto se corresponden con las formas en que interactúan los objetos del mundo real.

Por ejemplo, la clase Hora definida en el Capítulo 13 se corresponde con la forma en la que la gente registra la hora del día, y las funciones que definimos se corresponden con el tipo de cosas que la gente hace con las horas. De forma similar, las clases Punto y Rectangulo se corresponden con los conceptos matemáticos de un punto y un rectangulo.

Hasta ahora, no nos hemos aprovechado de las características que Python nos ofrece para dar soporte a la programación orientada a objetos. Hablando estrictamente, estas características no son necesarias. En su mayor³a, proporcionan una sintaxis alternativa para cosas que ya hemos hecho, pero en muchos casos,la alternativa es mas concisa y expresa con mas precisión a la estructura del programa.

Por ejemplo, en el programa Hora no hay una conexión obvia entre la definición de la clase y las definiciones de las funciones que siguen. Observando bien, se hace patente que todas esas funciones toman al menos un objeto Hora como
parámetro.

Esta observación es la que motiva los métodos. Ya hemos visto varios métodos, como keys y values, que se invocan sobre diccionarios. Cada metodo esta asociado con una clase y esta pensado para invocarse sobre instancias de esa clase.

  • Los métodos son como las funciones, con dos diferencias:
  • Los métodos se definen dentro de una definición de clase para explicitar la relación entre la clase y el metodo.

La sintaxis para invocar un metodo es diferente de la de una llamada a una función.

En las próximas secciones tomaremos las funciones de los capítulos anteriores y las transformaremos en métodos. Esta transformación es puramente mecánica; puede hacerla simplemente siguiendo una secuencia de pasos. Si se acostumbra
a convertir de una forma a la otra será capaz de elegir la mejor forma de hacer lo que quiere.

14.2. imprimeHora

En el Capítulo 13, definimos una clase llamada Hora y escribimos una función llamada imprimeHora, que debería ser parecida a esto:

   1: class Hora:

   2: pass

   3: def imprimeHora(hora):

   4: print str(hora.horas) + ":" +

   5: str(hora.minutos) + ":" +

   6: str(hora.segundos)

Para llamar a esta función, pasábamos un objeto Hora como parámetro:

   1: >>> horaActual = Hora()

   2: >>> horaActual.horas = 9

   3: >>> horaActual.minutos = 14

   4: >>> horaActual.segundos = 30

   5: >>> impriemHora(horaActual)

Para convertir imprimeHora en un metodo, todo lo que necesitamos hacer es mover la definición de la función al interior de la definición de la clase. Fíjese en como cambia el sangrado.

 

   1: class Hora:

   2:     def imprimeHora(hora):

   3:         print str(hora.horas) + ":" +

   4:         str(hora.minutos) + ":" +

   5:         str(hora.segundos)

Ahora podemos invocar imprimeHora usando la notación de punto.

   1: >>> horaActual.imprimeHora()

Como es habitual, el objeto sobre el que se invoca el metodo aparece delante del punto y el nombre del metodo aparece tras el punto.

El objeto sobre el que se invoca el metodo se asigna al primer parámetro, así que en este caso horaActual se asigna al parámetro hora.

Por convenio, el primer parámetro de un metodo se llama self. La razón de esto es un tanto rebuscada, pero se basa en una metáfora útil.

La sintaxis para la llamada a una función, imprimeHora(horaActual), sugiere que la función es el agente activo. Dice algo como ¡Oye imprimeHora! Aquí hay un objeto para que lo imprimas”.

En programación orientada a objetos, los objetos son los agentes activos. Una invocación como horaActual.imprimeHora() dice ¡Oye horaActual Imprímete!

Este cambio de perspectiva puede ser mas elegante, pero no es obvio que sea útil. En los ejemplos que hemos visto hasta ahora, puede no serlo. Pero a veces transferir la responsabilidad de las funciones a los objetos hace posible escribir funciones mas versátiles, y hace mas fácil mantener y reutilizar código.

14.3. Otro ejemplo

Vamos a convertir incremento (de la Sección 13.3) en un metodo. Para ahorrar espacio, dejaremos a un lado los métodos ya definidos, pero usted debería mantenerlos en su versión:

   1: class Hora:

   2:     #aquí van las definiciones anteriores de métodos...

   3:     def incremento(self, segundos):

   4:         self.segundos = segundos + self.segundos

   5:         while self.segundos >= 60:

   6:             self.segundos = self.segundos - 60

   7:             self.minutos = self.minutos + 1

   8:         while self.minutos >= 60:

   9:             self.minutos = self.minutos - 60

  10:             self.horas = self.horas + 1

La transformación es puramente mecánica; hemos llevado la definición del metodo al interior de la definición de la clase y hemos cambiado el nombre del primer parámetro.

Ahora podemos invocar incremento como un metodo.

   1: horaActual.incremento(500)

De nuevo, el objeto sobre el que invocamos el metodo se asigna al primer parámetro, self. El segundo parámetro, segundos toma el valor de 500.

Como ejercicio, convierta convertirASegundos (de la Sección 13.5) en un metodo de la clase Hora.

14.4. Un ejemplo más complicado

La función después es ligeramente mas complicada porque opera sobre dos objetos Hora, no solo sobre uno. Solo podemos convertir uno de los parámetros en self; el otro se queda como esta:

   1: class Hora:

   2: #aquí van las definiciones anteriores de métodos...

   3:     def despues(self, hora2):

   4:         if self.horas > hora2.horas:

   5:             return 1

   6:         if self.horas < hora2.horas:

   7:             return 0

   8:         if self.minutos > hora2.minutos:

   9:             return 1

  10:         if self.minutos < hora2.minutos:

  11:             return 0

  12:         if self.segundos > hora2.segundos:

  13:             return 1

  14:         return 0

Invocamos este método sobre un objeto y pasamos el otro como argumento:

   1: if horaHecho.despues(horaActual):

   2: print "El pan estará hecho después de empezar."

 

Casi puede leer la invocación como una mezcla de ingles y español: Si la horahecho es después de la horaactual, entonces…”

 

14.5. Argumentos opcionales

Hemos visto funciones internas que toman un numero variable de argumentos.

Por ejemplo, string.find puede tomar dos, tres o cuatro argumentos.

Es posible escribir funciones definidas por el usuario con listas de argumentos opcionales. Por ejemplo, podemos modernizar nuestra propia versión de encuentra para que haga lo mismo que string.find.

Esta es la versión original de la Sección 7.7:

   1: def encuentra(cad, c):

   2:     indice = 0

   3:         while indice < len(cad):

   4:             if str[indice] == c:

   5:             return indice

   6:         indice = indice + 1

   7:     return –1

 

Esta es la versión aumentada y mejorada:

   1: def encuentra(cad, c, comienzo=0):

   2:     indice = comienzo

   3:     while indice < len(cad):

   4:         if str[indice] == c:

   5:             return indice

   6:         indice = indice + 1

   7:     return –1

 

El tercer parámetro, comienzo, es opcional porque se proporciona un valor por omisión, 0. Si invocamos encuentra solo con dos argumentos, utilizamos el valor por omisión y comenzamos por el principio de la cadena:

   1: >>> encuentra("arriba", "r")

   2: 1

Si le damos un tercer parámetro, anula el predefinido:

   1: >>> encuentra("arriba", "r", 2)

   2: 2

   3: >>> encuentra("arriba", "r", 3)

   4: –1

 

Como ejercicio, a~nada un cuarto parámetro, fin, que especifique donde dejar de buscar.

Cuidado: Este ejercicio tiene truco. El valor por omisión de fin debería ser len(cad), pero eso no funciona. Los valores por omisión se evalúan al definir la función, no al llamarla. Cuando se define encuentra, cad aun no existe, así que no puede averiguar su longitud.

Página 121 de 144

Creado con WordPress & Tema de Anders Norén