11 de enero de 2011

Programación orientada a objetos

Como muchos saben Python es un lenguaje multiparadigma; Esto quiere decir que podemos realizar con el mismo lenguaje programación estructurada, programación orientada a objetos y programación funcional.
Hasta el momento en el blog solo he usado programación estructurada ya que es mas fácil para realizar programas sencillos como los que he hecho hasta el momento pero me parece oportuno comenzar a practicar la programación orientada a objetos ó OOP por sus siglas en ingles.

Que mejor para desempolvar mis conocimientos sobre el tema que leyendo esta conversación que encontré en un blog muy interesante.


En la programación orientada a objetos, todo se reduce, como su nombre lo indica, a los objetos y sus relaciones entre ellos.
Los objetos tal como en el mundo real pueden ser, por ejemplo, una lampara, un carro, un perro. En la conversación hacen el ejemplo con un radio.

Un radio tiene, entre otras cosas, algo que permite seleccionar la emisora o frecuencia y algo para modificar el volumen, estas son las propiedades del objeto las cuales básicamente lo definen. Podríamos incluir otras propiedades como el ancho y el alto del radio o el color, la marca, y muchas mas; Pero para este ejemplo esas son las propiedades que nos interesan.

las propiedades tienen un valor. La frecuencia podría ser 97.9, el volumen = 50, el ancho=20 y así con todas las propiedades.

Cuando nosotros cambiamos la frecuencia de un radio o subimos el volumen, normalmente no estamos interesados en lo que ocurre al interior del objeto. Solo nos interesa saber que si presionamos un botón alguna acción va a ocurrir.

Los botones de subir y bajar volumen, o el de modificar la emisora podemos llamarlos como los métodos del objeto. Los métodos son el algo que realiza una tarea sobre el objeto, como modificar sus propiedades. Por ejemplo, Un método del objeto radio podría ser "subir volumen" y lo que hace es modificar la propiedad "volumen".

Los objetos son definidos por medio de clases, Las clases vienen siendo como una plantilla de objetos. Es decir, que si yo tengo la clase radio con sus atributos y métodos  puedo crear las radios que quiera con las mismas características.

Vamos a crear nuestra clase radio

class Radio:
        def __init__(self):
                self.volumen = 10
                self.frecuencia = 90 

Con esas lineas creamos la clase llamada Radio.

__init__  es un método especial. Este se ejecuta cuando se crea un objeto nuevo. Quiere decir que cuando creemos un objeto desde la clase radio, lo primero que se ejecuta va a ser el método __init__ el cual, a su vez, crea unos atributos llamados volumen y frecuencia y les asigna un valor.

Ahora vamos a crear un objeto. Ponemos el siguiente código:

class Radio:
        def __init__(self):
                self.volumen = 10
                self.frecuencia = 90 

mi_radio = Radio()
print mi_radio.volumen

Lo que hicimos fue crear el objeto mi_radio e imprimir el atributo volumen de ese objeto. Como en la clase radio le asigne un valor por defecto a volumen, muy seguramente la salida sera:

>> 10

También podría modificar las propiedades de mi objeto. Por ejemplo:


class Radio:
        def __init__(self):
                self.volumen = 10
                self.frecuencia = 90 
mi_radio = Radio()
mi_radio.volumen=45
mi_radio.frecuencia=97.9
print mi_radio.volumen
print mi_radio.frecuencia

y mi salida sera:

>> 45
>> 97.9

En esta caso modificamos las propiedades del objeto pero no tiene sentido modificar el código para modificar un atributo de un objeto. Lo correcto seria crear nuestro primer método que haces las veces de "botón" de nuestro radio. Así que vamos a crear un método que suba el volumen el cual quedaría de la siguiente manera:

class Radio:
        def __init__(self):
                self.volumen = 10
                self.frecuencia = 90 
        def subir_volumen(self):
                self.volumen +=1
mi_radio = Radio()
print mi_radio.volumen
mi_radio.subir_volumen()
print mi_radio.volumen

Esto nos daría como salida:

>> 10
>> 11

Lo cual es justo lo que queríamos  En vez de modificar directamente las propiedades, creamos un método que lo hiciera por nosotros.

Si quisiera elegir el volumen y la frecuencia (atributos) al momento de crear mi objeto, mi código seria el siguiente.

class Radio:
        def __init__(self,vol,frec):
                self.volumen = vol
                self.frecuencia = frec
        def subir_volumen(self):
                self.volumen +=1
mi_radio = Radio(20,94.9)
print mi_radio.volumen
print mi_radio.frecuencia

Mi salida seria:

>> 20
>> 94.9

Es bastante obvio que en el método __init__ le digo que al momento de crear un objeto Radio le tengo que pasar 2 argumentos tal como hice. Pero si al momento de crear el objeto no le pongo atributos daría un error, por lo que es conveniente ponerle unos valores por defecto a los atributos de la siguiente manera:

class Radio:
        def __init__(self,vol=10,frec=97.9):
                self.volumen = vol
                self.frecuencia = frec
        def subir_volumen(self):
                self.volumen +=1
mi_radio = Radio()
print mi_radio.volumen
print mi_radio.frecuencia

como no asigne nada a mi_radio = Radio() la salida sera

>> 10
>> 97.9

porque las asigne por defecto en la clase. También podría solo haber pasado un argumento así:

mi_radio = Radio(50)

y mi salida seria

>> 50
>> 97.9

Si quisiera solo asignar la frecuencia y dejar el volumen por defecto tendría que especificar el nombre del atributo

mi_radio = Radio(frec=104.9)

y la salida seria:

>> 10
>> 104.9

Ahora como ejercicio voy a modificar la clase Radio de tal manera que el método subir_volumen me permita escoger cuanto quiero subir y si no le digo cuanto, me suba 1.

class Radio:
        def __init__(self,vol=10,frec=97.9):
              self.volumen = vol
              self.frecuencia = frec
        def subir_volumen(self,aumento=1):
                self.volumen +=aumento
mi_radio = Radio()
mi_radio.subir_volumen(10)
print mi_radio.volumen

salida:
>> 20


Ahora vamos a observar otro método especial llamado __str__.
Si hacemos la prueba de imprimir un objeto por medio del siguiente código:

class Radio:
        def __init__(self,vol=10,frec=97.9):
              self.volumen = vol
              self.frecuencia = frec
        def subir_volumen(self,aumento=1):
                self.volumen +=aumento
mi_radio = Radio()
print mi_radio

nos va a salir esto:

>> <__main__.Radio instance at 0x011F4940>

Esto nos dice que "mi_radio" es una instanciación de la clase Radio.
Mediante el método __str__ podemos indicar que queremos mostrar en el momento que se le haga un print al objeto mi_radio o a cualquier instancia de la clase Radio.

Voy a poner el mismo ejemplo de la conversación:

class Radio:
        def __init__(self,vol=10,frec=97.9):
                self.volumen = vol
                self.frecuencia = frec
        def subir_volumen(self,aumento=1):
                self.volumen +=aumento
        def __str__(self):
                return "Volumen: " + str(self.volumen) + "\nFrecuencia: " + str(self.frecuencia)
mi_radio = Radio()
print mi_radio

Mi salida sera

>>Volumen: 10
>>Frecuencia: 97.9

____________________________________________________________

Vamos a practicar un poco. Voy a realizar una clase que me permita crear un punto con sus correspondientes coordenadas X y Y, si no le paso argumentos al momento de crear el objeto que me de un punto en (0,0). Ademas crear un método __str__ que me muestre las coordenadas del punto.

Codigo:

class punto:
    def __init__(self,x=0,y=0):
        self.x=x
        self.y=y
        
    def __str__(self):
        return '('+str(self.x)+','+str(self.y)+')'
    
a=punto(4,3)
print a

Ahora voy le voy a agregar un método que me sume otro punto a mi punto existente.

Codigo:

class punto:
    def __init__(self,x=0,y=0):
        self.x=x
        self.y=y
        
    def __str__(self):
        return '('+str(self.x)+','+str(self.y)+')'

    
    def sumarpuntos(self,b): 
        return punto(self.x+b.x,self.y+b.y)
        
    
a=punto(4,3)
b=punto(5,7)
c=a.sumarpuntos(b)

print c

Podría realizarlo de otra manera usando un método especial llamado __add__

Codigo:

class Punto:
    def __init__(self,x=0,y=0):
        self.x=x
        self.y=y
        
    def __str__(self):
        return '('+str(self.x)+','+str(self.y)+')'

    
    def __add__(self,b): 
        return Punto(self.x+b.x,self.y+b.y)
        
a=Punto(4,3)
b=Punto(5,7)
c=a+b
print c

Podría extender el código con una resta usando el método especial __sub__ de la misma manera que uso el __add__

El siguiente y ultimo ejemplo es crear un método que calcule la distancia entre dos puntos

Codigo:

import math
class Punto:
    def __init__(self,x=0,y=0):
        self.x=x
        self.y=y
        
    def __str__(self):
        return '('+str(self.x)+','+str(self.y)+')'

    
    def __add__(self,b): 
        return Punto(self.x+b.x,self.y+b.y)
    
    def distancia(self,b):
        c=(b.x-self.x,b.y-self.y)
        c=(c[0]**2)+(c[1]**2)
        c=math.sqrt(c)
        return c
    
a=Punto(4,8)
b=Punto(5,7)
c=a.distancia(b)


No hay comentarios:

Publicar un comentario