Sie sind auf Seite 1von 31

PROGRAMACIÓN ORIENTADA A OBJETOS

Introducción

En esta unidad aprenderemos sobre un paradigma que cambió la forma de


entender la programación, introduciendo conceptos tan importantes como las
clases, los objetos y la herencia.

Con todo lo que conocemos hasta ahora ya podemos crear bastantes buenos
programas, sin embargo, no dejan de ser instrucciones estructuradas, eso significa
que cuando queremos solucionar un problema tenemos que pensar de arriba
abajo y lo único que nos da un poco de juego son las funciones, las listas y los
diccionarios. Así era la programación en el pasado bastante aburrida, con
muchísimo código y mucha gestión de recursos y difícil de mantener, hasta que
poco a poco fue tomando fama un paradigma de programación conocido como
programación orientada a objetos, este paradigma o modelos de solución de
problemas resulto muy útil para satisfacer las necesidades de un mundo cada vez
más tecnificado, en el que más y más sectores se estaban informatizando y era
necesaria una forma más fácil y sencilla de trasladar los problemas del mundo real
a la programación, la mejora fue muy importante, ya que las tediosas estructuras
no solo se podían replicar fácilmente en clase y objetos mucho mejor ordenados,
si no que estos además permiten manejar sus propios atributos y funciones
internas llamadas métodos. En esta tercera fase del curso vamos a meternos de
lleno en el mundo de los objetos, definiendo y manejando nuestras propias clases,
descubriendo la herencia y repasando algunos de los métodos nativos de las
colecciones, haciéndolas cada vez aún más potentes. Bienvenidos a la
programación orientada a objetos.
Primer contacto con la POO

Para entender en qué se basa este paradigma (modelo de solución de problemas),


en lugar de empezar con teoría os propongo jugar un poco con dos códigos
distintos. Ambos implementan una estructura de clientes y funciones para trabajar
con ellos, pero uno está creado con programación estructurada (la clásica cuyas
bases hemos estudiado hasta ahora) y la otra con programación orientada a
objetos.

Lo único que tenéis que hacer es probar ambos códigos (sin necesidad de
analizarlos), luego sacad vuestras propias conclusiones sobre cuál os parece más
útil e intuitivo de aplicar y extender en el mundo real.
Ejemplo estructurado
# Definimos unos cuantos clientes
clientes= [
{'Nombre': 'Hector', 'Apellidos’: ‘Costa Guzman',
'dni':'11111111A'},
{'Nombre': 'Juan', 'Apellidos’: ‘González Márquez',
'dni':'22222222B'}
]

# Creamos una función que muestra un cliente en una lista a


partir del DNI
def mostrar_cliente(clientes, dni):
for c in clientes:
if (dni == c['dni']):
print('{} {}'.format(c['Nombre'],c['Apellidos']))
return
print('Cliente no encontrado')

# Creamos una función que borra un cliente en una lista a


partir del DNI
def borrar_cliente(clientes, dni):
for i,c in enumerate(clientes):
if (dni == c['dni']):
del( clientes[i] )
print(str(c),"> BORRADO")
return

print('Cliente no encontrado')

### Fíjate muy bien cómo se utiliza el código estructurado

print("==LISTADO DE CLIENTES==")
print(clientes)

print("\n==MOSTRAR CLIENTES POR DNI==")


mostrar_cliente(clientes, '11111111A')
mostrar_cliente(clientes, '11111111Z')

print("\n==BORRAR CLIENTES POR DNI==")


borrar_cliente(clientes, '22222222V')
borrar_cliente(clientes, '22222222B')

print("\n==LISTADO DE CLIENTES==")
print(clientes)
Ejemplo orientado a objetos
### No intentes entender este código, sólo fíjate en cómo se
utiliza abajo

# Creo una estructura para los clientes


class Cliente:

def __init__(self, dni, nombre, apellidos):


self.dni = dni
self.nombre = nombre
self.apellidos = apellidos

def __str__(self):
return '{} {}'.format(self.nombre,self.apellidos)

# Y otra para las empresas


class Empresa:

def __init__(self, clientes=[]):


self.clientes = clientes

def mostrar_cliente(self, dni=None):


for c in self.clientes:
if c.dni == dni:
print(c)
return
print("Cliente no encontrado")

def borrar_cliente(self, dni=None):


for i,c in enumerate(self.clientes):
if c.dni == dni:
del(self.clientes[i])
print(str(c),"> BORRADO")
return
print("Cliente no encontrado")

### Ahora utilizaré ambas estructuras

# Creo un par de clientes


hector = Cliente(nombre="Hector", apellidos="Costa Guzman",
dni="11111111A")
juan = Cliente("22222222B", "Juan", "Gonzalez Marquez")
# Creo una empresa con los clientes iniciales
empresa = Empresa(clientes=[hector, juan])

# Muestro todos los clientes


print("==LISTADO DE CLIENTES==")
print(empresa.clientes)

print("\n==MOSTRAR CLIENTES POR DNI==")


# Consulto clientes por DNI
empresa.mostrar_cliente("11111111A")
empresa.mostrar_cliente("11111111Z")

print("\n==BORRAR CLIENTES POR DNI==")


# Borro un cliente por DNI
empresa.borrar_cliente("22222222V")
empresa.borrar_cliente("22222222B")

# Muestro de nuevo todos los clientes


print("\n==LISTADO DE CLIENTES==")
print(empresa.clientes)

¿No os parece que el código orientado a objetos es más auto explicativo a la hora
de utilizarlo? Además, con programación estructurada tenemos que enviar la lista
que queremos consultar todo el rato, mientras que con la POO tenemos esas
"estructuras" como la empresa que contienen los clientes, todo queda como más
ordenado.
Clases y objetos

La base de la POO son los objetos.

Podéis imaginaros los objetos como un nuevo tipo de dato cuya definición viene
dada en una estructura llamada clase.

Suelo hacer una metáfora y comparar las clases con moldes de galletas y los
objetos con las galletas en sí mismas. Si bien todas las galletas que se hacen con
el mismo molde tienen la misma forma, cada una adquiere atributos individuales
después del horneado. Cosas como el color, la textura, el sabor... pueden llegar a
ser muy distintas.

En otras palabras, las galletas comparten un proceso de fabricación y unos


atributos, pero son independientes entre ellas y del propio molde y eso hace que
cada una sea única.

Extrapolando el ejemplo, una clase es sólo un guión sobre como deben ser los
objetos que se crearán con ella.

La función type()

Ya he comentado varias veces que en Python todo son clases y objetos, eso se
puede comprobar fácilmente pasando a la función type() cualquier variable o
literal:

numero = 10
type(numero)

En el código anterior número es una variable entera, pero si vamos más allá, en


realidad es una instancia del tipo int, una clase muy básica de dato para
almacenar números enteros. Como curiosidad, incluso las funciones en Python
son instancias del tipo function:

def hola():
pass
type(hola)
Con eso en mente veamos cómo crear nuestras propias clases.

Definición de clase

La sintaxis es muy sencilla:


class Galleta:
pass

Esta es una definición muy simple de lo que es una galleta, ya que con el  pass la
dejo vacía. Luego añadiremos más información, por ahora veamos cómo crear
galletas con este molde.

Instancias de clase

Para entender bien los objetos debemos tener claras dos cuestiones
fundamentales:

¿Cuándo y dónde existen los objetos?

Puede parecer trivial, pero es importante tener claro que los objetos "existen" sólo
durante la ejecución del programa y se almacenan en la memoria del sistema
operativo.

Es decir, mientras las clases están ahí en el código haciendo su papel de


instrucciones, los objetos no existen hasta que el programa se ejecuta y se crean
en la memoria.

Este proceso de "crear" los objetos en la memoria se denomina instanciación y


para realizarlo es tan fácil como llamar a la clase como si fuera una función:

una_galleta = Galleta()
otra_galleta = Galleta()
Demostrar que las galletas existen como "entes independientes" dentro de la
memoria, es tan sencillo como imprimirlas por pantalla:

print(una_galleta)
print(otra_galleta)

Cada instancia tiene su propia referencia, demostrando que están en lugares


distintos de la memoria. En cambio, la clase no tiene una referencia porque es
sólo un guion de instrucciones:

print(Galleta)

Es posible consultar la clase de un objeto con la función type(), pero también se


puede consultar a través de su atributo especial class:

print(Galleta)
print(type(una_galleta))
print(una_galleta.__class__)

A su vez las clases tienen un atributo especial name que nos devuelve su nombre


en forma de cadena sin adornos:

print(Galleta.__name__)
print(type(una_galleta).__name__)
print(una_galleta.__class__.__name__)

Resumiendo: los objetos son instancias de una clase.


Atributos y métodos

Si hay algo que ilustre el potencial de la POO esa es la capacidad de definir


variables y funciones dentro de las clases, aunque aquí se conocen como
atributos y métodos respectivamente.

Atributos

A efectos prácticos los atributos no son muy distintos de las variables, la diferencia
fundamental es que sólo existen dentro del objeto.

Atributos dinámicos

Dado que Python es muy flexible los atributos pueden manejarse de distintas
formas, por ejemplo, se pueden crear dinámicamente (al vuelo) en los objetos.

class Galleta:
pass

galleta = Galleta()
galleta.sabor = "salado"
galleta.color = "marrón"

print(f"El sabor de esta galleta es {galleta.sabor} "


f"y el color {galleta.color}")

Atributos de clase

Aunque la flexibilidad de los atributos dinámicos puede llegar a ser muy útil, tener
que definir los atributos de esa forma es tedioso. Es más práctico definir unos
atributos básicos en la clase. De esa manera todas las galletas podrían tener unos
atributos por defecto:
class Galleta:
chocolate = False

galleta = Galleta()

if galleta.chocolate:
print("La galleta tiene chocolate")
else:
print("La galleta no tiene chocolate")

Luego podemos cambiar su valor en cualquier momento:

galleta.chocolate = True

if galleta.chocolate:
print("La galleta tiene chocolate")
else:
print("La galleta no tiene chocolate")

Por lo menos de esta forma nos aseguraremos de que el atributo chocolate existe
en todas las galletas desde el principio. Además, es posible consultar el valor por
defecto que deben tener las galletas haciendo referencia al atributo en la definición
de la clase:

print(Galleta.chocolate)

Lo curioso es que si cambiamos ese atributo de clase (que no de objeto) a True,


las siguientes galletas se crearán con chocolate, es decir, habremos modificado
las instrucciones de creación de los objetos:
class Galleta:
chocolate = False

Galleta.chocolate = True

galleta = Galleta()

if galleta.chocolate:
print("La galleta tiene chocolate")
else:
print("La galleta no tiene chocolate")

Ya les gustaría a otros lenguajes ser tan flexibles. 😁😁😁

Métodos

Si por un lado tenemos las "variables" de las clases, por otro tenemos sus
"funciones", que evidentemente nos permiten definir funcionalidades para
llamarlas desde las instancias.

Definir un método es bastante simple, sólo tenemos que añadirlo en la clase y


luego llamarlo desde el objeto con los paréntesis, como si de una función se
tratase:

class Galleta:
chocolate = False

def saludar():
print("Hola, soy una galleta muy sabrosa")

galleta = Galleta()
galleta.saludar()
Sin embargo, al intentar ejecutar el código anterior desde una galleta veréis que
no funciona. Nos indica que el método saludar() requiere 0 argumentos pero se
está pasando uno.

¿Cómo puede ser? Si en ningún momento hemos enviado ninguna información a


a la galleta...

Lo que tenemos aquí, estimados alumnos, es la diferencia fundamental entre


métodos de clase y métodos de instancia.

Probad a ejecutar el método llamando a la clase en lugar del objeto:

class Galleta:
chocolate = False

def saludar():
print("Hola, soy una galleta muy sabrosa")

Galleta.saludar()

¡Ahora sí ha funcionado! ¿Cómo es posible? Y más importante, ¿por qué al


llamarlo desde el objeto dice que estamos enviando un argumento?

Primer argumento self

Los objetos tienen una característica muy importante: son conscientes de que
existen. Y no, no es broma.

Cuando se ejecuta un método desde un objeto (que no desde una clase), se envía
un primer argumento implícito que hace referencia al propio objeto. Si lo definimos
en nuestro método podremos capturarlo y ver qué es:
class Galleta:
chocolate = False

def saludar(soy_el_propio_objeto):
print("Hola, soy una galleta muy sabrosa")
print(soy_el_propio_objeto)

galleta = Galleta()
galleta.saludar()

¿Curioso que haya funcionado verdad? Además ¿no os suena de algo ese
resultado que muestra el parámetro que hemos definido? Se trata de la propia
representación del objeto.

class Galleta:
chocolate = False

def saludar(soy_el_propio_objeto):
print("Hola, soy una galleta muy sabrosa")
print(soy_el_propio_objeto)

galleta = Galleta()
galleta.saludar()
print(galleta)

Pues sí, podemos acceder al propio objeto desde el interior de sus métodos. Lo
único que como este argumento hace referencia al objeto en sí mismo por
convención se le llama self.
Poder acceder al propio objeto desde un método es muy útil, ya que nos permite
acceder a sus atributos. Fijaros, el siguiente código no funcionaría como
esperamos:

class Galleta:
chocolate = False

def chocolatear(self):
chocolate = True

galleta = Galleta()
galleta.chocolatear()
print(galleta.chocolate)

En cambio, si hacemos ver que self es el propio objeto...

class Galleta:
chocolate = False

def chocolatear(self):
self.chocolate = True

galleta = Galleta()
galleta.chocolatear()
print(galleta.chocolate)

¿No es interesante? Da la sensación como os decía antes de que las instancias


tienen que saber quiénes son porque si no no pueden acceder sus atributos
internos y por eso tienen que enviarse a sí mismas a los métodos.
Sea como sea con este ejemplo podemos entender que por defecto el valor de un
atributo se busca en la clase, pero para modificarlo en la instancia es necesario
hacer referencia al objeto.

Métodos especiales

Ahora que sabemos crear métodos y hemos aprendido para qué sirve el
argumento self, es momento de introducir algunos métodos especiales de las
clases.

Se llaman especiales porque la mayoría ya existen de forma oculta y sirven para


tareas específicas.

Constructor

El constructor es un método que se llama automáticamente al crear un objeto, se


define con el nombre init:

class Galleta:

def __init__(self):
print("Soy una galleta acabada de hornear!")

galleta = Galleta()

La finalidad del constructor es, como su nombre indica, construir los objetos. Por
esa razón permite sobreescribir el método que crea los objetos, permitiéndonos
enviar datos desde el principio para construirlo:

class Galleta:
chocolate = False

def __init__(self, sabor, color):


self.sabor = sabor
self.color = color
print(f"Se acaba de crear una galleta {self.color} y
{self.sabor}.")

galleta_1 = Galleta("marrón", "amarga")


galleta_2 = Galleta("blanca", "dulce")

Como los métodos se comportan como funciones tienen sus mismas


características, permitiéndonos definir valores nulos, valores por posición y
nombre, argumentos indeterminadas, etc.

Destructor

Si existe un constructor también debe existir un destructor que se


llame al eliminar el objeto para que encargue de las tareas de
limpieza como vaciar la memoria. Ese es el papel del método
especial del. Es muy raro sobreescribir este método porque se
maneja automáticamente, pero es interesante saber que existe.

Todos los objetos se borran automáticamente de la memoria al


finalizar el programa, aunque también podemos eliminarlos
automáticamente pasándolos a la función del():

class Galleta:
def __del__(self):
print("La galleta se está borrando de la memoria")

galleta = Galleta()

del(galleta)

En este punto vale comentar algo respecto a los métodos


especiales como éste, y es que, pese a que tienen accesores en
forma de función para facilitar su llamada, es totalmente posible
ejecutarlos directamente como si fueran métodos normales:
class Galleta:

def __del__(self):
print("La galleta se está borrando de la memoria")

galleta = Galleta()

galleta.__del__()

Si tenéis memoria seguro que ahora mismo os estáis acordando de


funciones como str() y len(), y es que en efecto, esas también son
accesores de los métodos especiales str y len que tienen los
objetos.

String

El método str es el que devuelve la representación de un objeto en


forma de cadena. Un momento en que se llama automáticamente
es cuando imprimirmos una variable por pantalla.

Por defecto los objetos imprimen su clase y una dirección de


memoria, pero eso puede cambiarse sobreescribiendo el
comportamiento:

class Galleta:

def __init__(self, sabor, color):


self.sabor = sabor
self.color = color

def __str__(self):
return f"Soy una galleta {self.color} y {self.sabor}."
galleta = Galleta("dulce", "blanca")

print(galleta)
print(str(galleta))
print(galleta.__str__())

Hay que tener en cuenta que este método debe devolver la cadena
en lugar de mostrar algo por pantalla, ese es el funcionamiento
que se espera de él.

Length

Finalmente, otro método especial interesante es el que devuelve


la longitud. Normalmente está ligado a colecciones, pero nada
impide definirlo en una clase. Y sí, digo definirlo y no redefinirlo
porque por defecto no existe en los objetos aunque sea el que se
ejecuta al pasarlos a la función len().

class Cancion:

def __init__(self, autor, titulo, duracion): # en


segundos
self.duracion = duracion

def __len__(self):
return self.duracion

cancion = Cancion("Queen", "Don't Stop Me Now", 210)


print(len(cancion))
print(cancion.__len__())

Mientras devolvamos un número, este método no debería dar


problemas.

Objetos dentro de objetos

Hasta ahora no lo hemos comentado, pero al ser las clases un nuevo tipo de dato
resulta más que obvio que se pueden poner en colecciones e incluso utilizarlos
dentro de otras clases.

Os voy a dejar un pequeño código de ejemplo sobre un catálogo de películas para


que lo estudiéis detenidamente:

class Pelicula:

# Constructor de clase
def __init__(self, titulo, duracion, lanzamiento):
self.titulo = titulo
self.duracion = duracion
self.lanzamiento = lanzamiento
print('Se ha creado la película:', self.titulo)

def __str__(self):
return '{} ({})'.format(self.titulo,
self.lanzamiento)

class Catalogo:

peliculas = [] # Esta lista contendrá objetos de la


clase Pelicula

def __init__(self, peliculas=[]):


self.peliculas = peliculas

def agregar(self, p): # p será un objeto Pelicula


self.peliculas.append(p)

def mostrar(self):
for p in self.peliculas:
print(p) # Print toma por defecto str(p)

p = Pelicula("El Padrino", 175, 1972)


c = Catalogo([p]) # Añado una lista con una película desde
el principio
c.mostrar()
c.agregar(Pelicula("El Padrino: Parte 2", 202, 1974)) #
Añadimos otra
c.mostrar()
Encapsulación

Finalmente, para acabar la introducción vale la pena comentar esta "técnica". No


es santo de mi devoción porque no le veo mucho sentido, pero si venís de otros
lenguajes quizá os interesa conocerla.

La encapsulación consiste en denegar el acceso a los atributos y métodos internos


de la clase desde el exterior. En Python no existe, pero se puede simular
precediendo atributos y métodos con dos barras bajas __ como indicando que son
"especiales".

En el caso de los atributos quedarían así:

class Ejemplo:
__atributo_privado = "Soy un atributo inalcanzable desde
fuera."

e = Ejemplo()
print(e.__atributo_privado)

Y en los métodos...

class Ejemplo:
def __metodo_privado(self):
print("Soy un método inalcanzable desde fuera.")

e = Ejemplo()
e.__metodo_privado()

¿Qué sentido tiene esto en Python? Ninguno, porque se pierde toda la gracia de lo
que en esencia es el lenguaje: flexibilidad y polimorfismo sin control (hablaremos
de esto más adelante).

Sea como sea para acceder a esos datos se deberían crear métodos públicos que
hagan de interfaz. En otros lenguajes les llamaríamos getters y setters y es lo que
da lugar a las propiedades, que no son más que atributos protegidos con
interfaces de acceso:

class Ejemplo:
__atributo_privado = "Soy un atributo inalcanzable desde
fuera."

def __metodo_privado(self):
print("Soy un método inalcanzable desde fuera.")

def atributo_publico(self):
return self.__atributo_privado

def metodo_publico(self):
return self.__metodo_privado()

e = Ejemplo()
print(e.atributo_publico())
e.metodo_publico()

Ejercicios «Programación Orientada a Objetos

Teoría previa

En este ejercicio vas a trabajar el concepto de puntos, coordenadas y vectores


sobre el plano cartesiano y cómo la programación Orientada a Objetos puede ser
una excelente aliada para trabajar con ellos. No está pensado para que hagas
ningún tipo de cálculo sino para que practiques la automatización de tareas.

Nota

Creo que es un ejemplo muy interesante, punto de partida en la programación de gráficos,


pero si consideras que esto no lo tuyo puedes simplemente pasar de largo. Ahora bien,
debes ser consciente de que te vas a perder uno de los ejercicios más interesantes del
curso.
Voy a explicar brevemente los conceptos básicos por si alguien necesita un
repaso.

El plano cartesiano

Representa un espacio bidimensional (en 2 dimensiones), formado por dos rectas


perpendiculares, una horizontal y otra vertical que se cortan en un punto. La recta
horizontal se denomina eje de las abscisas o eje X, mientras que la vertical recibe
el nombre de eje de las ordenadas o simplemente eje Y. En cuanto al punto donde
se cortan, se conoce como el punto de origen O.

Es importante remarcar que el plano se divide en 4 cuadrantes:


Puntos y coordenadas

El objetivo de todo esto es describir la posición de puntos sobre el plano en forma


de coordenadas, que se forman asociando el valor del eje de las X (horizontal)
con el 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.

Vectores en el plano

Finalmente, un vector en el plano hace referencia a un segmento orientado,


generado a partir de dos puntos distintos.

A efectos prácticos no deja de ser una


línea formada desde un punto inicial en
dirección a otro punto final, por lo que
se entiende que un vector tiene
longitud y dirección/sentido.
En esta figura, podemos observar dos puntos A y B que podríamos definir de la
siguiente forma:

 A(x1, y1) => A(2, 3)

 B(x2, y2) => B(5, 5)

Y el vector se representaría como la diferencia entre las coordendas del segundo


punto respecto al primero (el segundo menos el primero):

 AB = (x2-x1, y2-y1) => (5-2, 5-3) => (3,2)

Lo que en definitiva no deja de ser: 3 a la derecha y 2 arriba.

Y con esto finalizamos este mini repaso.

Ejercicio

 Crea una clase llamada Punto con sus dos coordenadas X e Y.

 Añade un método constructor para crear puntos fácilmente. Si no se


reciben una coordenada, su valor será cero.

 Sobreescribe el método string, para que al imprimir por pantalla un punto


aparezca en formato (X,Y)

 Añade un método llamado cuadrante que indique a qué cuadrante


pertenece el punto, teniendo en cuenta que si X == 0 e Y != 0 se sitúa sobre el
eje Y, si X != 0 e Y == 0 se sitúa sobre el eje X y si X == 0 e Y == 0 está sobre
el origen.

 Añade un método llamado vector, que tome otro punto y calcule el vector


resultante entre los dos puntos.

 (Optativo) Añade un método llamado distancia, que tome otro punto y


calcule la distancia entre los dos puntos y la muestre por pantalla. La fórmula
es la siguiente:

Nota:

La función raíz cuadrada en Python sqrt() se debe importar del módulo math y utilizarla de la
siguiente forma:

import math
math.sqrt(9)

 Crea una clase llamada Rectangulo con dos puntos (inicial y final) que


formarán la diagonal del rectángulo.

 Añade un método constructor para crear ambos puntos fácilmente, si no se


envían se crearán dos puntos en el origen por defecto.

 Añade al rectángulo un método llamado base que muestre la base.

 Añade al rectángulo un método llamado altura que muestre la altura.

 Añade al rectángulo un método llamado área que muestre el área.


Sugerencia

Puedes identificar fácilmente estos valores si intentas dibujar el cuadrado a partir de su diagonal. Si
andas perdido, prueba de dibujarlo en un papel, ¡seguro que lo verás mucho más claro! Además
recuerda que puedes utilizar la función abs() para saber el valor absolute de un número.

Experimentación

 Crea los puntos A(2, 3), B(5,5), C(-3, -1) y D(0,0) e imprimelos por pantalla.

 Consulta a que cuadrante pertenecen el punto A, C y D.

 Consulta los vectores AB y BA.

 (Optativo) Consulta la distancia entre los puntos 'A y B' y 'B y A'.

 (Optativo) Determina cual de los 3 puntos A, B o C, se encuentra más lejos


del origen, punto (0,0).

 Crea un rectángulo utilizando los puntos A y B.

 Consulta la base, altura y área del rectángulo.

import math

class Punto:

def __init__(self, x=0, y=0):


self.x = x
self.y = y

def __str__(self):
return "({}, {})".format(self.x, self.y)

def cuadrante(self):
if self.x > 0 and self.y > 0:
print("{} pertenece al primer
cuadrante".format(self))
elif self.x < 0 and self.y > 0:
print("{} pertenece al segundo
cuadrante".format(self))
elif self.x < 0 and self.y < 0:
print("{} pertenece al tercer
cuadrante".format(self))
elif self.x > 0 and self.y < 0:
print("{} pertenece al cuarto
cuadrante".format(self))
elif self.x != 0 and self.y == 0:
print("{} se sitúa sobre el eje X".format(self))
elif self.x == 0 and self.y != 0:
print("{} se sitúa sobre el eje Y".format(self))
else:
print("{} se encuentra sobre el
origen".format(self))

def vector(self, p):


print("El vector entre {} y {} es ({}, {})".format(
self, p, p.x - self.x, p.y - self.y) )

def distancia(self, p):


d = math.sqrt( (p.x - self.x)**2 + (p.y - self.y)**2
)
print("La distancia entre los puntos {} y {} es
{}".format(
self, p, d))

class Rectangulo:

def __init__(self, pInicial=Punto(), pFinal=Punto()):


self.pInicial = pInicial
self.pFinal = pFinal

# Hago los cálculos, pero no llamo los atributos


igual
# que los métodos porque sino podríamos
sobreescribirlos
self.vBase = abs(self.pFinal.x - self.pInicial.x)
self.vAltura = abs(self.pFinal.y - self.pInicial.y)
self.vArea = self.vBase * self.vAltura

def base(self):
print("La base del rectángulo es {}".format(
self.vBase ) )

def altura(self):
print("La altura del rectángulo es {}".format(
self.vAltura ) )

def area(self):
print("El área del rectángulo es {}".format(
self.vArea ) )

A = Punto(2,3)
B = Punto(5,5)
C = Punto(-3, -1)
D = Punto(0,0)

A.cuadrante()
C.cuadrante()
D.cuadrante()

A.vector(B)
B.vector(A)

A.distancia(B)
B.distancia(A)

A.distancia(D)
B.distancia(D)
C.distancia(D)

R = Rectangulo(A, B)
R.base()
R.altura()
R.area()

Das könnte Ihnen auch gefallen