Herramientas Informaticas

Mes: julio 2012 Página 1 de 6

A.3.3. Tengo una función o método que no devuelve lo que esperaba.

Si tiene una sentencia return con una expresión compleja no tendrá la oportunidad de imprimir el valor de retorno antes de volver. De nuevo, puede usar una variable temporal. Por ejemplo, en lugar de:
 

   1: return self.manos[i].eliminaCoincidencias()

podría escribir:

   1: cant = self.manos[i].eliminaCoincidencias()
   2: return cant

Ahora ya tiene la oportunidad de mostrar el valor de can’t antes de regresar.

A.3.4. Estoy atascado de verdad y necesito ayuda.

Primero, intente alejarse del computador durante unos minutos. Los computadores emiten unas ondas que afectan al cerebro provocando estos efectos:
  • Frustración y/o furia.
  • Creencias supersticiosas (el computador me odia”) y pensamiento mágico (el programa solo funciona cuando me pongo la gorra hacia atrás”).
  • Programar dando palos de ciego (el empeño de programar escribiendo todos los programas posibles y eligiendo el que hace lo correcto).
Si se encuentra afectado por alguno de estos síntomas, levántese y de un paseo.
Cuando este calmado, piense en el programa.
  • ¿Que es lo que hace? 
  • ¿Cuales pueden ser las causas de tal comportamiento?
  • ¿Cuando fue la ultima vez que tenia un programa que funcionaba y que fue lo siguiente que hizo?
A veces lleva tiempo encontrar un error. Muchas veces encontramos errores cuando estamos lejos del computador y divagamos.
Algunos de los mejores lugares para encontrar errores son los trenes, las duchas y la cama, justo antes de quedarse dormido.

A.3.5. No, de verdad necesito ayuda.

Sucede. Incluso los mejores programadores se atascan de vez en cuando. A veces trabajan durante tanto tiempo en un programa que no puede ver el error.

Lo que necesita es un par de ojos nuevos.

  • Antes de llamar a nadie, asegúrese de que ha agotado las tecnicas explicadas aquí. Su programa debería ser tan simple como sea posible, y usted debería estar trabajando con la entrada mínima que provoca el error. Debería tener sentencias print en los lugares adecuados (y lo que dicen debería ser comprensible). Debería entender el problema lo bastante bien como para describirlo sucintamente.
  • Cuando llame a alguien para que le ayude, asegúrese de darles la información que necesitan: Si hay un mensaje de error, cual es y que parte del programa señala?
  • Que fue lo ultimo que hizo antes de que apareciera el error? Cuales son las ultimas líneas de código que escribió, o cual es el nuevo caso de prueba que no cumple?
  • Que ha intentado hasta ahora y que ha averiguado?
  • Cuando encuentre el error, tómese un momento para pensar acerca de lo que podría haber hecho para encontrarlo mas rápido. La siguiente vez que vea algo parecido, será capaz de encontrar el error antes.
  • Recuerde, el objetivo no es solo hacer que el programa funciones. El objetivo es aprender como hacer funcionar al programa.

Crear un nuevo tipo de datos en Python

Los lenguajes de programación orientados a objetos permiten a los programadores crear nuevos tipos de datos que se comporten de manera muy parecida a los tipos de datos nativos. Exploraremos esta posibilidad construyendo una clase Fracción que funcione de manera muy similar a los tipos numéricos nativos, enteros, enteros largos y flotantes.
Las fracciones, también conocidas como números racionales, son valores que pueden expresarse como la proporción entre dos números enteros, tal como 5=6.
Al numero superior se se le llama numerador y al inferior se se le llama denominador.
Comenzamos definiendo la clase Fracción con un método de inicialización que nos surta de un numerador y un denominador enteros:
   1: class Fracción:
   2:     def __init__(self, numerador, denominador=1):
   3:         self.numerador = numerador
   4:         self.denominador = denominador

El denominador es opcional. Una Fracción con un solo parámetro representa un numero entero. Si el numerador es n, construimos la fracción n=1.

El siguiente paso es escribir un método str para que imprima las fracciones de forma que tenga sentido. La forma natural de hacerlo es numerador/denominador”:

   1: class Fracción:
   2:     ...
   3:     def __str__(self):
   4:     return "%d/%d" % (self.numerador, self.denominador)

Para probar lo que tenemos hasta ahora, lo ponemos en un ¯chero llamado Fracción.py y lo importamos desde el interprete de Python. Entonces creamos un objeto fracción y lo imprimimos.

>;>> from Fracción import fracción
>;>> mortadela = Fracción(5,6)
>;>> print "La fracción es", mortadela
La fracción es 5/6

Como siempre, la función print invoca implícitamente al método str .

B.1. Multiplicación de fracciones Python

Nos gustaría poder aplicar las operaciones normales de suma, resta, multiplicación y división a las fracciones. Para ello, podemos sobrecargar los operadores matemáticos para los objetos de clase Fracción.
Comenzaremos con la multiplicación porque es la mas fácil de implementar.
Para multiplicar dos fracciones, creamos una nueva fracción cuyo numerador es el producto de los numeradores de los operandos y cuyo denominador es el producto de los denominadores de los operandos. __mul__ es el nombre que Python utiliza para el método que sobrecarga al operador *:
class Fracción:
...
    def __mul__(self, otro):
        return Fracción(self.numerador*otro.numerador,
            self.denominador*otro.denominador)

Podemos probar este método calculando el producto de dos fracciones:

>;>> print Fracción(5,6) * Fracción(3,4)
15/24

Funciona, pero podemos hacerlo mejor! Podemos ampliar el método para manejar la multiplicación por un entero.

Usamos la función type para ver si otro es un entero y convertirlo en una fracción en tal caso.

class Fracción:
...
    def __mul__(self, otro):
        if type(otro) == type(5):
        otro = Fracción(otro)
        return Fracción(self.numerador * otro.numerador,
        self.denominador * otro.denominador)

Ahora funciona la multiplicación para fracciones y enteros, pero solo si la fracción es el operando de la izquierda.

>;>> print Fracción(5,6) * 4
20/6
>;>> print 4 * Fracción(5,6)
TypeError: __mul__ nor __rmul__ defined for these operands

Para evaluar un operador binario como la multiplicación, Python comprueba primero el operando de la izquierda para ver si proporciona un método __mul__ que soporte el tipo del segundo operando. En este caso, el operador nativo de multiplicación del entero no soporta fracciones.
Después, Python comprueba el segundo operando para ver si provee un método __rmul__ que soporte el tipo del primer operando. En este caso, no hemos provisto el método rmul , por lo que falla.

Por otra parte, hay una forma sencilla de obtener __rmul __:

class Fracción:
...
__rmul__ = __mul__

Esta asignación hace que el método rmul sea el mismo que __mul__ . Si ahora evaluamos 4 * Fracción(5,6), Python llamaría al método __rmul__ del objeto Fracción y le pasara 4 como parámetro:

>;>> print 4 * Fracción(5,6)
20/6

Dado que rmul es lo mismo que __mul__ , y __mul__ puede manejar un parámetro entero, ya esta hecho.

B.2. Suma de fracciones Python

La suma es mas complicada que la multiplicación, pero aun es llevadera. La suma de a=b y c=d es la fracción (a*d+c*b)/b*d.
Usando como modelo el código de la multiplicación, podemos escribir __add__ y __radd__:

class Fracción:
...
    def __add__(self, otro):
        if type(otro) == type(5):
        otro = Fracción(otro)
    return Fracción(self.numerador * otro.denominador +
    self.denominador * otro.numerador,
    self.denominador * otro.denominador)
    __radd__ = __add__

Podemos probar estos métodos con Fracciones y enteros.

>;>> print Fracción(5,6) + Fracción(5,6)
60/36
>;>> print Fracción(5,6) + 3
23/6
>;>> print 2 + Fracción(5,6)
17/6

Los dos primeros ejemplos llaman a __add__ ; el ultimo llama a __radd__ .

B.3. Algoritmo de Euclides Python

En el ejemplo anterior, computamos la suma de 5=6 + 5=6 y obtuvimos 60=36.
Es correcto, pero no es la mejor forma de representar la respuesta. Para reducir la fracción a su expresión mas simple, hemos de dividir el numerador y el denominador por el máximo común divisor (MCD) de ambos, que es 12.
El resultado sería 5=3.
En general, siempre que creamos un nuevo objeto Fracción, deber³amos reducirlo dividiendo el numerador y el denominador por el MCD de ambos. Si la fracción ya esta reducida, el MCD es 1.
Euclides de Alejandr³a (aprox. 325{265 a. C.) presento un algoritmo para encontrar el MCD de dos números enfermos m y n:
Si n divide a m sin resto, entonces n es el MCD. De lo contrario, el MCD es el MCD de n y el resto de dividir m entre n. Esta definición recursiva puede expresarse concisamente como una función:
   1: def mcd (m, n):
   2:     if m % n == 0:
   3:         return n
   4:     else:
   5:         return mcd(n, m%n)

En la primera l³nea del cuerpo, usamos el operador de modulo para comprobar la divisibilidad. En la ultima l³nea, lo usamos para calcular el resto de la división.

Dado que todas las operaciones que hemos escrito creaban un nuevo objeto Fracción para devolver el resultado, podemos reducir todos los resultados modificando el método de inicialización.

   1: class Fracción:
   2:     def __init__(self, numerador, denominador=1):
   3:         m = mcd (numerador, denominador)
   4:         self.numerador = numerador / m
   5:         self.denominador = denominador / m

Ahora siempre que creemos una Fracción quedara reducida a su forma canoníca:

>;>> Fracción(100,-36)
-25/9

Una característica estupenda de mcd es que si la fracción es negativa, el signo menos siempre se trasladara al numerador.

B.4. Comparar fracciones Python

Supongamos que tenemos dos objetos Fracción, a y b, y evaluamos a == b. La implementación por defecto de == comprueba la igualdad super¯cial, por lo que solo devuelve true si a y b son el mismo objeto.
Queremos mas bien devolver verdadero si a y b tienen el mismo valor |eso es,igualdad en profundidad.
Hemos de enseñar a las fracciones como compararse entre si. Como vimos en la Sección 15.4, podemos sobrecargar todos los operadores de comparación de una vez proporcionando un método cmp .
Por convenio, el método cmp devuelve un numero negativo si self es menor que otro, zero si son lo mismo, y un numero positivo si self es mayor que otro.
La forma mas simple de comparar dos fracciones es la multiplicación cruzada.
Si a=b > c=d, entonces ad > bc. Con esto en mente, aquí esta el código para cmp :
   1: class Fraccion:
   2: ...
   3:     def __cmp__(self, otro):
   4:         dif = (self.numerador * otro.denominador -
   5:             otro.numerador * self.denominador)
   6:         return dif

Si self es mayor que otro, entonces dif sera positivo. Si otro es mayor, entonces dif sera negativo. Si son iguales, dif es cero.

B.5. Forzando la máquina

Por supuesto, aun no hemos terminado. Todavía hemos de implementar la resta sobrecargando sub y la división sobrecargando div .
Una manera de manejar estas operaciones es implementar la negación sobrecargando negó y la inversión sobrecargando invert . Entonces podemos restar negando el segundo operando y sumando, y podemos dividir invirtiendo el segundo operando y multiplicando.
Luego, hemos de suministrar los métodos rsub y rdiv . Desgraciadamente, no podemos usar el mismo truco que usamos para la suma y la multiplicación, porque la resta y la división no son conmutativas. No podemos igualar rsub
y rdiv a sub y div . En estas operaciones, el orden de los operandos tiene importancia.
Para manejar la negación unitaria, que es el uso del signo menos con un único operando, sobrecargamos el método neg .
Podemos computar potencias sobrecargando pow , pero la implementación tiene truco. Si el exponente no es un numero entero podría no ser posible representar el resultado como una Fracción. Por ejemplo, Fracción(2) ** Fracción(1,2) es la raíz cuadrada de 2, que es un numero irracional (no se puede representar como una fracción). Por lo tanto, no es fácil escribir la versión mas general de pow .
Existe otra extensión a la clase Fracción que cabr³a considerar. Hasta ahora, hemos asumido que el numerador y el denominador son enteros. Podríamos considerar la posibilidad de perimirles que sean enteros largos.
Como ejercicio, termine la implementación de la clase Fracción de forma que pueda manejar resta, división, exponenciación y enteros largos como numerador y denominador.

B.6. Glosario

Máximo común divisor (MCD): El mayor entero positivo que divide al numerador y al denominador de una fracción sin que quede un resto.
Reducir: Cambiar la fracción a su forma equivalente con un MCD igual a 1.
Negación unitaria: Operación que computa el elemento simétrico aditivo, normalmente denotada con un signo menos delante. Se denomina unitaria” en contraste con la operación binaria menos, que es la resta.

Página 1 de 6

Creado con WordPress & Tema de Anders Norén