Cómo se representan los objetos

PythonPythonBeginner
Practicar Ahora

This tutorial is from open-source community. Access the source code

💡 Este tutorial está traducido por IA desde la versión en inglés. Para ver la versión original, puedes hacer clic aquí

Introducción

En este laboratorio, aprenderás cómo se representan internamente los objetos de Python y entenderás los mecanismos de asignación y búsqueda de atributos. Estos conceptos son fundamentales para comprender cómo Python gestiona los datos y el comportamiento dentro de los objetos.

Además, explorarás la relación entre clases e instancias y examinarás el papel de las definiciones de clase en la programación orientada a objetos. Este conocimiento mejorará tu comprensión de las características orientadas a objetos de Python.


Skills Graph

%%%%{init: {'theme':'neutral'}}%%%% flowchart RL python(("Python")) -.-> python/ObjectOrientedProgrammingGroup(["Object-Oriented Programming"]) python/ObjectOrientedProgrammingGroup -.-> python/classes_objects("Classes and Objects") python/ObjectOrientedProgrammingGroup -.-> python/constructor("Constructor") subgraph Lab Skills python/classes_objects -.-> lab-132499{{"Cómo se representan los objetos"}} python/constructor -.-> lab-132499{{"Cómo se representan los objetos"}} end

Creación de una clase simple de acciones

En este paso, vamos a crear una clase simple para representar una acción (stock). Comprender cómo crear clases es fundamental en Python, ya que nos permite modelar objetos del mundo real y sus comportamientos. Esta clase simple de acciones será nuestro punto de partida para explorar cómo funcionan internamente los objetos de Python.

Para comenzar, necesitamos abrir una shell interactiva de Python. La shell interactiva de Python es un excelente lugar para experimentar con código Python. Puedes escribir y ejecutar comandos de Python uno por uno. Para abrirla, escribe el siguiente comando en la terminal:

python3

Una vez que hayas ingresado el comando, verás el indicador de Python (>>>). Esto indica que ahora estás dentro de la shell interactiva de Python y puedes comenzar a escribir código Python.

Ahora, definamos una clase SimpleStock. Una clase en Python es como un plano para crear objetos. Define los atributos (datos) y métodos (funciones) que tendrán los objetos de esa clase. Así es como se define la clase SimpleStock con los atributos y métodos necesarios:

>>> class SimpleStock:
...     def __init__(self, name, shares, price):
...         self.name = name
...         self.shares = shares
...         self.price = price
...     def cost(self):
...         return self.shares * self.price
...

En el código anterior, el método __init__ es un método especial en las clases de Python. Se llama constructor y se utiliza para inicializar los atributos del objeto cuando se crea un objeto. El parámetro self se refiere a la instancia de la clase que se está creando. El método cost calcula el costo total de las acciones multiplicando el número de acciones por el precio por acción.

Después de definir la clase, podemos crear instancias de la clase SimpleStock. Una instancia es un objeto real creado a partir del plano de la clase. Creemos dos instancias para representar diferentes acciones:

>>> goog = SimpleStock('GOOG', 100, 490.10)
>>> ibm = SimpleStock('IBM', 50, 91.23)

Estas instancias representan 100 acciones de Google a $490.10 por acción y 50 acciones de IBM a $91.23 por acción. Cada instancia tiene su propio conjunto de valores de atributos.

Verifiquemos que nuestras instancias estén funcionando correctamente. Podemos hacer esto comprobando sus atributos y calculando su costo. Esto nos ayudará a confirmar que la clase y sus métodos están funcionando como se espera.

>>> goog.name
'GOOG'
>>> goog.shares
100
>>> goog.price
490.1
>>> goog.cost()
49010.0
>>> ibm.cost()
4561.5

El método cost() multiplica el número de acciones por el precio por acción para calcular el costo total de poseer esas acciones. Al ejecutar estos comandos, podemos ver que las instancias tienen los valores de atributos correctos y que el método cost está calculando el costo con precisión.

Explorando el diccionario interno de los objetos

En Python, los objetos son un concepto fundamental. Un objeto se puede considerar como un contenedor que almacena datos y tiene ciertos comportamientos. Uno de los aspectos interesantes de los objetos de Python es cómo almacenan sus atributos. Los atributos son como variables que pertenecen a un objeto. Python almacena estos atributos en un diccionario especial, que se puede acceder a través del atributo __dict__. Este diccionario es una parte interna del objeto y es donde Python lleva el registro de todos los datos asociados con ese objeto.

Echemos un vistazo más de cerca a esta estructura interna utilizando nuestras instancias de SimpleStock. En Python, una instancia es un objeto individual creado a partir de una clase. Por ejemplo, si SimpleStock es una clase, goog e ibm son instancias de esa clase.

Para ver el diccionario interno de estas instancias, podemos usar la shell interactiva de Python. La shell interactiva de Python es una excelente herramienta para probar rápidamente el código y ver los resultados. En la shell interactiva de Python, escribe los siguientes comandos para inspeccionar el atributo __dict__ de nuestras instancias:

>>> goog.__dict__
{'name': 'GOOG', 'shares': 100, 'price': 490.1}
>>> ibm.__dict__
{'name': 'IBM', 'shares': 50, 'price': 91.23}

Cuando ejecutas estos comandos, la salida muestra que cada instancia tiene su propio diccionario interno. Este diccionario contiene todos los atributos de la instancia. Por ejemplo, en la instancia goog, los atributos name, shares y price se almacenan en el diccionario con sus valores correspondientes. Así es como Python implementa los atributos de los objetos detrás de escena. Cada objeto tiene un diccionario privado que contiene todos sus atributos.

Es importante entender lo que el atributo __dict__ revela sobre la implementación interna de nuestros objetos:

  1. Las claves en el diccionario corresponden a los nombres de los atributos. Por ejemplo, en la instancia goog, la clave 'name' corresponde al atributo name del objeto.
  2. Los valores en el diccionario son los valores de los atributos. Entonces, el valor 'GOOG' es el valor del atributo name para la instancia goog.
  3. Cada instancia tiene su propio __dict__ separado. Esto significa que los atributos de una instancia son independientes de los atributos de otra instancia. Por ejemplo, el atributo shares de la instancia goog puede ser diferente del atributo shares de la instancia ibm.

Este enfoque basado en diccionarios permite que Python sea muy flexible con los objetos. Como veremos en el siguiente paso, podemos aprovechar esta flexibilidad para modificar y acceder a los atributos de los objetos de varias maneras.

Agregar y modificar atributos de objetos

En Python, los objetos se implementan basados en diccionarios. Esta implementación le otorga a Python un alto grado de flexibilidad al tratar con atributos de objetos. A diferencia de algunos otros lenguajes de programación, Python no limita los atributos de un objeto solo a aquellos definidos en su clase. Esto significa que puedes agregar nuevos atributos a un objeto en cualquier momento, incluso después de que el objeto haya sido creado.

Exploremos esta flexibilidad agregando un nuevo atributo a una de nuestras instancias. Supongamos que tenemos una instancia llamada goog de una clase. Agregaremos un atributo date a ella:

>>> goog.date = "6/11/2007"
>>> goog.__dict__
{'name': 'GOOG', 'shares': 100, 'price': 490.1, 'date': '6/11/2007'}

Aquí, agregamos un nuevo atributo date a la instancia goog. Observe que este atributo date no estaba definido en la clase SimpleStock. Este nuevo atributo existe solo en la instancia goog. Para confirmar esto, veamos la instancia ibm:

>>> ibm.__dict__
{'name': 'IBM', 'shares': 50, 'price': 91.23}
>>> hasattr(ibm, 'date')
False

Como podemos ver, la instancia ibm no tiene el atributo date. Esto muestra tres características importantes de los objetos de Python:

  1. Cada instancia tiene su propio conjunto único de atributos.
  2. Los atributos se pueden agregar a un objeto después de que haya sido creado.
  3. Agregar un atributo a una instancia no afecta a otras instancias.

Ahora, probemos una forma diferente de agregar un atributo. En lugar de usar la notación de punto, manipularemos directamente el diccionario subyacente del objeto. En Python, cada objeto tiene un atributo especial __dict__ que almacena todos sus atributos como pares clave - valor.

>>> goog.__dict__['time'] = '9:45am'
>>> goog.time
'9:45am'
>>> goog.__dict__
{'name': 'GOOG', 'shares': 100, 'price': 490.1, 'date': '6/11/2007', 'time': '9:45am'}

Al modificar directamente el diccionario __dict__, agregamos un nuevo atributo time a la instancia goog. Cuando accedemos a goog.time, Python busca la clave 'time' en el diccionario __dict__ y devuelve su valor correspondiente.

Estos ejemplos ilustran que los objetos de Python son esencialmente diccionarios con algunas características adicionales. La flexibilidad de los objetos de Python permite una modificación dinámica, lo cual es muy poderoso y conveniente en la programación.

Entendiendo la relación entre clases e instancias

Ahora, vamos a explorar cómo están relacionadas las clases y las instancias en Python, y cómo funciona la búsqueda de métodos. Este es un concepto importante porque te ayuda a entender cómo Python encuentra y utiliza métodos y atributos cuando trabajas con objetos.

Primero, veamos a qué clase pertenecen nuestras instancias. Saber la clase de una instancia es crucial, ya que nos dice dónde Python buscará los métodos y atributos relacionados con esa instancia.

>>> goog.__class__
<class '__main__.SimpleStock'>
>>> ibm.__class__
<class '__main__.SimpleStock'>

Ambas instancias tienen una referencia a la clase SimpleStock. Esta referencia es como un puntero que Python utiliza cuando necesita buscar métodos. Cuando llamas a un método en una instancia, Python utiliza esta referencia para encontrar la definición adecuada del método.

Cuando llamas a un método en una instancia, Python sigue estos pasos:

  1. Busca el atributo en el __dict__ de la instancia. El __dict__ de una instancia es como un área de almacenamiento donde se guardan todos los atributos específicos de la instancia.
  2. Si no lo encuentra, verifica el __dict__ de la clase. El __dict__ de la clase almacena todos los atributos y métodos que son comunes a todas las instancias de esa clase.
  3. Si lo encuentra en la clase, devuelve ese atributo.

Veamos esto en acción. Primero, verificamos que el método cost no está en los diccionarios de las instancias. Este paso nos ayuda a entender que el método cost no es específico de cada instancia, sino que está definido a nivel de clase.

>>> 'cost' in goog.__dict__
False
>>> 'cost' in ibm.__dict__
False

Entonces, ¿de dónde viene el método cost? Veamos la clase. Al mirar el __dict__ de la clase, podemos averiguar dónde está definido el método cost.

>>> SimpleStock.__dict__['cost']
<function SimpleStock.cost at 0x7f...>

El método está definido en la clase, no en las instancias. Cuando llamas a goog.cost(), Python no encuentra cost en goog.__dict__, así que busca en SimpleStock.__dict__ y lo encuentra allí.

En realidad, puedes llamar al método directamente desde el diccionario de la clase, pasando la instancia como primer argumento (que se convierte en self). Esto muestra cómo Python llama internamente a los métodos cuando usas la sintaxis normal instancia.método().

>>> SimpleStock.__dict__['cost'](goog)
49010.0
>>> SimpleStock.__dict__['cost'](ibm)
4561.5

Esto es esencialmente lo que Python hace detrás de escena cuando llamas a goog.cost().

Ahora, agreguemos un atributo de clase. Los atributos de clase son compartidos por todas las instancias. Esto significa que todas las instancias de la clase pueden acceder a este atributo, y se almacena solo una vez a nivel de clase.

>>> SimpleStock.exchange = 'NASDAQ'
>>> goog.exchange
'NASDAQ'
>>> ibm.exchange
'NASDAQ'

Ambas instancias pueden acceder al atributo exchange, pero no está almacenado en sus diccionarios individuales. Verifiquemos esto comprobando los diccionarios de la instancia y de la clase.

>>> 'exchange' in goog.__dict__
False
>>> 'exchange' in SimpleStock.__dict__
True
>>> SimpleStock.__dict__['exchange']
'NASDAQ'

Esto demuestra que:

  1. Los atributos de clase son compartidos por todas las instancias. Todas las instancias pueden acceder al mismo atributo de clase sin tener su propia copia.
  2. Python verifica primero el diccionario de la instancia, luego el diccionario de la clase. Este es el orden en el que Python busca atributos cuando intentas acceder a ellos en una instancia.
  3. Las clases actúan como un repositorio de datos y comportamientos (métodos) compartidos. La clase almacena todos los atributos y métodos comunes que pueden ser utilizados por todas sus instancias.

Si modificamos un atributo de instancia con el mismo nombre, se oculta el atributo de clase. Esto significa que cuando accedes al atributo en esa instancia, Python utilizará el valor específico de la instancia en lugar del valor a nivel de clase.

>>> ibm.exchange = 'NYSE'
>>> ibm.exchange
'NYSE'
>>> goog.exchange  ## Todavía usando el atributo de clase
'NASDAQ'
>>> ibm.__dict__['exchange']
'NYSE'

Ahora ibm tiene su propio atributo exchange que oculta el atributo de clase, mientras que goog sigue usando el atributo de clase.

Resumen

En este laboratorio, has aprendido sobre el funcionamiento interno del sistema de objetos de Python y varios conceptos clave. En primer lugar, los objetos de Python almacenan atributos en un diccionario accesible a través del atributo __dict__, lo que ofrece flexibilidad. En segundo lugar, has comprendido cómo funcionan la asignación y la búsqueda de atributos, incluyendo la adición dinámica de atributos y el orden de comprobación de atributos.

Además, has explorado la relación entre clases e instancias, donde las clases contienen datos y comportamientos compartidos, y las instancias mantienen su propio estado. También has descubierto cómo funcionan las llamadas a métodos, donde los métodos de la clase actúan sobre las instancias a través del parámetro self. Comprender estos conceptos profundiza tu comprensión del modelo de POO (Programación Orientada a Objetos) de Python y es útil para la depuración, el diseño de jerarquías de clases y el aprendizaje de características avanzadas.