Next Up Previous Hi Index

Chapter 15

\newcommand{\eningles[1]{{\foreignlanguage{english}{#1}}} \newcommand{\revisar}[1]{\textbf{***}\marginpar{\raggedright\tiny\textsc{#1}}} \chapter{Conjuntos de objetos} \section{Composici�n} \index{composici�n} \index{estructura anidada} Hasta ahora, ya ha visto varios ejemplos de composici�n. Uno de los primeros ejemplos fue el uso de la llamada a un m�todo como parte de una expresi�n. Otro ejemplo es la estructura anidada de las sentencias; se puede escribir una sentencia {\tt if} dentro de un bucle {\tt while}, dentro de otra sentencia {\tt if}, y as� sucesivamente. Una vez visto este patr�n, y sabiendo acerca de listas y objetos, no le deber�a sorprender que pueda crear listas de objetos. Tambi�n puede crear objetos que contengan listas (en forma de atributos); puede crear listas que contengan listas; objetos que contengan objetos, y as� indefinidamente. En este cap�tulo y el siguiente, exploraremos algunos ejemplos de estas combinaciones, y usaremos objetos {\tt Carta} como ejemplo. \section{Objetos {\tt Carta}} \index{Carta} \index{clase!Carta} Si no est� usted familiarizado con los naipes de juego comunes, puede ser un buen momento para que consiga un mazo, si no este cap�tulo puede que no tenga mucho sentido. Hay cincuenta y dos naipes en una baraja inglesa, cada uno de los cuales pertenece a un palo y tiene un valor; hay cuatro palos diferentes y trece valores. Los palos son Picas, Corazones, Diamantes, y Tr�boles (en el orden descendente seg�n el \eningles{bridge}). Los valores son As, 2, 3, 4, 5, 6, 7, 8, 9, 10, Sota, Reina, y Rey. Dependiendo del tipo de juego que se juegue, el valor del As puede ser mayor al Rey o inferior al 2. \index{valor} \index{palo} Si queremos definir un nuevo objeto para representar un naipe, es obvio qu� atributos deber�a tener: {\tt valor} y {\tt palo}. Lo que no es tan obvio es el tipo que se debe dar a los atributos. Una posibilidad es usar cadenas de caracteres que contengan palabras como \verb+"Picas"+ para los palos y \verb+"Reina"+ para los valores. Un problema de esta implementaci�n es que no ser� f�cil comparar naipes para ver cu�l tiene mayor valor o palo. \index{codificar} \index{cifrar} \index{corresponder} Una alternativa es usar n�meros enteros para {\bf codificar} los valores y palos. Con el t�rmino ``codificar'' no queremos significar lo que algunas personas pueden pensar, acerca de cifrar o traducir a un c�digo secreto. Lo que un programador entiende por ``codificar'' es ``definir una correspondencia entre una secuencia de n�meros y los elementos que se desea representar''. Por ejemplo: \beforefig \begin{tabular}{l c l} Picas & $\mapsto$ & 3 \\ Corazones & $\mapsto$ & 2 \\ Diamantes & $\mapsto$ & 1 \\ Tr�boles & $\mapsto$ & 0 \end{tabular} \afterfig Esta correspondencia tiene una caracter�stica obvia: los palos corresponden a n�meros enteros en orden, o sea que podemos comparar los palos al comparar los n�meros. La asociaci�n de los valores es bastante obvia; cada uno de los valores num�ricos se asocia con el entero correspondiente, y para las figuras: \beforefig \begin{tabular}{l c l} Sota & $\mapsto$ & 11 \\ Reina & $\mapsto$ & 12 \\ Rey & $\mapsto$ & 13 \\ \end{tabular} \afterfig Estamos usando una notaci�n matem�tica para estas asociaciones por una raz�n: no son parte del programa Python. Son parte del dise�o del programa, pero nunca aparecen expl�citamente en el c�digo fuente. La definici�n de clase para el tipo {\tt Carta} se parecer� a: \beforeverb \begin{verbatim} class Carta: def __init__(self, palo=0, valor=0): self.palo = palo self.valor = valor \end{verbatim} \afterverb % Como acostumbramos, proporcionaremos un m�todo de inicializaci�n que toma un par�metro opcional para cada atributo. \index{constructor} Para crear un objeto que representa el 3 de Tr�boles, usaremos la instrucci�n: \beforeverb \begin{verbatim} tresDeTreboles = Carta(0, 3) \end{verbatim} \afterverb % El primer argumento, {\tt 0}, representa el palo de Tr�boles. \section{Atributos de clase y el m�todo {\tt \_\_str\_\_}} \index{atributo de clase} \index{atributo!clase} Para poder imprimir los objetos {\tt Carta} de una manera f�cil de leer para las personas, vamos a establecer una correspondencia entre los c�digos enteros y las palabras. Una manera natural de hacer esto es con listas de cadenas de caracteres. Asignaremos estas listas dentro de {\bf atributos de clase} al principio de la definici�n de clase: \beforeverb \begin{verbatim} class Carta: listaDePalos = ["Tr�boles", "Diamantes", "Corazones", "Picas"] listaDeValores = ["nada", "As", "2", "3", "4", "5", "6", "7", "8", "9", "10", "Sota", "Reina", "Rey"] # se omite el m�todo init def __str__(self): return (self.listaDeValores[self.valor] + " de " + self.listaDePalos[self.palo]) \end{verbatim} \afterverb % Un atributo de clase se define fuera de cualquier m�todo, y puede accederse desde cualquiera de los m�todos de la clase. Dentro de {\tt \_\_str\_\_}, podemos usar {\tt listaDePalos} y {\tt listaDeValores} para asociar los valores num�ricos de {\tt palo} y {\tt valor} con cadenas de caracteres. Por ejemplo, la expresi�n \verb+self.listaDePalos[self.palo]+ significa ``usa el atributo {\tt palo} del objeto {\tt self} como un �ndice dentro del atributo de clase denominado {\tt listaDePalos}, y selecciona la cadena apropiada''. El motivo del {\tt ``nada''} en el primer elemento de {\tt listaDeValores} es para relleno del elemento de posici�n cero en la lista, que nunca se usar�. Los �nicos valores l�citos para el valor van de 1 a 13. No es obligatorio que desperdiciemos este primer elemento. Podr�amos haber comenzado en 0 como es usual, pero es menos confuso si el 2 se codifica como 2, el 3 como 3, y as� sucesivamente. Con los m�todos que tenemos hasta ahora, podemos crear e imprimir naipes: \beforeverb \begin{verbatim} >>> carta1 = Carta(1, 11) >>> print carta1 Sota de Diamantes \end{verbatim} \afterverb % Los atributos de clase como {\tt listaDePalos} son compartidos por todos los objetos de tipo {\tt Carta}. La ventaja de esto es que podemos usar cualquier objeto {\tt Carta} para acceder a los atributos de clase: \beforeverb \begin{verbatim} >>> carta2 = Carta(1, 3) >>> print carta2 3 de Diamantes >>> print carta2.listaDePalos[1] Diamantes \end{verbatim} \afterverb % La desventaja es que si modificamos un atributo de clase, afectaremos a cada instancia de la clase. Por ejemplo, si decidimos que ``Sota de Diamantes'' en realidad deber�a llamarse ``Sota de Ballenas Bailarinas'', podr�amos hacer lo siguiente: \index{instancia!objeto} \index{instancia de objeto} \beforeverb \begin{verbatim} >>> carta1.listaDePalos[1] = "Ballenas Bailarinas" >>> print carta1 Sota de Ballenas Bailarinas \end{verbatim} \afterverb % El problema es que \emph{todos} los Diamantes se transformar�n en Ballenas Bailarinas: \beforeverb \begin{verbatim} >>> print carta2 3 de Ballenas Bailarinas \end{verbatim} \afterverb % En general no es una buena idea modificar los atributos de clase. \section{Comparaci�n de naipes} \label{comparecard} \index{operador!condicional} \index{operador condicional} Para los tipos primitivos, existen operadores condicionales ({\tt <{} }, {\tt >{}}, {\tt ==}, etc.) que comparan valores y determinan cuando uno es mayor, menor, o igual a otro. Para los tipos definidos por el usuario, podemos sustituir el comportamiento de los operadores internos si proporcionamos un m�todo llamado {\tt \_\_cmp\_\_}. Por convenci�n, {\tt \_\_cmp\_\_} toma dos par�metros, {\tt self} y {\tt otro}, y retorna 1 si el primer objeto es el mayor, -1 si el segundo objeto es el mayor, y 0 si ambos son iguales. \index{sustituir} \index{sobrecarga de operadores} \index{orden} \index{orden completo} \index{orden parcial} Algunos tipos est�n completamente ordenados, lo que significa que se pueden comparar dos elementos cualesquiera y decir cu�l es el mayor. Por ejemplo, los n�meros enteros y los n�meros en coma flotante tienen un orden completo. Algunos conjuntos no tienen orden, o sea, que no existe ninguna manera significativa de decir que un elemento es mayor a otro. Por ejemplo, las frutas no tienen orden, lo que explica por qu� no se pueden comparar peras con manzanas. El conjunto de los naipes tiene un orden parcial, lo que significa que algunas veces se pueden comparar los naipes, y otras veces no. Por ejemplo, usted sabe que el 3 de Tr�boles es mayor que el 2 de Tr�boles y el 3 de Diamantes es mayor que el 3 de Tr�boles. Pero, �cu�l es mejor?, �el 3 de Tr�boles o el 2 de Diamantes?. Uno tiene mayor valor, pero el otro tiene mayor palo. \index{comparable} A los fines de hacer que los naipes sean comparables, se debe decidir qu� es m�s importante: valor o palo. Para no mentir, la selecci�n es arbitraria. Como algo hay que elegir, diremos que el palo es m�s importante, porque un mazo nuevo viene ordenado con todos los Tr�boles primero, luego con todos los Diamantes, y as� sucesivamente. Con esa decisi�n tomada, podemos escribir {\tt \_\_cmp\_\_}: \beforeverb \begin{verbatim} def __cmp__(self, otro): # controlar el palo if self.palo > otro.palo: return 1 if self.palo < otro.palo: return -1 # si son del mismo palo, controlar el valor if self.valor > otro.valor: return 1 if self.valor < otro.valor: return -1 # los valores son iguales, es un empate return 0 \end{verbatim} \afterverb % En este ordenamiento, los Ases son menores que los doses. \begin{quote} {\em Como ejercicio, modifique {\tt \_\_cmp\_\_} de tal manera que los Ases tengan mayor valor que los Reyes.} \end{quote} \section{Mazos de naipes} \index{lista!de objetos} \index{objetos!lista de} \index{mazo} Ahora que ya tenemos los objetos para representar las {\tt Carta}s, el pr�ximo paso l�gico es definir una clase para representar un {\tt Mazo}. Por supuesto, un mazo est� compuesto de naipes, as� que cada objeto {\tt Mazo} contendr� una lista de naipes como atributo. \index{m�todo de inicializaci�n} \index{m�todo!inicializaci�n} A continuaci�n se muestra una definici�n para la clase {\tt Mazo}. El m�todo de inicializaci�n crea el atributo {\tt cartas} y genera el conjunto est�ndar de cincuenta y dos naipes. \index{composici�n} \index{bucle!anidado} \beforeverb \begin{verbatim} class Mazo: def __init__(self): self.cartas = [] for palo in range(4): for valor in range(1, 14): self.cartas.append(Carta(palo, valor)) \end{verbatim} \afterverb % La forma m�s f�cil de poblar el mazo es mediante un bucle anidado. El bucle exterior enumera los palos desde 0 hasta 3. El bucle interior enumera los valores desde 1 hasta 13. Como el bucle exterior itera cuatro veces, y el interior itera trece veces, la cantidad total de veces que se ejecuta el cuerpo interior es cincuenta y dos (trece por cuatro). Cada iteraci�n crea una nueva instancia de {\tt Carta} con el palo y valor actual, y agrega dicho naipe a la lista de {\tt cartas}. El m�todo {\tt append} funciona sobre listas pero no sobre tuplas, por supuesto. \index{m�todo append} \index{m�todos de lista} \index{m�todo!lista} \adjustpage{1} \section{Impresi�n del mazo de naipes} \label{muestraMazo} \index{impresi�n!objeto mazo} Como es usual, cuando definimos un nuevo tipo de objeto queremos un m�todo que imprima el contenido del objeto. Para imprimir un {\tt Mazo}, recorremos la lista e imprimimos cada {\tt Carta}: \beforeverb \begin{verbatim} class Mazo: ... def muestraMazo(self): for carta in self.cartas: print carta \end{verbatim} \afterverb % Desde ahora en adelante, los puntos suspensivos ({\tt ...}) indicar�n que hemos omitido los otros m�todos en la clase. En lugar de escribir un m�todo {\tt muestraMazo}, podr�amos escribir un m�todo {\tt \_\_str\_\_} para la clase {\tt Mazo}. La ventaja de {\tt \_\_str\_\_} est� en que es m�s flexible. En lugar de imprimir directamente el contenido del objeto, {\tt \_\_str\_\_} genera una representaci�n en forma de cadena de caracteres que las otras partes del programa pueden manipular antes de imprimir o almacenar para un uso posterior. Se presenta ahora una versi�n de {\tt \_\_str\_\_} que retorna una representaci�n como cadena de caracteres de un {\tt Mazo}. Para darle un toque especial, acomoda los naipes en una cascada, de tal manera que cada naipe est� sangrado un espacio m�s que el precedente. \beforeverb \begin{verbatim} class Mazo: ... def __str__(self): s = "" for i in range(len(self.cartas)): s = s + " "*i + str(self.cartas[i]) + "\n" return s \end{verbatim} \afterverb % Este ejemplo demuestra varias caracter�sticas. Primero, en lugar de recorrer {\tt self.cartas} y asignar cada naipe a una variable, usamos {\tt i} como variable de bucle e �ndice de la lista de naipes. Segundo, utilizamos el operador de multiplicaci�n de cadenas de caracteres para sangrar cada naipe un espacio m�s que el anterior. La expresi�n {\tt " "*i} prooprciona una cantidad de espacios igual al valor actual de {\tt i}. Tercero, en lugar de usar la instrucci�n {\tt print} para imprimir los naipes, utilizamos la funci�n {\tt str}. El pasar un objeto como argumento a {\tt str} es equivalente a invocar el m�todo {\tt \_\_str\_\_} sobre dicho objeto. \index{acumulador} Finalmente, usamos la variable {\tt s} como {\bf acumulador}. Inicialmente, {\tt s} es una cadena de caracteres vac�a. En cada pasada a trav�s del bucle, se genera una nueva cadena de caracteres que se concatena con el viejo valor de {\tt s} para obtener el nuevo valor. Cuando el bucle termina, {\tt s} contiene la representaci�n completa en formato de cadena de caracteres del {\tt Mazo}, la cual se ve como a continuaci�n se presenta: \adjustpage{-2} \beforeverb \begin{verbatim} >>> mazo = Mazo() >>> print mazo As de Tr�boles 2 de Tr�boles 3 de Tr�boles 4 de Tr�boles 5 de Tr�boles 6 de Tr�boles 7 de Tr�boles 8 de Tr�boles 9 de Tr�boles 10 de Tr�boles Sota de Tr�boles Reina de Tr�boles Rey de Tr�boles As of Diamantes \end{verbatim} \afterverb % Y as� sucesivamente. A�n cuando los resultados aparecen en 52 renglones, se trata de s�lo una �nica larga cadena de caracteres que contiene los saltos de l�nea. \section{Barajar el mazo} \index{barajar} Si un mazo est� perfectamente barajado, cualquier naipe tiene la misma probabilidad de aparecer en cualquier posici�n del mazo, y cualquier lugar en el mazo tiene la misma probabilidad de contener cualquier naipe. \index{aleatorio} \index{random} \index{randrange} Para mezclar el mazo, utilizaremos la funci�n {\tt randrange} del m�dulo {\tt random}. Esta funci�n toma dos enteros como argumentos {\tt a} y {\tt b}, y elige un n�mero entero en forma aleatoria en el rango {\tt a <= x < b}. Como el l�mite superior es estrictamente menor a {\tt b}, podemos usar la longitud de la lista como el segundo argumento y de esa manera tendremos garantizado un �ndice legal dentro de la lista. Por ejemplo, esta expresi�n selecciona el �ndice de un naipe al azar dentro del mazo: \beforeverb \begin{verbatim} random.randrange(0, len(self.cartas)) \end{verbatim} \afterverb % Una manera sencilla de mezclar el mazo es recorrer los naipes e intercambiar cada una con otra elegida al azar. Es posible que el naipe se intercambie consigo mismo, pero no es un problema. De hecho, si eliminamos esa posibilidad, el orden de los naipes no ser� completamente al azar: \adjustpage{-2} \beforeverb \begin{verbatim} class Mazo: ... def mezclar(self): import random nCartas = len(self.cartas) for i in range(nCartas): j = random.randrange(i, nCartas) self.cartas[i], self.cartas[j] =\ self.cartas[j], self.cartas[i] \end{verbatim} \afterverb % En lugar de presuponer que hay cincuenta y dos naipes en el mazo, obtenemos la longitud real de la lista y la almacenamos en {\tt nCartas}. \index{intercambio} \index{asignaci�n de tuplas} \index{asignaci�n!tupla} Para cada naipe del mazo, seleccionamos un naipe al azar entre aquellos que no han sido intercambiados a�n. Luego intercambiamos el naipe actual ({\tt i}) con el naipe seleccionado ({\tt j}). Para intercambiar los naipes usaremos la asignaci�n de tuplas, como se describe en la Secci�n~\ref{tuple assignment}: \beforeverb \begin{verbatim} self.cartas[i], self.cartas[j] = self.cartas[j], self.cartas[i] \end{verbatim} \afterverb % \begin{quote} {\em Como ejercicio, reescriba esta l�nea de c�digo sin usar una asignaci�n de secuencias.} \end{quote} \section{Eliminaci�n y reparto de los naipes} \index{eliminaci�n de naipes} Otro m�todo que podr�a ser �til para la clase {\tt Mazo} es {\tt eliminaCarta}, que toma un naipe como par�metro, lo elimina, y retorna verdadero (1) si el naipe estaba en el mazo, y falso (0) si no estaba: \beforeverb \begin{verbatim} class Mazo: ... def eliminaCarta(self, carta): if carta in self.cartas: self.cartas.remove(carta) return 1 else: return 0 \end{verbatim} \afterverb % El operador {\tt in} retorna verdadero si el primer operando est� en el segundo, el cual debe ser una lista o tupla. Si el primer operando es un objeto, Python usa el m�todo {\tt \_\_cmp\_\_} del objeto para determinar la igualdad entre los elementos de la lista. Como el {\tt \_\_cmp\_\_} en la clase {\tt Carta} verifica la igualdad en profundidad, el m�todo {\tt eliminaCarta} tambi�n verifica igualdad en profundidad. \index{operador in} \index{operador!in} Para repartir los naipes, queremos eliminar y devolver el naipe que ocupa la posici�n superior en el mazo. El m�todo {\tt pop} de las listas proporciona una manera conveniente de realizar esto: \beforeverb \begin{verbatim} class Mazo: ... def darCarta(self): return self.cartas.pop() \end{verbatim} \afterverb % En realidad, {\tt pop} elimina el \emph{�ltimo} naipe en la lista, as� que en efecto estamos repartiendo desde el extremo inferior del mazo. \index{funci�n booleana} \index{funci�n!booleana} Otra operaci�n m�s que es muy probable necesitemos es la funci�n booleana {\tt estaVacio}, la cual devuelve verdadero si el mazo no contiene ning�n naipe: \beforeverb \begin{verbatim} class Deck: ... def estaVacio(self): return (len(self.cartas) == 0) \end{verbatim} \afterverb \section{Glosario} \begin{description} \item[codificar:] Representar un conjunto de valores uilizando otro conjunto de valores, entre los cuales se construye una correspondencia. \item[atributo de clase:] Una variable que se define dentro de la definici�n de una clase pero fuera de cualquiera de sus m�todos. Los atributos de clase son accesibles desde cualquier m�todo de la clase y est�n compartidos por todas las instancias de la misma. \item[acumulador:] Una variable que se usa en un bucle para acumular una serie de valores, por ejemplo concaten�ndolos dentro de una cadena de caracteres o adicion�ndolos a una suma. \index{codificar} \index{atributo de clase} \index{atributo!clase} \index{acumulador} \end{description} %\selectlanguage{english} %%% Local Variables: %%% mode: latex %%% TeX-master: "top" %%% End:


Next Up Previous Hi Index

" + str + "

Close window

Hosted by www.Geocities.ws

"); } //-->
Hosted by www.Geocities.ws

1