Cesar Systems

Herramientas Informaticas

11.4. Encurtido

Para poner valores en un archivo, debe convertirlos en cadenas. Ya ha visto como hacerlo con str:

   1: >>> f.write (str(12.3))

   2: >>> f.write (str([1,2,3]))

El problema es que cuando vuelve usted a leer el valor, obtiene una cadena. Ha perdido la información del tipo de dato original. En realidad, no puede distinguir donde termina un valor y comienza el siguiente:

   1: >>> f.readline()

   2: '12.3[1, 2, 3]'

La solución es el encurtido, llamado así porque “conserva” estructuras de datos.

El modulo pickle contiene las ordenes necesarias. Para usarlo, importe pickle y luego abra el archivo de la forma habitual:

   1: >>> import pickle

   2: >>> f = open("test.pck","w")

Para almacenar una estructura de datos, use el metodo dump y luego cierre el archivo de la forma habitual:

   1: >>> pickle.dump(12.3, f)

   2: >>> pickle.dump([1,2,3], f)

   3: >>> f.close()

Ahora podemos abrir el archivo para leer y cargar las estructuras de datos que volcamos ahí:

   1: >>> f = open("test.pck","r")

   2: >>> x = pickle.load(f)

   3: >>> x

   4: 12.3

   5: >>> type(x)

   6: <type 'float'>

   7: >>> y = pickle.load(f)

   8: >>> y

   9: [1, 2, 3]

  10: >>> type(y)

  11: <type 'list'>

Cada vez que invocamos load obtenemos un valor del archivo, completo con su tipo original.

11.5. Excepciones

Siempre que ocurre un error en tiempo de ejecución, se crea una excepción.

Normalmente el programa se para y Python presenta un mensaje de error.

Por ejemplo, la división por cero crea una excepción:

   1: >>> print 55/0

   2: ZeroDivisionError: integer division or modulo

Un elemento no existente en una lista hace lo mismo:

   1: >>> a = []

   2: >>> print a[5]

   3: IndexError: list index out of range

O el acceso a una clave que no está en el diccionario:

 

   1: >>> b = {}

   2: >>> print b['qué']

   3: KeyError: qué

En cada caso, el mensaje de error tiene dos partes: el tipo de error antes de los dos puntos y detalles sobre el error después de los dos puntos. Normalmente Python también imprime una traza de donde se encontraba el programa, pero
la hemos omitido en los ejemplos.

A veces queremos realizar una operación que podría provocar una excepción, pero no queremos que se pare el programa. Podemos manejar la excepción
usando las sentencias try y except.

Por ejemplo, podemos preguntar al usuario por el nombre de un archivo y luego intentar abrirlo. Si el archivo no existe, no queremos que el programa se pare;  queremos manejar la excepción.

   1: nombreArch = raw_input('Introduce un nombre de archivo: ')

   2: try:

   3:     f = open (nombreArch, "r")

   4: except:

   5:     print 'No hay ning¶un archivo que se llame', nombreArch

La sentencia try ejecuta las sentencias del primer bloque. Si no se produce ninguna excepción, pasa por alto la sentencia excepto. Si ocurre cualquier excepción, ejecuta las sentencias de la rama except y después continua.

Podemos encapsular esta capacidad en una función: existe acepta un nombre de archivo y devuelve verdadero si el archivo existe y falso si no:

 

   1: def existe(nombreArch):

   2: try:

   3: f = open(nombreArch)

   4: f.close()

   5:     return 1

   6: except:

   7:     return 0

Puede usar múltiples bloques except para manejar diferentes tipos de excepciones. El Manual de Referencia de Python contiene los detalles.

Si su programa detecta una condición de error, puede hacer que lance (raise en ingles) una excepción. Aquí tiene usted un ejemplo que acepta una entrada del usuario y comprueba si es 17. Suponiendo que 17 no es una entrada valida por cualquier razón, lanzamos una excepción.

   1: def tomaNumero () :                                 # Recuerde, los acentos estan

   2: x = input ('Elige un numero: ')                     # prohibidos en los nombres

   3: if x == 17 :                                        # de funciones y variables!

   4:     raise 'ErrorNumeroMalo', '17 es un mal numero'

   5: return x

La sentencia raise acepta dos argumentos: el tipo de excepción e información específica acerca del error. ErrorNumeroMalo es un nuevo tipo de excepción que hemos inventado para esta aplicación.

Si la función llamada tomaNumero maneja el error, el programa puede continuar; en caso contrario, Python imprime el mensaje de error y sale:

   1: >>> tomaNumero ()

   2: Elige un numero: 17

   3: ErrorNumeroMalo: 17 es un mal numero

El mensaje de error incluye el tipo de excepción y la información adicional que usted proporcionó.

Como ejercicio, escriba una función que use tomaNumero para leer
un numero del teclado y que maneje la excepción ErrorNumeroMalo.

11.6. Glosario

archivo: Una entidad con nombre, normalmente almacenada en un disco duro, disquete o CD-ROM, que contiene una secuencia de caracteres.

directorio: Una colección, con nombre, de archivos, también llamado carpeta.

ruta: Una secuencia de nombres de directorio que especifica la localización exacta de un archivo.archivo de texto: Un archivo que contiene caracteres imprimibles organizados en líneas separadas por caracteres de salto de l³nea.

sentencia break: Una sentencia que provoca que el flujo de ejecución salga de un bucle.

sentencia continue: Una sentencia que provoca que termine la iteración actual de un bucle. El flujo de la ejecución va al principio del bucle, evalúa la condición, y procede en consecuencia.

operador de formato: El operador % toma una cadena de formato y una tupla de expresiones y entrega una cadena que incluye las expresiones, formateadas de acuerdo con la cadena de formato.

cadena de formato: Una cadena que contiene caracteres imprimibles y secuencias de formato que indican como formatear valores.

secuencia de formato: Una secuencia de caracteres que comienza con % e indica como formatear un valor.

encurtir: Escribir el valor de un dato en un archivo junto con la información sobre su tipo de forma que pueda ser reconstituido mas tarde.

excepción: Un error que ocurre en tiempo de ejecución.

manejar: Impedir que una excepción detenga un programa utilizando las sentencias try y except.

lanzar: Señalar una excepción usando la sentencia raise.

Capítulo 12

Clases y objetos

12.1. Tipos compuestos definidos por el usuario

Una vez utilizados algunos de los tipos internos de Python, estamos listos para crear un tipo definido por el usuario: el Punto.

Piense en el concepto de un punto matemático. En dos dimensiones, un punto es dos números (coordenadas) que se tratan colectivamente como un solo objeto.

En notación matemática, los puntos suelen escribirse entre paréntesis con una coma separando las coordenadas. Por ejemplo, (0,0) representa el origen, y (x,y) representa el punto x unidades a la derecha e y unidades hacia arriba desde el origen.

Una forma natural de representar un punto en Python es con dos valores en coma flotante. La cuestión es, entonces, como agrupar esos dos valores en un objeto compuesto. La solución rápida y burda es utilizar una lista o tupla, y para algunas aplicaciones esa podr³a ser la mejor opción.

Una alternativa es que el usuario defina un nuevo tipo compuesto, también llamado una clase. Esta aproximación exige un poco mas de esfuerzo, pero tiene sus ventajas que pronto se harán evidentes.

Una definición de clase se parece a esto:

   1: class Punto:

   2: pass

Las definiciones de clase pueden aparecer en cualquier lugar de un programa,

pero normalmente están al principio (tras las sentencias import). Las reglas sintácticas de la definición de clases son las mismas que para cualesquiera otras sentencias compuestas. (ver la Sección 4.4).

Esta definición crea una nueva clase llamada Punto. La sentencia pass no tiene efectos; solo es necesaria porque una sentencia compuesta debe tener algo en su cuerpo.

Al crear la clase Punto hemos creado un nuevo tipo, que también se llama Punto.

Los miembros de este tipo se llaman instancias del tipo u objetos. La creacion de una nueva instancia se llama instanciación. Para instanciar un objeto Punto ejecutamos una función que se llama (lo ha adivinado) Punto:

   1: blanco = Punto()

A la variable blanco se le asigna una referencia a un nuevo objeto Punto. A una función como Punto que crea un objeto nuevo se le llama constructor.

12.2. Atributos

Podemos añadir nuevos datos a una instancia utilizando la notación de punto:

 

   1: >>> blanco.x = 3.0

   2: >>> blanco.y = 4.0

Esta sintaxis es similar a la sintaxis para seleccionar una variable de un modulo, como math.pi o string.uppercase. En este caso, sin embargo, estamos seleccionando un dato de una instancia. Estos ítems con nombre se llaman atributos.

El diagrama de estados que sigue muestra el resultado de esas asignaciones:

Sin título

La variable blanco apunta a un objeto Punto, que contiene dos atributos. Cada atributo apunta a un numero en coma flotante.

Podemos leer el valor de un atributo utilizando la misma sintaxis:

   1: >>> print blanco.y

   2: 4.0

   3: >>> x = blanco.x

   4: >>> print x

   5: 3.0

La expresión blanco.x significa, “ve al objeto al que apunta blanco y toma el valor de x”. En este caso, asignamos ese valor a una variable llamada x. No hay conflicto entre la variable x y el atributo x. El propósito de la notación de punto es identificar de forma inequívoca a que variable se refiere.

Puede usted usar la notación de punto como parte de cualquier expresión. Así, las sentencias que siguen son correctas:

   1: print '(' + str(blanco.x) + ', ' + str(blanco.y) + ')'

   2: distanciaAlCuadrado = blanco.x * blanco.x + blanco.y * blanco.y

La primera línea presenta (3.0, 4.0); la segunda línea calcula el valor 25.0.

Puede tentarle imprimir el propio valor de blanco:

   1: >>> print blanco

   2: 

El resultado indica que blanco es una instancia de la clase Punto que se definió en __main__. 80f8e70 es el identificador único de este objeto, escrito en hexadecimal. Probablemente no es esta la manera mas clara de mostrar un objeto Punto.

En breve vera como cambiarlo.

Como ejercicio, cree e imprima un objeto Punto y luego use id pa-
ra imprimir el identificador único del objeto. Traduzca el numero
hexadecimal a decimal y asegúrese de que coinciden.

 

 

12.3. Instancias como parámetro

Puede usted pasar una instancia como parámetro de la forma habitual. Por ejemplo:

   1: def imprimePunto(p):

   2: print '(' + str(p.x) + ', ' + str(p.y) + ')'

imprimePunto acepta un punto como argumento y lo muestra en formato estándar. Si llama a imprimePunto(blanco), el resultado es (3.0, 4.0).

Como ejercicio, reescriba la función distancia de la Sección 5.2 de
forma que acepte dos Puntos como parámetros en lugar de cuatro
números.

12.4. Mismidad

El significado de la palabra “mismo” parece totalmente claro hasta que uno se para un poco a pensarlo, y entonces se da cuenta de que hay algo mas de lo que suponía.

Por ejemplo, si dice “Pepe y yo tenemos la misma moto”, lo que quiere decir es que su moto y la de usted son de la misma marca y modelo, pero que son dos motos distintas. Si dice “Pepe y yo tenemos la misma madre”, quiere decir que su madre y la de usted son la misma persona1. Así que la idea de “identidad” es diferente según el contexto.

Cuando habla de objetos, hay una ambigüedad parecida. Por ejemplo, si dos

Puntos son el mismo, ¿significa que contienen los mismos datos (coordenadas) o que son de verdad el mismo objeto?

Para averiguar si dos referencias se refieren al mismo objeto, utilice el operador ==. Por ejemplo:

   1: >>> p1 = Punto()

   2: >>> p1.x = 3

   3: >>> p1.y = 4

   4: >>> p2 = Punto()

   5: >>> p2.x = 3

   6: >>> p2.y = 4

   7: >>> p1 == p2

   8: 0

Aunque p1 y p2 contienen las mismas coordenadas, no son el mismo objeto. Si asignamos p1 a p2, las dos variables son alias del mismo objeto:

   1: >>> p2 = p1

   2: >>> p1 == p2

   3: 1

Este tipo de igualdad se llama igualdad superficial porque solo compara las referencias, pero no el contenido de los objetos.

Para comparar los contenidos de los objetos (igualdad profunda) podemos escribir una función llamada mismoPunto:

   1: def mismoPunto(p1, p2) :

   2: return (p1.x == p2.x) and (p1.y == p2.y)

 

 

 

1No todas las lenguas tienen el mismo problema. Por ejemplo, el alemán tiene palabras
diferentes para los diferentes tipos de identidad. “Misma moto” en este contexto sería “gleiche
Motorrad” y “misma madre” sería “selbe Mutter”.

12.5. Rectángulos

Digamos que queremos una clase que represente un rectangulo. La pregunta es, ¿que información tenemos que proporcionar para definir un rectangulo?

Para simplificar las cosas, supongamos que el rectangulo esta orientado vertical u horizontalmente, nunca en diagonal.

Tenemos varias posibilidades: podemos señalar el centro del rectangulo (dos coordenadas) y su tamaño (anchura y altura); o podemos señalar una de las esquinas y el tamaño; o podemos señalar dos esquinas opuestas. Un modo convencional es señalar la esquina superior izquierda del rectangulo y el tamaño.

De nuevo, definiremos una nueva clase:

   1: class Rectangulo: # Prohibidos los acentos fuera de las cadenas!

   2: pass

Y la instanciaremos:

   1: caja = Rectangulo()

   2: caja.anchura = 100.0

   3: caja.altura = 200.0

Este código crea un nuevo objeto Rectangulo con dos atributos en coma flotante. ¡Para señalar la esquina superior izquierda podemos incrustar un objeto dentro de otro!

   1: caja.esquina = Punto()

   2: caja.esquina.x = 0.0;

   3: caja.esquina.y = 0.0;

El operador punto compone. La expresión caja.esquina.x significa “ve al objeto al que se re¯ere caja y selecciona el atributo llamado esquina; entonces ve a ese objeto y selecciona el atributo llamado x”.

La figura muestra el estado de este objeto:

Sin título

12.6. Instancias como valores de retorno

Las funciones pueden devolver instancias. Por ejemplo, encuentraCentro acepta un Rectangulo como argumento y devuelve un Punto que contiene las coordenadas del centro del Rectangulo:

   1: def encuentraCentro(caja):

   2:     p = Punto()

   3:     p.x = caja.esquina.x + caja.anchura/2.0

   4:     p.y = caja.esquina.y + caja.altura/2.0

   5:     return p

Para llamar a esta función, pase caja como argumento y asigne el resultado a una variable:

   1: >>> centro = encuentraCentro(caja)

   2: >>> imprimePunto(centro)

   3: (50.0, 100.0)

Página 118 de 143

Creado con WordPress & Tema de Anders Norén