====== Python: Programación Orientada a Objetos ======
Python permite el usos de clases para organizar mejor el código y modelar objetos del mundo real mediante código.
En Python todo es un objeto. El tipo más básico de objeto en Python es ''object''. Las clases que creemos serán subclases de ''object'', por lo que ''object'' será la superclase de todo el resto de clases.
Una clase en Python representa objetos del mundo real mediante la unión de sus características con las acciones/funciones que pueden realizar esos objetos. Por ejemplo, un pájaro tiene características como pico, alas y patas. También realiza ciertas funciones como volcar o hacer sonidos.
En POO, las características/propiedades de un objeto se llama **atributos** y sus funciones/acciones **métodos**.
En Python podríamos hacer:
class pajaro:
# Características, llamados métodos en POO
alas = 2
patas = 2
pico = 1
# Funciones, llamados métodos en POO
def comer(self):
print("Estoy comiendo")
def hacer_sonidos(self):
print("Estoy piando")
def volar(self):
print("Estoy volando")
Para nombrar una clase se sigue la siguiente nomenclatura: ''class nombreClase''. Es decir, comenzamos con una letra minúscula y separamos cada palabra por una mayúscula. La palabra reservada ''class'' indica que estamos definiendo una clase.
Una vez que se crea una clase, podemos usarla sus atributos y funciones tantas veces como queramos mediante la creación de nuevos **objetos** de esa clase. Un objeto es una **instancia** de una clase. Para el ejemplo de los pájaros, podemos tener diferentes tipos de pájaro, todos tendrán comparten el mismo número de patas, alas y picos; pueden comer, piar y volar. Estas especies pueden ser objetos de la clase "pájaro". Podríamos tener un objeto llamado "pájaro azul" y otro "gorrión". Ambos objetos son instancias de la clase "pájaro". ¿Cómo hacemos esto en Python?
pajaro_azul = pajaro()
gorrion = pajaro()
La sintaxis para definir un objeto en Python:
nombreObjeto = nombreClase()
Si queremos saber cuántas patas tiene el pájaro azul:
print(pajaro_azul.patas)
Si queremos que vuele, es decir, que use el método correspondiente:
gorrion.volar()
Si queremos objener información sobre los atributos de un objeto, creamos un método de clase para ello:
class pajaro:
alas = 2
patas = 2
pico = 1
def comer(self):
print("Estoy comiendo")
def hacer_sonidos(self):
print("Estoy piando")
def volar(self):
print("Estoy volando")
def hablar(self):
print("Hola, soy un pájaro. Tengo %s alas, %s patas y %s pico" % (self.alas, self.patas, self.pico))
Debemos usar ''self'' para que el método correspondiente sepa a qué objeto pertenene el atributo que queremos. El parámetro ''self'' es como un //placeholder// temporal, almacena el propio objeto. Cuando un método del objeto es llamado, el propio objeto es pasado como argumento al método y mostrado en una variable llamada ''self'', por eso siempre tenemos que usar ''self'' como primero argumento de los métodos de clase en Python. Esto también permite al método poder acceder a otros métodos y atributos del objeto.
La función ''init()'' es un método de clase que inicializa ciertas variables o tareas que se harán tan pronto como se cree un objeto:
class pajaro:
alas = 2
patas = 2
pico = 1
def __init()__(self, nombre, peso, color):
self.especie = nombre
self.peso = peso
self.color = color
def comer(self):
print("Estoy comiendo")
def hacer_sonidos(self):
print("Estoy piando")
def volar(self):
print("Estoy volando")
def hablar(self):
print("Hola, soy un %s. Tengo %s alas, %s patas y %s pico" % (self.specie, self.alas, self.patas, self.pico))
print("Peso %s gramos y soy de color %s " % (self.peso, self.color))
A la hora de crear el objeto:
gorrion = pajaro('gorrion', ''20'', 'marrón')
Podemos añadir otras cosas en el momento de creación del objeto. Por ejemplo:
# (...)
def __init()__(self, nombre):
self.especie = nombre
self.peso = peso
self.color = color
print("Creando el objeto %s", % self.especie)
De esa manera, cada vez que creemos un objeto, se imprimirá también ese mensaje.
Si no fuese por las clases, tendríamos que crear diferentes variables y funciones por cada tipo de pájaro que queramos. Las clases y los objetos reducen así la repetición de código.
===== Todo es un objeto =====
En Python todo es un objeto. Incluso cuando definimos una cadena de texto:
s = "hola"
Estamos creando un objeto de la clase //String// (ya incluida en Python). Podemos comprobarlo:
print(type(s))
#
Como objeto, tiene sus atributos y métodos. Con la función ''dir'' podemos ver los métodos disponibles:
dir(s)
# ['__add__', '__class__', '__contains__', '__delattr__', '__dir__', '__doc__', '__eq__', '__format__', '__ge__', '__getattribute__', '__getitem__', '__getnewargs__', '__gt__', '__hash__', '__init__', '__init_subclass__', '__iter__', '__le__', '__len__', '__lt__', '__mod__', '__mul__', '__ne__', '__new__', '__reduce__', '__reduce_ex__', '__repr__', '__rmod__', '__rmul__', '__setattr__', '__sizeof__', '__str__', '__subclasshook__', 'capitalize', 'casefold', 'center', 'count', 'encode', 'endswith', 'expandtabs', 'find', 'format', 'format_map', 'index', 'isalnum', 'isalpha', 'isdecimal', 'isdigit', 'isidentifier', 'islower', 'isnumeric', 'isprintable', 'isspace', 'istitle', 'isupper', 'join', 'ljust', 'lower', 'lstrip', 'maketrans', 'partition', 'replace', 'rfind', 'rindex', 'rjust', 'rpartition', 'rsplit', 'rstrip', 'split', 'splitlines', 'startswith', 'strip', 'swapcase', 'title', 'translate', 'upper', 'zfill']
===== Clases =====
Son el modelo que nos permite crear objetos.
class Coche(object):
# Constructor de la clase
def __ini__(self, marca):
# Ojo, este atributo es distinto que el argumento
self.marca = marca
El constructor permite dar una serie de valores iniciales a los objetos que se creen a partir de la clase.
Al pasarle a la clase ''object'' estamos heredando una clase incluida en Python llamada ''Object'' y nos proporcionará una serie de utilidades.
El parámetro ''self'' se utiliza para referirse al objeto que se crea, esa instancia en particular. No es obligatorio usar esa palabra, pero se utiliza por convención.
==== Atributos ====
Las propiedades de un objeto se pueden definir en cualquier sitio, pero lo recomendable es hacerlo al principio de la clase:
class Coche(object):
ruedas = 4
def __init__(self, marca):
self.marca = marca
Creando un objeto y accediendo a sus propiedades:
c1 = Coche('Renault')
print(c1.ruedas) # 4
Podemos modificar las propiedades del objeto:
c1.ruedas = 6
print(c1.ruedas) # 6
==== Métodos ====
(...)
def info(self):
print("Marca de este coche: %s", % self.marca")
Uso:
c1 = Coche('Renault')
c1.info()
# Marca de este coche: Renault
===== Creación de objetos =====
Crear un objeto a partir de una clase se conoce como **instanciar una clase**. Un objeto es una instancia de una clase.
Creando dos objetos de la misma clase:
c1 = Coche('Renault')
c2 = Coche('Ford')
print(c1.marca)
print(c2.marca)
# Renault
# Ford
===== Herencia =====
La herencia es un proceso que nos permite utilizar métodos definidos en otras clases sin tener que definirlos en nuestra clase. De esta manera se comparte la funcionalidad, atributos y métodos.
class Coche(object):
def __init__(self):
print("Acabas de crear una instancia de Coche")
def arrancar(self):
print("Coche arrancando...")
def parar(self):
print("Coche parado")
Creamos una clase heredada de la anterior:
class Renault(Coche):
def __init__(self):
# Llamamos al constructor de la clase padre
Car.__init__(self)
print("Acabas de crear una instancia de Renault")
Ahora, desde la clase **Renault**, tendremos acceso también a los métodos de la clase **Coche**:
r = Renault()
r.arrancar()
r.parar()
==== Sobrescritura de métodos ====
Si queremos redefinir algún método de la clase padre, basta con definirlo con el mismo nombre en la clase heredada y darle la funcionalidad que queramos:
class Renault(Coche):
def __init__(self):
# Llamamos al constructor de la clase padre
Car.__init__(self)
print("Acabas de crear una instancia de Renault")
def arrancar(self):
print("Arrancando el Renault")
Si quisiéramos hacer referencia a un método de la clase padre, utilizamos ''super'':
(...)
def arrancar(self):
super().arrancar()
print("Arrancando el Renault")
===== Imprimir la representación de un objeto =====
Python llama al método ''%%__%%str%%__%%'' cuando utilizamos ''print'' sobre un objeto. Por defecto, mostrará algo como:
Si queremos decidir qué imprimirá, crearemos nosotros un método ''%%__%%str%%__%%'' en nuestra clase:
class Coordenada(object):
def__init__(self, x, y):
self.x = x
self.y = y
def __str__(self):
return "<" + str(self.x) + ", " + str(self.y) + ">"
''%%__%%str%%__%%'' debe devolver un string
==== Operadores especiales ====
Hay una serie de operadores que al utilizarlos en Python, llama a ciertos métodos de los objetos:
^ Método ^ Operador ^
| ''%%__%%add%%__%%'' | ''self + other'' |
| ''%%__%%sub%%__%%'' | ''self - other'' |
| ''%%__%%eq%%__%%'' | ''self == other'' |
| ''%%__%%lt%%__%%'' | ''self < other'' |
| ''%%__%%len%%__%%'' | ''len(self)'' |
| ''%%__%%str%%__%%'' | ''print(self)'' |
* https://docs.python.org/3/reference/datamodel.html#basic-customization
===== Recursos =====
* [[https://www.youtube.com/watch?v=-DP1i2ZU9gk|8. Object Oriented Programming]] (YouTube)