9.3. Programación orientada a objetos¶
La programación orientada a objetos (POO, u OOP según sus siglas en inglés) es un paradigma de programación que viene a innovar la forma de obtener resultados. Los objetos manipulan los datos de entrada para la obtención de datos de salida específicos, donde cada objeto ofrece una funcionalidad especial.
Muchos de los objetos prediseñados de los lenguajes de programación actuales permiten la agrupación en bibliotecas o librerías, sin embargo, muchos de estos lenguajes permiten al usuario la creación de sus propias bibliotecas.
Está basada en varias técnicas, como las siguientes:
cohesión.
acoplamiento.
La POO tiene sus raíces en la década del 60 con el lenguaje de programación Simula
que en 1967, el cual fue el primer lenguaje que posee las características principales
de un lenguaje orientado a objetos.
Smalltalk (de 1972 a 1980) es posiblemente el ejemplo canónico, y con el que gran parte de la teoría de la POO se ha desarrollado. Más su uso se popularizó a principios de la década de 1990.
En la actualidad, existe una gran variedad de lenguajes de programación que soportan la orientación a objetos.
Los objetivos de la POO son:
Organizar el código fuente, y
re-usar código fuente en similares contextos.
Nota
Más información consulte el articulo de Wikipedia Programación orientada a objetos.
9.3.1. POO en Python¶
El mecanismo de clases de Python agrega clases al lenguaje con un mínimo de nuevas sintaxis y semánticas.
En Python las clases es una mezcla de los mecanismos de clase encontrados en C++
y Modula-3
.
Como es cierto para los módulos, las clases en Python no ponen una barrera absoluta entre la definición y el usuario, sino que más bien se apoya en la cortesía del usuario de no «forzar la definición».
Sin embargo, se mantiene el poder completo de las características más importantes de las clases: el mecanismo de la herencia de clases permite múltiples clases base, una clase derivada puede sobrescribir cualquier método de su(s) clase(s) base, y un método puede llamar al método de la clase base con el mismo nombre.
«Los objetos pueden tener una cantidad arbitraria de datos.»
En terminología de C++
, todos los miembros de las clases (incluyendo los miembros
de datos), son públicos, y todas las funciones miembro son virtuales.
Como en Modula-3
, no hay atajos para hacer referencia a los miembros del objeto
desde sus métodos: la función método se declara con un primer argumento explícito
que representa al objeto, el cual se provee implícitamente por la llamada.
Como en Smalltalk
, las clases mismas son objetos. Esto provee una semántica para
importar y renombrar.
A diferencia de C++
y Modula-3
, los tipos de datos integrados pueden usarse
como clases base para que el usuario los extienda.
También, como en C++
pero a diferencia de Modula-3
, la mayoría de los operadores
integrados con sintaxis especial (operadores aritméticos, de subíndice, etc.) pueden ser
redefinidos por instancias de la clase.
(Sin haber una terminología universalmente aceptada sobre clases, haré uso ocasional de
términos de Smalltalk
y C++
. Usaría términos de Modula-3
, ya que su semántica
orientada a objetos es más cercana a Python que C++
, pero no espero que muchos lectores
hayan escuchado hablar de él).
Algunas particularidades de POO en Python son las siguientes:
Todo es un objeto, incluyendo los tipos y clases.
Permite herencia múltiple.
No existen métodos ni atributos privados.
Los atributos pueden ser modificados directamente.
Permite «monkey patching».
Permite «duck typing».
Permite la sobrecarga de operadores.
Permite la creación de nuevos tipos de datos.
A continuación se procede a definir algunos conceptos necesarios para entender la POO:
9.3.2. Objetos¶
Los objetos son abstracción de Python para data. Toda la data en un programa Python es representado por objectos o por relaciones entre objectos. (En cierto sentido, y en el código modelo de Von Neumann de una «computadora almacenada del programa» también es un código representado por los objetos.)
Cada objeto tiene una identidad, un tipo y un valor. Una identidad de objecto nunca cambia una vez es creada; usted puede pensar eso como la dirección de objeto en memoria. El operador in compara la identidad de dos objetos; la función id() devuelve un número entero representando la identidad (actualmente implementado como su dirección).
El tipo de un objeto también es inmutable. El tipo de un objeto determina las operaciones que admite el objeto (por ejemplo, «¿tiene una longitud?») Y también define los valores posibles para los objetos de ese tipo. La función «type()» devuelve el tipo de un objeto (que es un objeto en sí mismo). El valor de algunos objetos puede cambiar. Se dice que los objetos cuyo valor puede cambiar son mutables; los objetos cuyo valor no se puede cambiar una vez que se crean se llaman immutable. (El valor de un objeto contenedor inmutable que contiene una referencia a un objeto mutable puede cambiar cuando se cambia el valor de este último; sin embargo, el contenedor todavía se considera inmutable, porque la colección de objetos que contiene no se puede cambiar. Por lo tanto, la inmutabilidad no es estrictamente lo mismo que tener un valor incambiable, es más sutil.) La mutabilidad de un objeto está determinada por su tipo; por ejemplo, los números, las cadenas y las tuplas son inmutables, mientras que los diccionarios y las listas son mutables.
Los objetos son la clave para entender la POO. Si mira a nuestro alrededor encontrará un sin fin de objetos de la vida real: perro, escritorio, televisor, bicicleta, etc…
En Python puede definir una clase con la palabra reservada class, de la siguiente forma:
1 2 |
|
En el ejemplo anterior, el nombre de la clase es Persona
y dentro del bloque de
código usa la sentencia pass. Aunque no es requerido por
el intérprete, los nombres de las clases se escriben por convención capitalizadas.
Las clases pueden (y siempre deberían) tener comentarios.
9.3.3. Atributos¶
Los atributos o propiedades de los objetos son las características que puede tener
un objeto, como el color. Si el objeto es Persona
, los atributos podrían ser:
cedula
, nombre
, apellido
, sexo
, etc…
Los atributos describen el estado de un objeto. Pueden ser de cualquier tipo de dato.
1 2 3 4 5 6 7 |
|
Usted puede probar el código anterior, si lo transcribe en el consola interactiva Python como lo siguiente:
>>> class Persona:
... """Clase que representa una Persona"""
... cedula = "V-13458796"
... nombre = "Leonardo"
... apellido = "Caballero"
... sexo = "M"
...
>>> macagua = Persona
>>> type(macagua)
<type 'classobj'>
>>> dir(macagua)
['__doc__', '__module__', 'apellido', 'cedula', 'nombre', 'sexo']
>>> macagua.cedula
'V-13458796'
>>> macagua.nombre
'Leonardo'
>>> macagua.apellido
'Caballero'
>>> macagua.sexo
'M'
>>> print("El objeto de la clase {}, {}.".format(macagua.__name__, macagua.__doc__))
El objeto de la clase Persona, Clase que representa una Persona.
>>> print("Hola, mucho gusto, mi nombre es '{} {}', \nmi cédula de identidad es '{}', y mi sexo es '{}'.".format(
... macagua.nombre,
... macagua.apellido,
... macagua.cedula,
... macagua.sexo)
... )
Hola, mucho gusto, mi nombre es 'Leonardo Caballero',
mi cédula de identidad es 'V-13458796', y mi sexo es 'M'.
Si el nombre de un atributo esta encerrado entre dobles guiones bajos son atributos especiales.
__name__, describe el nombre del objeto o del método.
>>> macagua.__name__
'Persona'
__doc__, contiene la documentación de un módulo, una clase, o método especifico, escrita en el formato docstrings.
>>> macagua.__doc__
'Clase que representa una Persona'
Si el nombre de un atributo esta con dobles guiones bajos al principio son atributos «escondidos». A continuación un pseudo código que ilustra un ejemplo:
>>> ms_windows.__privado
'True'
>>> ms_windows.codigo_fuente.__no_tocar
'True'
En la sección encapsulación se describe esto a más profundidad.
9.3.4. Métodos¶
Los métodos describen el comportamiento de los objetos de una clase. Estos representan las operaciones que se pueden realizar con los objetos de la clase,
La ejecución de un método puede conducir a cambiar el estado del objeto.
Se definen de la misma forma que las funciones normales pero deben declararse dentro de la clase y su primer argumento siempre referencia a la instancia que la llama, de esta forma se afirma que los métodos son funciones, adjuntadas a objectos.
Nota
Usted puede encontrar ejemplos en las funciones de cadena de caracteres, listas, diccionarios, etc.
Si el objeto es Persona
, los métodos pueden ser: hablar
, caminar
,
comer
, dormir
, etc.
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 |
|
La única diferencia sintáctica entre la definición de un método y la definición de una
función es que el primer parámetro del método por convención debe ser el nombre self
.
Usted puede probar el código anterior, si lo transcribe en el consola interactiva Python como lo siguiente:
>>> class Persona:
... """Clase que representa una Persona"""
...
... cedula = "V-13458796"
... nombre = "Leonardo"
... apellido = "Caballero"
... sexo = "M"
...
... def hablar(self, mensaje):
... """Mostrar mensaje de saludo de Persona"""
... return mensaje
...
>>>
Analizar la estructura de la clase el cual representa a una Persona:
>>> type(Persona())
<class '__main__.Persona'>
>>> Persona().__doc__
'Clase que representa una Persona'
>>> Persona().hablar.__doc__
'Mostrar mensaje de saludo de Persona'
>>> type(Persona().hablar)
<class 'method'>
>>> Persona().hablar.__doc__
'Mostrar mensaje de saludo de Persona'
>>> Persona().hablar("Hola, soy la clase {0}.".format(macagua.__class__.__name__))
'Hola, soy la clase Persona.'
>>>
Crear una instancia de objeto para la clase Persona
:
>>> macagua = Persona()
>>> type(macagua)
<class '__main__.Persona'>
>>> macagua.__doc__
'Clase que representa una Persona'
>>> macagua.hablar.__doc__
'Mostrar mensaje de saludo de Persona'
>>> type(macagua.hablar)
<class 'method'>
>>> macagua.hablar("Hola Plone")
'Hola Plone'
>>> macagua.hablar("Hola, soy la clase {0}.".format(macagua.__class__.__name__))
'Hola, soy la clase Persona.'
>>>
9.3.4.1. Ámbito de los métodos¶
Los métodos cuentan con un espacio de nombres propio. En caso de no encontrar un nombre en su ámbito local, buscará en el ámbito superior hasta encontrar alguna coincidencia.
Los métodos pueden acceder y crear atributos dentro del objeto al que pertenecen,
anteponiendo la palabra self
y el operador de atributo «.» antes del nombre
del atributo en cuestión.
9.3.4.2. Métodos especiales¶
Las clases en Python cuentan con múltiples métodos especiales, los cuales se
encuentran entre dobles guiones bajos __<metodo>__()
.
Los métodos especiales más utilizados son __init__(), __str__() y __del__().
9.3.4.2.1. __str__()¶
El método __str__()
es un método especial, el cual se ejecuta al momento en el
cual un objeto se manda a mostrar, es decir es una cadena representativa de la clase,
la cual puede incluir formatos personalizados de presentación del mismo.
1 2 3 4 5 6 7 8 9 |
|
9.3.4.2.2. __del__()¶
El método __del__()
es un método especial, el cual se ejecuta al momento en el
cual un objeto es descartado por el intérprete. El comportamiento de __del__()
es muy similar a los «destructores» en otros lenguajes.
9.3.4.3. Métodos de clase¶
En ocasiones es necesario contar con métodos que interactúen con elementos de la clase de la cual el objeto es instanciado. Python permite definir métodos de clase para esto.
Los métodos de clase son aquellos que están ligados directamente con los atributos
definidos en la clase que los contiene. Para definir un método de clase se utiliza el
decorador @classmethod
y por convención se utiliza cls
como argumento inicial
en lugar de self
.
Del mismo modo, los métodos de clase utilizan el prefijo cls
para referirse a los
atributos de la clase.
class <Clase>:
...
...
@classmethod
def <metodo>(cls, <argumentos>):
...
...
9.3.4.4. Métodos estáticos¶
Los métodos estáticos hacen referencia a las instancias y métodos de una clase. Para
definir un método estático se utiliza el decorador @staticmethod
y no utiliza ningún
argumento inicial.
Al no utilizar self
, los métodos estáticos no pueden interactuar con los atributos
y métodos de la instancia.
Para referirse a los elementos de la clase, se debe utilizar el nombre de la clase como prefijo.
class <Clase>:
...
...
@staticmethod
def <metodo>(<argumentos>):
...
...
9.3.4.5. Interfaces¶
La forma en que los métodos de un objeto pueden ser accedidos por otros objetos se conoce como «interfaz». Una interfaz bien definida permite a objetos de distinta índole interactuar entre sí de forma modular. La interfaz define el modo en que los objetos intercambian información.
Truco
Para más detalle consulte el paquete zope.interface.
9.3.5. Clases¶
Las clases definen las características del objeto.
Con todos los conceptos anteriores explicados, se puede decir que una clase es una plantilla genérica de un objeto. La clase proporciona variables iniciales de estado (donde se guardan los atributos) e implementaciones de comportamiento (métodos) necesarias para crear nuevos objetos, son los modelos sobre los cuáles serán construidos.
9.3.6. Instancias¶
Ya sabe que una clase es una estructura general del objeto. Por ejemplo, puede decir
que la clase Persona
necesita tener una cedula
, un nombre
, un apellido
y una sexo
, pero no va a decir cual es cedula
, nombre
, apellido
y
sexo
, es aquí donde entran las instancias.
Una instancia es una copia específica de la clase con todo su contenido. Por ejemplo:
>>> persona1 = Persona("V-13458796", "Leonardo", "Caballero", "M")
Traceback (most recent call last):
File "<stdin>", line 1, in <module>
TypeError: this constructor takes no arguments
La excepción TypeError indica que el método constructor no toma argumentos, esto se debe a que la momento de definir la clase a cada atributo se le asigno un valor (tipo de dato).
Usted puede definir el método constructor de la clase usando el método __init__().
9.3.6.1. Método __init__()¶
El método __init__()
es un método especial, el cual se ejecuta al momento de
instanciar un objeto. El comportamiento de __init__()
es muy similar a los
«constructores» en otros lenguajes. Los argumentos que se utilizan en la
definición de __init__()
corresponden a los parámetros que se deben ingresar
al instanciar un objeto.
1 2 3 4 5 6 |
|
9.3.6.2. Función isinstance()¶
isinstance()
, es una función integrada la cual le
permite corroborar si un objeto es instancia de una clase.
Nota
Más información consulte la documentación detallada de la función isinstance().
Importante
Usted puede descargar el código usado en esta sección haciendo clic
aquí
.
Truco
Para ejecutar el código poo.py
, abra una consola de comando, acceda al
directorio donde se encuentra el mismo, y ejecute el siguiente comando:
$ python poo.py
Ver también
Consulte la sección de lecturas suplementarias del entrenamiento para ampliar su conocimiento en esta temática.
¿Cómo puedo ayudar?
¡Mi soporte está aquí para ayudar!
Mi horario de oficina es de lunes a sábado, de 9 AM a 5 PM. GMT-4 - Caracas, Venezuela.
La hora aquí es actualmente 7:35 PM GMT-4.
Mi objetivo es responder a todos los mensajes dentro de un día hábil.