0% encontró este documento útil (0 votos)
7 vistas

Tema1 1 POO Clases Python

Derechos de autor
© © All Rights Reserved
Nos tomamos en serio los derechos de los contenidos. Si sospechas que se trata de tu contenido, reclámalo aquí.
Formatos disponibles
Descarga como PDF, TXT o lee en línea desde Scribd
0% encontró este documento útil (0 votos)
7 vistas

Tema1 1 POO Clases Python

Derechos de autor
© © All Rights Reserved
Nos tomamos en serio los derechos de los contenidos. Si sospechas que se trata de tu contenido, reclámalo aquí.
Formatos disponibles
Descarga como PDF, TXT o lee en línea desde Scribd
Está en la página 1/ 52

Algorítmica y Estructuras de

Datos
Tema 1_1: Programación Orientada
a Objetos.

Efrén Arias Jordán


Dpto Electrónica e Computación
Programación Orientada a Objetos
#Tema1_00_1 Creamos una función que muestra un cliente en una lista #Tema1_00_2
a partir del DNI class Cliente: # Creo una estructura para los clientes
def mostrar_cliente(clientes, dni): def __init__(self, dni, nombre, apellidos):
self.dni = dni
for c in clientes: self.nombre = nombre
if (dni == c['dni']): self.apellidos = apellidos
print(f"{c['Nombre']} {c['Apellidos']}") def __str__(self):
return return f'{self.nombre} {self.apellidos} {self.dni}'
print('Cliente no encontrado’) class Empresa: # Y otra para las empresas
def __init__(self, clientes=[]):
# Creamos una función que borra un cliente en una lista a partir del self.clientes = clientes
DNI def mostrar_cliente(self, dni=None):
def borrar_cliente(clientes, dni): for c in self.clientes:
for i, c in enumerate(clientes): if c.dni == dni:
if (dni == c['dni']): print(c)
del(clientes[i]) return
print("Cliente no encontrado")
print(str(c), "> BORRADO") def borrar_cliente(self, dni=None):
return for i, c in enumerate(self.clientes):
print('Cliente no encontrado’) if c.dni == dni:
del(self.clientes[i])
# Definimos unos cuantos clientes print(str(c), "> BORRADO")
return
clientes = [{'Nombre': 'Hector', 'Apellidos': 'Costa Guzman', 'dni': print("Cliente no encontrado")
'11111111A'},{'Nombre': 'Juan', 'Apellidos': 'González Márquez', 'd # Ahora utilizaré ambas estructuras
ni': '22222222B’}] # Creo un par de clientes
hector = Cliente(nombre="Hector", apellidos="Costa Guzman", dni="111
# Muestro todos los clientes 11111A")
print("==LISTADO DE CLIENTES==") juan = Cliente("22222222B", "Juan", "Gonzalez Marquez")
print(clientes) # Creo una empresa con los clientes iniciales
empresa = Empresa(clientes=[hector, juan])
# Consulto clientes por DNI print("==LISTADO DE CLIENTES==") # Muestro todos los clientes
print("\n==MOSTRAR CLIENTES POR DNI==") for c in empresa.clientes:
mostrar_cliente(clientes, '11111111A') print(c)
mostrar_cliente(clientes, '11111111Z’) print("\n==MOSTRAR CLIENTES POR DNI==") # Consulto clientes por DNI
# Borro un cliente por DNI empresa.mostrar_cliente("11111111A")
empresa.mostrar_cliente("11111111Z")
print("\n==BORRAR CLIENTES POR DNI==") print("\n==BORRAR CLIENTES POR DNI==") # Borro un cliente por DNI
borrar_cliente(clientes, '22222222V') empresa.borrar_cliente("22222222V")
borrar_cliente(clientes, '22222222B') empresa.borrar_cliente("22222222B")
# Muestro de nuevo todos los clientes print("\n==LISTADO DE CLIENTES==") # Muestro de nuevo todos los
print("\n==LISTADO DE CLIENTES==") clientes
for c in empresa.clientes:
print(clientes) print(c) 2
Programación Orientada a Objetos
Introducción  probablemente optaríamos por algo así.

 Hasta el momento, hemos estudiado en #Tema1_01


import math
Fundamentos de Programación el modelo def crea_punto(x,y):
imperativo, basado en secuencia, return (x,y)
decisión y repetición. def calcula_distancia(p1,p2):
distancia_x=p1[0]-p2[0]
 Para este modelo, como ya se ha visto, se distancia_y=p1[1]-p2[1]
crean funciones que agrupan return math.sqrt((distancia_x**2)+(dista
ncia_y**2))
instrucciones, y módulos que agrupan
funciones. Sin embargo, se ha demostrado def calcula_distancia_origen(p1):
return calcula_distancia(p1,(0,0))
que estas estrategias no escalan bien a
medida que los proyectos van creciendo if __name__=="__main__":
p0=crea_punto(0,0)
tanto en tamaño como en complejidad. p1=crea_punto(3,4)

 Por ejemplo, con los conocimientos print("De", p0, "a", p1, ":", calcula_di
stancia(p0,p1))
adquiridos hasta ahora, si quisiéramos
desarrollar un módulo de soporte para el
manejo de puntos de coordenadas  Este código, que utiliza tuplas para
cartesianas en dos dimensiones, modelar puntos, supone en realidad
3
varios problemas.
Programación Orientada a Objetos
Otro Ejemplo: Tipos de datos «a medida»

 El primero es que no existe una separación  Supongamos que en un programa


clara en cómo se representan los puntos
utilizamos el nombre, el DNI y la edad de
(tuplas) y la interfaz (el conjunto de
dos clientes.
funciones) para manejarlos.

 Además, nada impide que a


 Necesitaremos tres variables para almacenar
calcula_distancia_origen(0), en lugar los datos de cada cliente, dos variables con
de un punto se le pase como parámetro valores de tipo cadena (el nombre y el
cualquier otro valor (texto, entero, real…). DNI) y otra con un valor de tipo entero (la

 La abstracción con respecto a los detalles


edad).
internos del módulo se manifiesta
 Necesitaríamos tantas variables como
precisamente de nuevo en la función
clientes quisiéramos almacenar y utilizar
calcula_distancia_origen(0), en la que
funciones con varios parámetros cada vez
se calcula la distancia con el punto
representado por la tupla (0, 0), en lugar de que quisiéramos hacer algo…
crear un nuevo punto llamando a
 Una mejora sería almacenar listas de
crea_punto(i1,i2), que sería lo correcto.
datos: Una para los nombres, otra para el
DNI, otra para la edad, etc.
4
Programación Orientada a Objetos
 Asumiríamos que cada dato de cada cliente  Siguiendo esta filosofía, también es posible
ocupa el mismo índice en cada lista. Pero si tener listas de clientes, que no serán más
quisiéramos ordenarla por orden alfabético… que listas de listas:

 En resumen, es posible desarrollar >>> clientes=[juan, pepe]

programas que gestionan «clientes» con


esta metodología, pero resulta incómodo  Si trabajamos con esta metodología nos
 Hay una alternativa a trabajar con grupos de aguardan nuevos problemas…
tres variables independientes por cliente:  Imaginemos que nuestro programa gestiona
definir un «cliente» como una lista con información sobre clientes y coches y que de
tres elementos. En cada elemento de la los coches necesitamos los siguientes datos:
lista almacenaremos uno de sus valores, marca, modelo, matrícula y edad.
siempre en el mismo orden:
 Lo ideal sería que Python proporcionara un
>>> nombre=0 tipo de datos básico «cliente» del mismo
>>> dni=1
>>> edad=2 modo que proporciona datos enteros,
>>> juan=['Juan Perez', '12345678Z',19] flotantes, listas, etc.
>>> pepe=['Pepe Lopez', '23456789D',20]

5
Programación Orientada a Objetos
 Una variable cuyo contenido fuera de tipo  Básicamente, estas entidades se componen
«cliente» albergaría en un solo paquete de datos y funciones, agrupadas en un
toda la información propia de un cliente: su concepto encapsulador llamado clase.
nombre, su DNI y su edad.
POO en Python: Definición de clases
 Pero también pediríamos el tipo coche, el
 Python es un lenguaje de POO. Una de
tipo empleado, el tipo profesor, etc.
las características más poderosas en estos
Python nos permite definir nuevos tipos de
lenguajes es la capacidad de perm itir a un
datos combinando tipos existentes.
program ador crear nuevas clases que
 Podemos definir un tipo de datos nuevo, m odelen los datos necesarios para
digamos Cliente, que agrupe en un solo resolver el problem a.
paquete los datos básicos que lo forman.
 Podemos imaginar los objetos como un
Los nuevos tipos de datos recibirán el
nuevo tipo de dato cuya definición viene
nombre genérico de clases.
dada en una estructura llamada clase.
 La programación orientada a objetos
 Usamos TAD’s para proporcionar la
se basa en simular la actuación de las
descripción lógica de cómo se ve un objeto
distintas entidades que toman parte en la
de datos (su estado) y qué puede hacer
automatización de un problema.
(sus métodos). 6
Programación Orientada a Objetos
 Mediante la construcción de una clase que  para realizarlo es tan fácil como llamar a la
implementa un TAD, un programador puede clase como si fuera una función.
aprovechar el proceso de abstracción y al
 Cada instancia tiene su propia referencia
mismo tiempo proporcionar los detalles
(ya que están en lugares distintos de la
necesarios para utilizar realmente la
memoria). En cambio, la clase no tiene una
abstracción en un programa.
referencia porque es sólo un guion de
Atributos y métodos instrucciones

 Los objetos "existen" sólo durante la  Si hay algo que ilustre el potencial de la
ejecución del programa y se almacenan en POO esa es la capacidad de definir variables
la memoria del sistema operativo. y funciones dentro de las clases, aunque
aquí se conocen como atributos y
 Es decir, mientras las clases están ahí en el
métodos respectivamente.
código haciendo su papel de instrucciones,
los objetos no existen hasta que el  Si por un lado tenemos las "variables" de las
programa se ejecuta y se crean en la clases (atributos), por otro tenemos sus
memoria. "funciones“ (métodos), que evidentemente
nos permiten definir funcionalidades para
 Este proceso de "crear" los objetos en la
llamarlas desde las instancias.
memoria se denomina instanciación y
Programación Orientada a Objetos
 Es posible definir unos atributos básicos  atributos, debe anteponerse "self.", para
en la clase. De esa manera todos los objetos que se distingan de los parámetros
podrían tener unos atributos por defecto y
podemos consultar el valor por defecto que
deben tener los objetos haciendo referencia Métodos especiales
al atributo en la definición de la clase.
Constructor
 Definir un método es bastante simple,
 Ahora que sabemos crear métodos y hemos
sólo tenemos que añadirlo en la clase y
aprendido para qué sirve el argumento
llamarlo desde el objeto con los paréntesis,
self, es momento de introducir algunos
como si de una función se tratase
métodos especiales de las clases.
 Cuando se ejecuta un método desde un
 El constructor es un método que se llama
objeto, se envía un primer argumento
automáticamente al crear un objeto, se
implícito que hace referencia al propio
define con __init__. Su finalidad es la de
objeto.
construir los objetos, permitiéndonos enviar
 Este primer parámetro que representa al datos para construirlo.
objeto sobre el que está actuando se
denomina self. Para acceder a los
Programación Orientada a Objetos
#Class_Clientes.py  Por ejemplo, este fragmento de programa
class Cliente:
def __init__(self, nombre, dni, edad): muestra el dni de los integrantes de la
self.nombre=nombre lista:
self.dni=dni
self.edad=edad
for i in clientes:
print(clientes[i].dni)
 Todos los métodos tendrán como primer
parámetro uno llamado self. Le siguen #o también
tantos parámetros como campos tiene una
variable del tipo Cliente for Cliente in clientes :
print(clientes.dni)
 Cada cliente creado es una instancia u
objeto de la clase Cliente.  Podemos dotar a los objetos de la clase
Cliente de cierta «inteligencia». Definiremos
 Si en algún momento necesitamos acceder a
métodos adicionales :
nombre, dni o edad, podemos hacerlo
anteponiendo el nombre del objeto al
def iniciales(self):
nombre del atributo, separándolos mediante cadena=''
for caracter in self.nombre:
un punto ('.’), como se verá en el ejemplo
if 'A' <= caracter <='Z':
Tema1_02 cadena=cadena+caracter+'. '
return cadena
9
Programación Orientada a Objetos
String #Tema1_02
from Class_Clientes import Cliente
 El método __str__ es el que devuelve la
juan = Cliente('Juan Perez','12345678Z',19)
representación de un objeto en forma de pepe = Cliente('Pepe Lopez','23456789D',20)
cadena. Cuando se llama es cuando toni = Cliente('Antonio Perez','87654321Q',
20)
imprimimos una variable por pantalla (esto clientes = [toni, juan, pepe]
es opcional, pero es realmente muy útil y print(juan)
print('Las iniciales de ', juan.nombre, 'so
recomendado). n ', juan.iniciales())
def __str__(self):
cadena='Nombre: {0}\n'.format(self.n
ombre) Destructor
cadena=cadena + 'DNI: {0}\n'.format(
self.dni)  Si existe un constructor también debe existir
cadena=cadena + 'Edad: {0}\n'.format un destructor que se llame al eliminar el
(self.edad)
return cadena objeto para que encargue de las tareas de
limpieza como vaciar la memoria. Ese es el
 Y este sería el código del programa principal papel del método especial del.
que usa la clase cliente. Lo primero que
 Es muy raro sobreescribir este método
debemos de hacer es importar la clase
porque se maneja automáticamente, pero es
Cliente del fichero que la contiene
interesante saber que existe.
(Class_Clientes.py) 10
__del__(self)
Programación Orientada a Objetos
Referencias y objetos  E invocarlo mediante
>>>copia=juan.copia()
 Hemos de tratar ahora un problema que ya
nos es conocido
 ¿Cómo ha quedado la memoria en este
>>> juan=Cliente('Juan Perez’, '12345678Z’, caso? Observa detenidamente la siguiente
19)
>>> copia=juan
figura:
>>> copia.edad=20
>>> print (copia.edad)
20
>>> print(juan.edad)
20

 Si quisiéramos que ambos tuvieran su propia


zona de memoria para estos datos:
def copia(self):
nuevo=Cliente(self.nombre[:], self.d
 Para solucionar este problema, lo mejor es ni[:],self.edad)
definir un método que genere una copia return nuevo

def copia(self):
nuevo=Cliente(self.nombre,self.dni,se
lf.edad)
11
return nuevo
Programación Orientada a Objetos
def consultar(self, nombre):
Una aplicación: un listín telefónico
if nombre in self.listin:
 Construyamos un programa que gestione un return self.listin[nombre]
listín telefónico que permita asociar a una else:
return []
persona más de un teléfono. A través de un
menú podremos seleccionar diferentes def eliminar(self, nombre):
acciones: añadir y/o eliminar teléfonos y if nombre in self.listin:
del self.listin[nombre]
consultar el listín.

#Class_Agenda.py #Tema1_03
class Listin: from Class_Agenda import Listin
def __init__(self):
def menu():
self.listin={}
opcion = 0
while opcion < 1 or opcion > 4:
def añadir(self, nombre, telefono):
print('\tEscoge una opción:')
if nombre in self.listin:
print('\t\t1) Añadir teléfonos.')
if not telefono in self.listin[n
print('\t\t2) Consultar el listín.')
ombre]:
print('\t\t3) Eliminar persona del listí
self.listin[nombre].append(t n.')
elefono)
print('\t\t4) Salir.')
else:
self.listin[nombre]=[telefono] opcion = int(input('\t Opción? '))
return opcion
12
Programación Orientada a Objetos
# programa principal elif opcion == 3:
listin = Listin() nombre = input('Nombre: ')
listin.eliminar(nombre)
opcion = 0
while opcion != 4:
opcion = menu() Length
if opcion == 1:  Finalmente, otro método especial
nombre = input('Nombre: ')
interesante es el que devuelve la longitud
telefono = input('Teléfono: ')
listin.añadir(nombre, telefono) __len__. Normalmente está ligado a
mas = input(f'¿Deseas añadir otro secuencias, pero nada impide definirlo en
teléfono a {nombre} (s/n)? ')
una clase. Lo usaremos más adelante.
while mas == 's':
telefono = input('Teléfono: ')
class Cancion:
listin.añadir(nombre, telefono)
mas = input(f'¿Deseas añadir def __init__(self, autor, titulo,duracio
otro teléfono a {nombre} (s/n)? ') n):
elif opcion == 2: self.duracion = duracion
nombre = input('Nombre: ') def __len__ (self ):
telefonos = listin.consultar(nombre) return self.duracion
for telefono in telefonos:
cancion = Cancion("Queen", "Don't Stop Me No
print(telefono) w",210)
print(len(cancion))
print(cancion.__len__())
Programación Orientada a Objetos
Puntos y vectores en el plano

 En este ejemplo vamos a trabajar el


concepto de puntos, coordenadas y
vectores sobre el plano cartesiano y cómo
la POO puede ser una excelente aliada para
trabajar con ellos.

 El objetivo de todo esto es describir la  Un vector en el plano hace referencia a un


posición de puntos sobre el plano en forma segmento orientado, generado a partir de
de coordenadas, que se forman asociando el dos puntos distintos y se entiende que un
valor del eje de las X (horizontal) con el vector tiene longitud y dirección/sentido
valor del eje Y (vertical).

 La representación de un punto es sencilla:


P(X,Y) dónde X y la Y son la distancia
horizontal (izquierda o derecha) y vertical
(arriba o abajo) respectivamente, utilizando
como referencia el punto de origen (0,0),
justo en el centro del plano.
14
Programación Orientada a Objetos
#Tema1_04
 El programa principal tendríamos…
class Punto:
def __init__(self,x,y): p1=Punto(11,12)
self.x=x p2=Punto(5,6)
self.y=y print("p1: "+ str(p1))
print("p2: "+ str(p2))
 Como los métodos se comportan como
funciones (tienen mismas características)  Si en algún momento necesitamos acceder
nos permite definir valores nulos, valores por a x e y, podemos hacerlo anteponiendo el
posición y nombre, argumentos nombre del objeto al nombre del atributo,
indeterminados, etc. separándolos mediante un punto ('.').
 El método __str__ es el que devuelve la
p1.x += 100
representación de un objeto en forma de
p2.y = 99
cadena. print("p1: "+ str(p1))
def __str__(self): print("p2: "+ str(p2))
return str.format("({0}, {1})",self.
x,self.y)
 Por supuesto, podemos tener más
 O mejor escrito aun… funciones dentro de una clase que
def __str__(self): __init__() y __str__(), estas son sólo
return f"({self.x}, {self.y})" 15
las más básicas.
Programación Orientada a Objetos
 A continuación, la clase Punto aparece con def calcula_distancia_origen(self):
"""Retorna la distancia desde el ori
un nuevo método que calcula la distancia gen de coordenadas a este punto
euclidiana, calcula_distancia(pl,p2) :return: La distancia entre ambo
s puntos
#Class_Puntos.py
"""
import math return self.calcula_distancia(Punto(
class Punto: 0, 0))
def __init__(self,x,y):
self.x=x
self.y=y  Y veamos cómo se importa la clase y como
def __str__(self): se utiliza desde un programa Python.
return f"({self.x}, {self.y})"
#Tema1_05
def calcula_distancia(self,p2):
from Class_Puntos import Punto
"""Retorna la distancia entre este p #Importamos la clase creada y reflejada en
unto y otro la clase puntos (Class_Puntos.py)
:param p2: El otro punto.
:return: La distancia entre este if __name__== "__main__":
punto y p2. p1=Punto(11,12)
""" p2=Punto(5,6)
distancia_x=self.x-p2.x
distancia_y=self.y-p2.y print("p1: "+str(p1))
return math.sqrt((distancia_x**2)+(d print("p2: "+str(p2))
istancia_y**2)) print(f"Distancia de {p1} a {p2} es
{p1.calcula_distancia(p2)}")
Programación Orientada a Objetos
Variables y funciones estáticas  En realidad, si quisiéramos tener una
solución completa, sería necesario tener un
 Los métodos estáticos son un tipo
solo objeto Punto, uno compartido por
especial de métodos de la clase. En lugar de
todos los objetos, es decir, una variable
estar ligados a cada uno de los objetos,
miembro estática.
están ligados a la clase, y por tanto todos
los objetos de la clase, comparten también
# Tema1_07
dichos métodos, es decir, mientras cada class Punto:
objeto puede llegar a tener una copia de ORIGEN=None #ORIGEN variable estática
cada método normal, todos los objetos @staticmethod #get_origen es un método e
stático
com parten una sola copia de un def get_origen():
m étodo estático. if Punto.ORIGEN is None:
Punto.ORIGEN=Punto(0,0)
# Tema1_06 return Punto.ORIGEN
class Punto:
@staticmethod def calcula_distancia_origen(self):
def get_origen(): """Retorna la distancia desde el ori
return Punto(0,0) gen de coordenadas a este punto
:return: La distancia entre ambos
def calcula_distancia_origen(self): puntos """
return self.calcula_distancia(Punto. return self.calcula_distancia (Punto
get_origen()) .get_origen())
Programación Orientada a Objetos
 En un último ejemplo, se utiliza una Composición
variable estática para llevar la cuenta de
 La composición consiste en formar clases
los puntos creados.
a partir de otras. Por ejemplo, un objeto de
la clase Línea se crea a partir de dos
#Tema1_08
class Punto: objetos de la clase Punto (es decir, las
num_puntos=0 #num_puntos es un contador
líneas tienen un punto de inicio, y otro de
estático
ORIGEN=None #ORIGEN es una variable está fin).
tica
@staticmethod #get_origen es un método e  Simplemente, se trata de crear en el
stático
def get_origen(): inicializador de la clase contenedora, al
if Punto.ORIGEN is None: menos un atributo que debe ser un objeto
Punto.ORIGEN=Punto(0,0)
return Punto.ORIGEN de la clase que se contiene.

def __init__(self,x,y):  En el caso de la línea, debe tener dos


self.x=x atributos que deben pertenecer a la clase
self.y=y
Punto.num_puntos+=1 Punto.
Programación Orientada a Objetos
#Class_Lineas.py Ejercicio
from Class_Puntos import Punto
class Linea:  Crea una clase llamada Punto con sus dos
"""Representa líneas formadas por dos pu coordenadas X e Y.
ntos, p1 y p2."""
def __init__(self,x1,y1,x2,y2):
 Añade un método constructor para crear
puntos fácilmente. Si no se recibe una de
self.p1=Punto(x1,y1) sus coordenadas, su valor será cero.
self.p2=Punto(x2,y2)
 Sobreescribe el método string, para que al
def calcula_longitud(self): imprimir por pantalla un punto aparezca en
return self.p1.calcula_distancia(sel
f.p2)
formato (X,Y)
 Añade un método llamado cuadrante que
def __str__(self):
indique a qué cuadrante pertenece el punto,
return f"({self.p1}, {self.p2})"
teniendo en cuenta que si X == 0 e Y != 0
#Tema1_09
se sitúa sobre el eje Y, si X != 0 e Y == 0 se
from Class_Lineas import Linea
sitúa sobre el eje X y si X == 0 e Y == 0
if __name__ == "__main__": está sobre el origen.
l = Linea(1, 2, 3, 4)
 Añade un método llamado vector, que
print("Línea:", l, "de tamaño:", l.calcu
la_longitud()) tome otro punto y calcule el vector
resultante entre los dos puntos.
Programación Orientada a Objetos
#Class_Puntos2.py
 Añade un método llamado distancia, que
import math
tome otro punto y calcule la distancia entre class Punto:
def __init__(self, x=0, y=0):
los dos puntos y la muestre por pantalla. La self.x = x
fórmula es la siguiente: self.y = y
def __str__(self):
return f"({self.x}, {self.y})"
def cuadrante(self):
 Crea una clase llamada Rectángulo con dos if self.x > 0 and self.y > 0:
print(f"{self} pertenece al primer
puntos (inicial y final) que formarán la cuadrante")
elif self.x < 0 and self.y > 0:
diagonal del rectángulo.
print(f"{self} pertenece al segundo
cuadrante")
 Añade un método constructor para crear
elif self.x < 0 and self.y < 0:
ambos puntos fácilmente, si no se envían se print(f"{self} pertenece al tercer
cuadrante")
crearán dos puntos en el origen por defecto. elif self.x > 0 and self.y < 0:
print(f"{self} pertenece al cuarto
 Añade al rectángulo un método llamado cuadrante")
base que muestre la base. elif self.x != 0 and self.y == 0:
print(f"{self} se sitúa sobre el eje
 Añade al rectángulo un método llamado X")
elif self.x == 0 and self.y != 0:
altura que muestre la altura. print(f"{self} se sitúa sobre el eje
Y")
 Añade al rectángulo un método llamado else:
área que muestre el área. print(f"{self} se encuentra sobre el
origen") 20
Programación Orientada a Objetos
def vector(self, p): def altura(self):
print(f"El vector entre {self} y {p} print(f"La altura del rectángulo es
es ({p.x - self.x}, {p.y - self.y})") {self.vAltura}")
def distancia(self, p):
d = math.sqrt((p.x - self.x)**2 + def area(self):
(p.y - self.y)**2) print(f"El área del rectángulo es
print(f"La distancia entre los {self.vArea}")
puntos {self} y {p} es {d}")
#Tema1_10
from Class_Puntos2 import *
class Rectangulo:
def __init__(self, pInicial=Punto(), A = Punto(2, 3)
pFinal=Punto()): B = Punto(5, 5)
self.pInicial = pInicial C = Punto(-3, -1)
self.pFinal = pFinal D = Punto(0, 0)
self.vBase = abs(self.pFinal.x - A.cuadrante()
self.pInicial.x) C.cuadrante()
self.vAltura = abs(self.pFinal.y - s D.cuadrante()
elf.pInicial.y) A.vector(B)
self.vArea = self.vBase * B.vector(A)
self.vAltura A.distancia(B)
B.distancia(A)
A.distancia(D)
def base(self):
B.distancia(D)
print(f"La base del rectángulo es C.distancia(D)
{self.vBase}") R = Rectangulo(A, B)
R.base()
R.altura()
R.area()
Programación Orientada a Objetos
#Class_Alumnos.py
Clase alumno y Clase Asignatura
class Alumno:
"""Representa alumnos en un colegio, con dni
 En el siguiente ejemplo, se crea la clase
, nombre y edad."""
Alumno y la clase Asignatura. Después, def __init__(self,dni,nombre,edad):
crearemos dos asignaturas y dos alumnos, self.dni=dni
self.nombre=nombre
demostrando como alumnos pueden estar
self.edad=edad
en más de una asignatura, o solo en una. def __str__(self):
return f"{self.dni}: {self.nombre}
 Así, los objetos Alumno tienen sentido por ({self.edad})"
sí mismos y no como parte de un objeto
#Tema1_11
Asignatura, pudiendo extinguirse de hecho from Class_Alumnos import Alumno
una de ellas sin consecuencias para los
if __name__ == "__main__":
alumnos. print(str(Alumno('11222333A', "Jose", 25)))

 Podríamos tener en cuenta que los objetos


 La clase Asignatura, a continuación,
alumno tendrán sentido mientras exista el
utiliza objetos de la clase Alumno para
centro en el que estudian, mientras que las
llevar la cuenta de los alumnos que están
asignaturas, independientemente, estén
inscritos en ella, empleando una lista. Las
vinculadas a un plan de estudios
listas de objetos se comportan de la misma
determinado….
forma que las listas de cualquier otro tipo.
Programación Orientada a Objetos
#Class_Asignaturas.py #Tema1_12
class Asignatura:
""" Representa asignaturas, guarda su no from Class_Alumnos import Alumno
mbre y lista de alumnos""" from Class_Asignaturas import Asignatura
def __init__(self, nombre):
if __name__ == "__main__":
self.nombre=nombre
asignatura1 = Asignatura("Introducción a
self.alumnos=[]
Python")
asignatura2 = Asignatura("Matemáticas")
def matricula(self,alumno):
alumno1 = Alumno('11222333A',"Jose",25)
self.alumnos+=[alumno]
alumno2 = Alumno('99999999B',"Jaimito",
55)
def get_num_alumnos(self):
asignatura1. matricula(alumno1)
return len(self.alumnos)
asignatura1. matricula(alumno2)
def get_alumno(self,i): asignatura2. matricula(alumno2)
return self.alumnos[i] print(str(asignatura1))
print(str(asignatura2))
def __str__(self):
Introducción a Python
cadena=self.nombre + "\n\n"
11222333A: Jose (25)
for alumno in self.alumnos: 99999999B: Jaimito (55)
cadena +=str(alumno) + '\n'
return cadena Matemáticas

99999999B: Jaimito (55) 23


Programación Orientada a Objetos
Otro ejemplo: gestión de calificaciones de  Definamos primero el tipo de datos
estudiantes Estudiante. Cada estudiante tiene cuatro
campos (nombre, grupo, nota y práctica):
 Vamos a diseñar un programa que gestiona
#Class_Estudiantes.py
la lista de estudiantes de una asignatura. De class Estudiante:
cada estudiante guardaremos su nombre, su def __init__(self, nombre, grupo, nota, prac
tica):
grupo de teoría (A, B o C), la nota obtenida self.nombre=nombre
en el examen y si ha entregado o no las self.grupo=grupo
prácticas. self.nota=nota
self.practica=practica
 Tener aprobada la asignatura implica haber def __str__(self): #Para poder imprimir en p
antalla el estudiante
entregado las prácticas y haber obtenido en cadena = f'Nombre: {self.nombre}\n'
el examen una nota igual o superior a 5. cadena = cadena + f'Grupo:
{self.grupo}\n'
 El programa permitirá añadir estudiantes cadena = cadena + f'Nota Examen:
{self.nota:3.1f}\n'
a la lista, mostrar la calificación de cada
if self.practica:
uno de ellos y efectuar algunas estadísticas cadena=cadena+'Practica Entregada'
sobre las notas, como obtener la nota else:
cadena=cadena+'Practica NO Entregada
media o el porcentaje de estudiantes
'
que ha entregado las prácticas. return cadena
24
Programación Orientada a Objetos
def calificacion(self): def lee_y_añade_estudiante(lista):
if not self.practica: estudiante = lee_estudiante()
return 'Suspenso' lista.append(estudiante)
else:
if self.nota<5:
return 'Suspenso' def acta(lista):
elif self.nota<7: for estudiante in lista:
return 'Aprobado' print(estudiante.nombre, estudiante.cali
elif self.nota<8.5: ficacion())
return 'Notable'
elif self.nota<10:
return 'Sobresaliente' def nota_media(lista):
else: suma = 0
return 'Matrícula de Honor' cantidad = 0
for estudiante in lista:
#Tema1_13 if estudiante.practica:
from Class_Estudiantes import Estudiante suma += estudiante.nota
cantidad += 1
if cantidad != 0:
def lee_estudiante(): # Esto no es un método, s
return suma/cantidad
ino una función
else:
nombre = input('Nombre: ') return 0.0
grupo = input('Grupo(A, B o C): ')
nota = float(input('Nota de Examen: '))
entregada = input('Práctica entregada (S/N):
')
practica = entregada== S' or entregada=='s'
return Estudiante(nombre, grupo, nota, pract
ica)
25
Programación Orientada a Objetos
def porcentaje_de_practicas_entregadas(lista):
Herencia
if len(lista) != 0:
cantidad = 0
for estudiante in lista:  La herencia se utiliza para crear clases más
if estudiante.practica:
detalladas a partir de una ya existente, de
cantidad += 1
return cantidad/len(lista)*100 manera que la nueva clase tiene una relación
else:
return 0.0 "es un tipo de " con respecto a la segunda.

 Además, un objeto de una clase derivada


def mejores_estudiantes(lista):
nota_mas_alta = 0 debe poder utilizarse en cualquier contexto
mejores = [] en el que se pueda utilizar un objeto de la
for estudiante in lista:
if estudiante.practica: superclase.
if estudiante.nota > nota_mas_alta:
mejores = [estudiante]  Por ejemplo, la clase Persona, podría ser la
nota_mas_alta = estudiante.nota
elif estudiante.nota == nota_mas_al superclase de Empleado.
ta:
mejores.append(estudiante)  Efectivamente, un empleado es un tipo de
return mejores
persona, y desde luego un objeto
empleado debería poder ser utilizado en
pepe = Estudiante('Pepe Garcia', 'A', 7.7, True
) cualquier contexto en el que pudiera
print(pepe.calificacion())
utilizarse una persona.
#hacer ejercicio al respecto 26
Programación Orientada a Objetos
#Class_Personas.py
 La herencia es una herramienta muy
class Persona:
def __init__(self, nombre, edad): poderosa, pero no se debe abusar de la
self.nombre=nombre misma. Muchas veces, se emplea herencia
self.edad=edad
en exceso cuando se debería utilizar
def __str__(self): composición.
return self.nombre + " (" + str(self.edad) + ")
"
#Class_Empleados.py (podría estar en el mismo fichero)
 La forma más práctica de distinguir entre
from Class_Personas import Persona las situaciones que requieren el uso de
class Empleado(Persona):
herencia o composición es utilizar los
def __init__(self,nombre,edad,empresa,email):
super().__init__(nombre,edad) lemas: “es un tipo de” y “es parte de” .
self.empresa=empresa
El primero sirve para establecer si existe o
self.email=email
def __str__(self): no una relación de herencia, mientras
cadena=super().__str__() que el segundo permite saber si existe una
cadena +=" en " + self.empresa
cadena +=": " + self.email relación de composición.
return cadena
 Por ejemplo, un em pleado es un tipo
#Tema1_14
de persona , mientras que desde luego un
from Class_Personas import Persona, Empleado
em pleado no es parte de una
if __name__ == "__main__":
print(Persona("Jose", 18))
persona .
print(Empleado("Jose", 18, "Usc", "[email protected]")) 27
Programación Orientada a Objetos
if __name__ == "__main__":
 De la misma manera, un punto es parte
random.seed()
de una línea , mientras que una línea no p = random.choice([p1, p2])
es un tipo de punto . print(p)

Algunas clases de uso común


Polimorfismo
 Reutilización de código
 El polimorfismo consiste en que un  Disponer de Clases comunes

objeto pueda ser utilizado correctamente,


La clase fecha
llamando a sus funciones (en lugar de a las #Class_Fechas.py
funciones de la(s) superclase(s)), incluso class Fecha:
en el caso en el que no se conoce la clase def __init__(self,dia,mes,año):
self.dia=dia
exacta a la que pertenece el objeto.
self.mes=mes
self.año=año
#Tema1_15
def __str__(self):
import random
return
f'{self.dia}/{self.mes}/{self.año}'
from Class_Personas import Persona, Empleado
def bisiesto(self):
return self.año%4==0 and (self.año%1
p1 = Persona("Jose", 18) 00!=0 or self.año%400==0)
p2 = Empleado("Jose", 18, "Usc", "jangel@usc
.es")
Programación Orientada a Objetos
def es_menor(self,fecha1): f = Fecha(int(s[0]),int(s[1]),int(s[2]))
if self.año<fecha1.año: print('\nTu fecha: ', F)
return True print('La fecha con la que comparamos: ',
elif self.año>fecha1.año: fecha1)
return False print('Año Bisiesto?: ', f.bisiesto())
if self.mes<fecha1.mes: print('Menor que la fecha actual?: ', f.e
return True s_menor(fecha1))
elif self.mes>fecha1.mes:
return False elif F[-5] == '/': #se introduce fecha
return self.dia<fecha1.dia separada por /
Fech = F.replace('/', ' ')
s = Fech.split()
#Tema1_16 f = Fecha(int(s[0]),int(s[1]),int(s[2]))
from Class_Fechas import *
from datetime import * print('\nTu fecha: ', F)
print('La fecha con la que comparamos: ',
fecha1)
hoy=str(datetime.now()).split() print('Año Bisiesto?: ', f.bisiesto())
fecha2=hoy[0].split('-') print('Menor que la fecha actual?: ', f.e
fecha1 = Fecha(int(fecha2[2]), int(fecha2[1] s_menor(fecha1))
), int(fecha2[0]))
F = input('Dime el dia,mes,año o bien Dime el dia,mes,año: 12/5/2024
dia/mes/año: ’).strip()
if F[-5] == ',': #se introduce fecha Tu fecha: 12/5/2024
separada por comas La fecha con la que comparamos: 5/2/2021
Año Bisiesto?: True
Fech = F.replace(',', ' ') Menor que la fecha actual?: False
s = Fech.split()
Algorítmica y Estructuras de
Datos
Tema1_2: Programación Orientada a
Objetos. Clases en C++

Efrén Arias Jordán


Dpto Electrónica e Computación
Programación Orientada a Objetos
Una clase Fraccion  La notación self.num en el constructor
𝟑𝟑 define que el objeto fraccion tenga un
 Una fracción como consta de dos partes.
𝟓𝟓
objeto de datos interno llamado num como
Numerador y Denominador (enteros)
parte de su estado. Del mismo modo,
 Las operaciones para el tipo Fraccion self.den crea el denominador.
permitirán que un objeto de datos
Fraccion se comporte como cualquier >>> miFraccion = Fraccion(3,5)

otro valor numérico. Necesitamos ser


 Crea un objeto llamado miFraccion que
capaces de sumar, restar, multiplicar 𝟑𝟑
representa la fracción
y dividir fracciones, entregando la forma 𝟓𝟓

más simplificada

class Fraccion:
def __init__(self,arriba,abajo):
self.num = arriba
self.den = abajo

def __str__(self):
return str(self.num)+"/"+str(self
.den) >>> print (miFraccion)
3/5
31
Programación Orientada a Objetos
 Podemos redefinir muchos otros métodos def __add__(self,otraFraccion):

para nuestra nueva clase Fraccion. Algunas nuevoNum = self.num*otraFraccion.den +


de los más importantes son las operaciones self.den*otraFraccion.num
nuevoDen = self.den * otraFraccion.den
aritméticas básicas.
return Fraccion(nuevoNum,nuevoDen)

 Dos fracciones deben tener el mismo


 El método de adición ya funciona como
denominador para poder ser sumadas o
queremos, pero podríamos mejorar algo:
restadas.
𝟔𝟔 𝟏𝟏 𝟏𝟏
 es el resultado correcto de ( + ) pero no
𝟖𝟖 𝟒𝟒 𝟐𝟐
𝑎𝑎 𝑐𝑐 𝑎𝑎𝑎𝑎 𝑐𝑐𝑐𝑐 𝑎𝑎𝑎𝑎 ± 𝑐𝑐𝑐𝑐 está en la representación de “términos
± = ± =
𝑏𝑏 𝑑𝑑 𝑏𝑏𝑏𝑏 𝑏𝑏𝑏𝑏 𝑏𝑏𝑏𝑏 menores”.
𝟑𝟑
 La mejor representación sería .
𝟒𝟒
 La multiplicación de dos fracciones
Necesitamos una función auxiliar, o un nuevo
𝑎𝑎 𝑐𝑐 𝑎𝑎𝑎𝑎
𝑥𝑥 = método, que calcule el MCD (algoritmo de
𝑏𝑏 𝑑𝑑 𝑏𝑏𝑏𝑏 Euclides).
 La división de dos fracciones
 Un grupo adicional de métodos que
𝑎𝑎 𝑐𝑐 𝑎𝑎𝑎𝑎 necesitamos incluir en nuestra clase de
÷ =
𝑏𝑏 𝑑𝑑 𝑏𝑏𝑏𝑏 ejemplo Fraccion permitirá que dos
fracciones se comparen entre sí. 32
Programación Orientada a Objetos
#Class_Fracciones.py def __mul__(self,otraFraccion):
class Fraccion: nuevoNum = self.num*otraFraccion.n
def __init__(self,arriba,abajo): um
self.num = arriba nuevoDen = self.den * otraFraccion
self.den = abajo .den
comun = mcd(nuevoNum,nuevoDen)
def __str__(self): return Fraccion(nuevoNum//comun,nu
return str(self.num)+"/" +str(self evoDen//comun)
.den)
def __truediv__(self,otraFraccion):
def __add__(self,otraFraccion): nuevoNum = self.num*otraFraccion.d
nuevoNum = self.num*otraFraccion.d en
en + self.den*otraFraccion.num nuevoDen = self.den * otraFraccion
nuevoDen = self.den * otraFraccion .num
.den comun = mcd(nuevoNum,nuevoDen)
comun = mcd(nuevoNum,nuevoDen) return Fraccion(nuevoNum//comun,nu
return Fraccion(nuevoNum//comun,nu evoDen//comun)
evoDen//comun)
def __eq__(self, otro):
def __sub__(self,otraFraccion): primerNum = self.num * otro.den
nuevoNum = self.num*otraFraccion.d segundoNum = otro.num * self.den
en - self.den*otraFraccion.num
nuevoDen = self.den * otraFraccion return primerNum == segundoNum
.den
comun = mcd(nuevoNum,nuevoDen)
return Fraccion(nuevoNum//comun,nu
evoDen//comun)
33
Programación Orientada a Objetos
def mcd(m,n): #Esto no es un método, es una Comparaciones
funcion
while m%n != 0:  Los métodos mágicos para cada una de las
mViejo = m operaciones de comparación son los
nViejo = n
siguientes.
m = nViejo
n = mViejo%nViejo
return n __lt__() para a < b.
__gt__() para a > b.
__le__() para a <= b.
 Ver Class_Fracciones1.py en donde se __ge__() para a >= b.
integra mcd(m,n) como un método estático. __ne__() para a != b.
__eq__() para a == b.
# Tema1_17
from Class_Fracciones import Fraccion
Ver documentación métodos mágicos
try:
x = Fraccion(1, 2) https://www.python-course.eu/python3_magic_method
y = Fraccion(2, 3) s.php

print('Suma de fracciones ', x + y)


print('Resta de fracciones ', x - y) Ejemplo
print('Multiplicación de fracciones ', x
 Hacer un programa para trabajar con
* y)
print('División de fracciones ', x / y) fracciones. Deberá solicitar al usuario lo que
desea hacer, y en función de su respuesta,
print('Igualdad de fracciones ', x == y) realizar las operaciones pertinentes. Para
except:
print("Error, no valen float") ello, completad el TAD racional con los
métodos necesarios.
Programación Orientada a Objetos
La clase Fracción en C++.  Es típico en C ++ crear todos los atributos
 En C ++, el método constructor siempre se de datos private y la mayoría de los
nombra con el mismo nombre que la clase métodos public. Todas las variables de
que crea. atributo bajo la palabra clave private solo

class Fraction {
podrán ser accedidas por los métodos de
public: clase del objeto, no por el usuario. Solo los
Fraction(int top, int bottom) métodos public pueden ser accedidos y
{ utilizados por el usuario.
//Fraction contructor method
num = top; //setting num's value  Polimorfismo significa la capacidad de
den = bottom;//setting den's value
aparecer en muchas formas. En la POO, el
}
private: polimorfismo se refiere a la capacidad de
int num; // num atribute procesar objetos o métodos de manera
int den; // den attribute diferente dependiendo de su tipo de datos,
};
clase, número de argumentos, etc.

 C ++ nos permite controlar el acceso con  Por ejemplo, podemos agregar constructores
las palabras clave de acceso public y adicionales para manejar números enteros e
private. instancias sin parámetros dados:
35
Programación Orientada a Objetos
Fraction (int top, int bottom){
 con derecho a acceder a todos los miembros
num = top;
den = bottom; privados y protegidos de la clase.
}
Fraction (int top){  La sobrecarga de operadores nos
num = top; permite hacer que los operadores funcionen
den = 1;
para clases definidas por el usuario al definir
}
Fraction (){ un significado especial para ese operador
num = 0; cuando se aplica a objetos de la clase como
den = 1;
operandos.
}

 Deberemos definir un método llamado algo  En C ++, este nuevo operador debe
así como show que permitirá que el objeto implementarse como un friend de la clase
Fraction se imprima a sí mismo como una para definir el comportamiento del operador
cadena en los objetos de la clase a partir de un
void show(){ método que no sea de la clase.
cout << num << "/" << den << endl;
}  Por ejemplo, en C ++, hacemos una
sobrecarga de operadores al declarar una
 Una función friend de una clase es una función amiga con el nombre de << y darle
función definida fuera de su alcance, pero una nueva implementación externa
36
Programación Orientada a Objetos
#include <iostream>
 Podemos sobrecargar muchos otros
using namespace std;
class Fraction { operadores para nuestra nueva clase
public: Fraction. Algunas de las más importantes
Fraction(int top = 0, int bottom = 1){
son las operaciones aritméticas básicas.
num = top;
den = bottom;}
//the following tells the compiler to look f Fraction operator +(const Fraction &otherFrac){
or this friend's definition outside the class //Note the return type is a Fraction
friend ostream &operator << (ostream &stream int newnum = num*otherFrac.den + den*otherFr
, const Fraction &frac); ac.num;
private: int newden = den*otherFrac.den;
int num, den; return Fraction(newnum, newden);
}; }
ostream &operator << (ostream &stream, const Fra
ction &frac) {
/** this is the definition. */  Podemos usar este método escribiendo una
stream << frac.num << "/" << frac.den; expresión aritmética estándar que incluya
return stream;
fracciones, asignando el resultado de la
}
int main() suma y luego imprimiendo nuestro
{ resultado.
Fraction myfraction(3, 5);
cout << myfraction;  También podríamos reescribir el operador de
return 0;
suma como una función amiga. 37
}
Programación Orientada a Objetos
friend Fraction operator +(const Fraction &frac1
 La clase Fracción completa en C++
, const Fraction &frac2);

Fraction operator +(const Fraction &frac1, const #include <iostream>


Fraction &frac2) { using namespace std;
int newnum = frac1.num * frac2.den + frac1
.den * frac2.num; int gcd(int m, int n){
int newden = frac1.den * frac2.den; while (m % n != 0) {
return Fraction(newnum, newden); int oldm = m;
} int oldn = n;

 La forma de sobrecargar operadores como m = oldn;


+ es una elección de diseño, ya que ambos n = oldm % oldn;
}
métodos funcionarán perfectamente bien.
return n;
Este es otro ejemplo de encapsulación. }
Para simplificar los resultados, deberíamos
class Fraction
usar el MCD {
int gcd(int m, int n) { public:
while (m%n != 0) { Fraction(int top, int bottom)
int oldm = m; {
int oldn = n; num = top;
m = oldn; den = bottom;
n = oldm%oldn; }
}
38
return n; }
Programación Orientada a Objetos
Fraction(int top) private:
{ int num, den;
num = top; };
den = 1;
} ostream &operator<<(ostream &stream, const Fract
Fraction() ion &fraction)
{ {
num = 1; stream << fraction.num << "/" << fraction.de
den = 1; n;
}
Fraction operator+(Fraction otherFrac) return stream;
{ }
int newnum = num * otherFrac.den + den *
otherFrac.num; # Tema1_17.cpp
int newden = den * otherFrac.den; #include "Class_Fracciones.hpp"
int common = gcd(newnum, newden);
int main()
return Fraction(newnum / common, newden {
/ common); Fraction x(1, 2);
} Fraction y(2, 4);
bool operator==(Fraction &otherFrac)
{ cout << x << " + " << y << " = " << x +
int firstnum = num * otherFrac.den; y << endl;
int secondnum = otherFrac.num * den; if (x == y)
cout << "x is equal y" << endl;
return firstnum == secondnum;
} else
friend ostream &operator<<(ostream &stream, cout << "x is not equal y" << endl;
const Fraction &fraction); return 0;
39
}
Programación Orientada a Objetos
Ejemplo  Podemos construir circuitos que tengan
funciones lógicas al combinar estas puertas
 Vamos a construir una simulación, una
en varios patrones y luego aplicarles un
aplicación para simular circuitos digitales. El
conjunto de valores de entrada.
bloque constructivo básico para esta
simulación será la puerta lógica.

 Estos conmutadores electrónicos


representan relaciones de álgebra booleana
entre su entrada y su salida. En general, las
puertas tienen una sola línea de salida.

 El valor de la salida depende de los valores


dados en las líneas de entrada.

 Para implementar un circuito, primero


construiremos una representación para
puertas lógicas. Las puertas lógicas se
organizan fácilmente en una jerarquía de
herencias de clase
40
Programación Orientada a Objetos
 En la parte superior de la jerarquía, la clase  Debajo de ellas, aparecen las funciones
PuertaLogica representa las características lógicas específicas de cada una.
más generales de las puertas lógicas:
Construyendo las clases
 una etiqueta para la puerta
 Cada puerta tiene una etiqueta para la
 una línea de salida. identificación y una sola línea de salida.
 El siguiente nivel de subclases divide las  Además, necesitamos métodos para permitir
puertas lógicas en dos familias, las que que un usuario de una puerta le pida la
tienen una línea de entrada y las que tienen etiqueta a la puerta.
dos.
class PuertaLogica:
def __init__(self,n):
self.etiqueta = n
self.salida = None

def obtenerEtiqueta(self):
return self.etiqueta

def obtenerSalida(self):
self.salida = self.ejecutarLogicaDePuert
a()
return self.salida
41
Programación Orientada a Objetos
 El otro comportamiento que necesita toda  El parámetro self es una referencia al
puerta lógica es la capacidad de conocer su verdadero objeto puerta que invoca el
valor de salida. Esto requerirá que la método.
puerta lleve a cabo la lógica apropiada
 Cualquier puerta lógica nueva que se
con base en la entrada actual.
agregue a la jerarquía simplemente tendrá
 Con el fin de producir la salida, la puerta que implementar la función
tiene que saber cuál es esa lógica ejecutarLogicaDePuerta y se utilizará en
el momento apropiado
 En este punto, no implementaremos la
función ejecutarLogicaDePuerta.
 La clase PuertaBinaria será una subclase
Todavía no sabemos cómo llevará a cabo
de PuertaLogica y agregará dos líneas de
cada puerta su propia operación lógica. entrada.
Estamos escribiendo un método que usará
código que aún no existe.  La clase PuertaUnaria también será
subclase de PuertaLogica pero sólo
 Estos detalles serán incluidos por cada contará con una única línea de entrada.
puerta individual que se añada a la
jerarquía.

42
Programación Orientada a Objetos
 En el diseño de circuitos digitales, estas  heredados de PuertaLogica. En este caso,
líneas a veces se llaman “pines” por lo que eso significa la etiqueta para la puerta. A
vamos a utilizar esa terminología en nuestra continuación, el constructor agrega las dos
implementación. líneas de entrada (pinA y pinB).

class PuertaBinaria(PuertaLogica): class PuertaUnaria(PuertaLogica):

def __init__(self,n): def __init__(self,n):


PuertaLogica.__init__(self,n) PuertaLogica.__init__(self,n)
self.pinA = None self.pin = None
self.pinB = None
def obtenerPin(self):
def obtenerPinA(self): return int(input(" Introduzca la entrada
return int(input("Introduzca la entrada del Pin para la puerta "+ self.obtenerEtiqueta(
del Pin A para la puerta "+ self.obtenerEtiqueta )+"-->"))
()+"-->"))

def obtenerPinB(self):  El único comportamiento que añade la clase


return int(input(" Introduzca la entrada
del Pin B para la puerta "+ self.obtenerEtiquet
PuertaBinaria es la capacidad de obtener
a()+"-->")) los valores de las líneas de entrada.

 Al crear una instancia de la clase  La misma implementación se usa para la

PuertaBinaria, primero queremos clase PuertaUnaria excepto que sólo hay

inicializar cualesquiera ítems de datos una línea de entrada. 43


Programación Orientada a Objetos
 Ahora que tenemos una clase general para  El mismo desarrollo se puede hacer para las
las puertas, podemos construir puertas puertas OR y las puertas NOT.
específicas que tengan un comportamiento
 La clase PuertaOR también será una
único.
subclase de PuertaBinaria y la clase
 Por ejemplo, la clase PuertaAND será una PuertaNOT extenderá la clase
subclase de PuertaBinaria, ya que las PuertaUnaria.
puertas AND tienen dos líneas de entrada.
 Ambas clases tendrán que proporcionar sus
class PuertaAND(PuertaBinaria): propias funciones
ejecutarLogicaDePuerta, ya que ése será
def __init__(self,n):
PuertaBinaria.__init__(self,n) su comportamiento específico.

def ejecutarLogicaDePuerta(self):
class PuertaNOT(PuertaUnaria):
a = self.obtenerPinA()
def __init__(self,n):
b = self.obtenerPinB()
PuertaUnaria.__init__(self,n)
if a==1 and b==1:
return 1
def ejecutarLogicaDePuerta(self):
else:
if self.obtenerPin():
return 0
return 0
else:
return 1
44
Programación Orientada a Objetos
 Vamos a centrar nuestra atención en la  Ahora, con la clase Conector, decimos que
construcción de circuitos. Para crear un un Conector TIENE-UNA PuertaLogica lo
circuito, necesitamos conectar las puertas cual significa que los conectores tendrán
juntas, la salida de una fluirá hacia la instancias de la clase PuertaLogica dentro
entrada de otra. Para ello, de ellos, pero no forman parte de la
implementaremos una nueva clase llamada jerarquía.
Conector.
 Al diseñar clases, hay que distinguir entre
 Esta clase usará la jerarquía de ellas por el aquéllas que tienen la relación ES-UN(A) (lo
hecho que cada conector tendrá dos cual requiere herencia) y aquéllas que
puertas, una en cada extremo. tienen relaciones TIENE-UN(A) (sin
herencia).
 Esta relación es muy importante en la
programación orientada a objetos. class Conector:
def __init__(self, deComp, aComp):
self.dePuerta = deComp
self.aPuerta = aComp
aComp.asignarProximoPin(self)

def obtenerFuente(self):
return self.dePuerta

def obtenerDestino(self): 45
return self.aPuerta
Programación Orientada a Objetos
 En la clase PuertaBinaria, para puertas  Ahora es posible obtener entradas desde dos
con dos posibles líneas de entrada, el lugares: externamente, como antes, y desde
conector debe conectarse a una sola línea. la salida de una puerta que está conectada a
esa línea de entrada.
 Si ambas están disponibles, elegiremos pinA
de forma predeterminada.  Esto requiere un cambio en los métodos
 Si pinA ya está conectado, entonces obtenerPinA y obtenerPinB
elegiremos pinB.  Si la línea de entrada no está conectada a
nada (None), entonces se pide al usuario que
 No es posible conectarse a una puerta sin
introduzca el valor externamente como antes.
líneas de entrada disponibles.
 Sin embargo, si hay una conexión, se accede a
def asignarProximoPin(self,fuente): ella y se consulta el valor de salida de
if self.pinA == None:
self.pinA = fuente
dePuerta.
else:
if self.pinB == None:
self.pinB = fuente
else:
raise RuntimeError("Error: NO HAY PIN
ES DISPONIBLES")
Programación Orientada a Objetos
def obtenerPinA(self): def obtenerSalida(self):
if self.pinA == None: self.salida = self.ejecutarLogicaDePuerta
return int(input("Introduzca la ()
entrada del Pin A para la Puerta return self.salida
"+self.obtenerNombre()+"--> "))
else: class PuertaBinaria(PuertaLogica):
return def __init__(self,n):
self.pinA.obtenerFuente().obtenerSalida() PuertaLogica.__init__(self,n)
self.pinA = None
self.pinB = None
 Esto, a su vez, hace que esa puerta
procese su lógica. Se continúa este def obtenerPinA(self):
if self.pinA == None:
proceso hasta que todas las entradas estén
return int(input("Introduzca la entra
disponibles y el valor de salida final se da del Pin A para la Puerta "+self.obtenerNombre(
)+"--> "))
convierta en la entrada requerida para la
else:
puerta en cuestión. return self.pinA.obtenerFuente().obtenerSalida()

#Class_Puertas
def obtenerPinB(self):
class PuertaLogica: if self.pinB == None:
def __init__(self,n): return int(input("Introduzca la entra
self.nombre = n da del Pin B para la Puerta "+self.obtenerNombre(
self.salida = None )+"--> "))
else:
def obtenerNombre(self): return self.pinB.obtenerFuente().obte
return self.nombre nerSalida()
Programación Orientada a Objetos
def asignarProximoPin(self,fuente): class PuertaOR(PuertaBinaria):
if self.pinA == None: def __init__(self,n):
self.pinA = fuente PuertaBinaria.__init__(self,n)
else:
if self.pinB == None: def ejecutarLogicaDePuerta(self):
self.pinB = fuente
a = self.obtenerPinA()
else:
b = self.obtenerPinB()
print("No se puede conectar: NO
HAY PINES DISPONIBLES en esta Puerta") if a ==1 or b==1:
return 1
class PuertaAND(PuertaBinaria): else:
def __init__(self,n): return 0
PuertaBinaria.__init__(self,n)
class PuertaUnaria(PuertaLogica):
def ejecutarLogicaDePuerta(self): def __init__(self,n):
a = self.obtenerPinA() PuertaLogica.__init__(self,n)
b = self.obtenerPinB() self.pin = None
if a==1 and b==1:
return 1 def obtenerPin(self):
else: if self.pin == None:
return 0 return int(input("Introduzca la entr
ada del Pin para la Puerta "+self.obtenerNombre(
)+"--> "))
else:
return self.pin.obtenerFuente().obte
nerSalida()
48
Programación Orientada a Objetos
def asignarProximoPin(self,fuente):
 El siguiente fragmento construye el circuito
if self.pin == None:
self.pin = fuente mostrado anteriormente:
else:
print("No se puede conectar: NO HA
Y PINES DISPONIBLES en esta Puerta")
class PuertaNOT(PuertaUnaria):
def __init__(self,n):
PuertaUnaria.__init__(self,n)

def ejecutarLogicaDePuerta(self):
if self.obtenerPin():
return 0
else:
#Tema1_18
return 1
class Conector: from Class_Puertas import *
def __init__(self, deComp, aComp): def main():
self.dePuerta = deComp c1 = PuertaAND("C1")
self.aPuerta = aComp c2 = PuertaAND("C2")
aComp.asignarProximoPin(self) c3 = PuertaOR("C3")
c4 = PuertaNOT("C4")
def obtenerFuente(self): c1 = Conector(c1, c3)
return self.dePuerta c2 = Conector(c2, c3)
c3 = Conector(c3, c4)
def obtenerDestino(self): print(c4.obtenerSalida())
return self.aPuerta main() 49
Programación Orientada a Objetos
La clase PuertasLógicas en C++  clases derivadas pueden acceder a él. La
 Veamos cómo vamos construyendo la clase palabra clave de acceso protected se utiliza
puertaslogicas en C++. Inicialmente la para esto
clase completa será  La clase BinaryGate será una subclase de
LogicGate y agregará dos líneas de
class LogicGate {
public: entrada. La clase UnaryGate también tendrá
LogicGate(string n) {
label = n; una subclase, LogicGate pero solo tendrá
} una línea de entrada
string getLabel() {
return label;
} class BinaryGate : public LogicGate
bool getOutput() { {
output = performGateLogic(); public:
return output; BinaryGate(string n) : LogicGate(n) {
} pinATaken = false;
protected: pinBTaken = false;}
string label; bool getPinA() {
bool output; if (pinATaken==false) {
}; cout << "Enter Pin input
for gate " << getLabel() << " : ";
cin >> pinA;
 Una variable o función protegida de pinATaken = true}
miembro es similar a un miembro privado, return pinA;}

pero tiene la ventaja adicional de que las 50


Programación Orientada a Objetos
bool getPinB() {  Ahora que tenemos una clase general para
if (pinBTaken==false ) {
cout << "Enter Pin i puertas en función del número de líneas de
nput for gate " << getLabel() << " : ";
cin >> pinB; entrada, podemos construir puertas
pinBTaken = true;} específicas que tengan un comportamiento
return pinB;}
protected: único. Por ejemplo, la clase AndGate será
bool pinA;
una subclase de BinaryGate
bool pinATaken;
bool pinB;
bool pinBTaken; class AndGate : public BinaryGate
}; {
public:
class UnaryGate : public LogicGate AndGate(string n) : BinaryGate(n) {};
{ virtual bool performGateLogic() {
public: bool a = getPinA();
UnaryGate(string n) : LogicGate(n) { bool b = getPinB();
pinTaken = false;} if (a == 1 && b == 1) {
bool getPin() { return true;
if (pinTaken==false) { }
cout << "Enter Pin i else {
nput for gate " << getLabel() << ": "; cin >> pi return false;
n; }
pinTaken = true;} }
return pin;} };
protected:
bool pin;
//Ver más en Class_Puertas.hpp…
bool pinTaken;
}; 51
Enderezo electrónico
[email protected]

universidade de santiago de compostela

También podría gustarte