Acceso a atributos y métodos enlazados

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 sobre el acceso a atributos en Python. Explorarás cómo utilizar funciones como getattr() y setattr() para manipular eficazmente los atributos de los objetos.

Además, experimentarás con métodos enlazados (bound methods). El laboratorio te guiará a través de estos conceptos, y crearás un archivo llamado tableformat.py durante el proceso.


Skills Graph

%%%%{init: {'theme':'neutral'}}%%%% flowchart RL python(("Python")) -.-> python/FunctionsGroup(["Functions"]) python(("Python")) -.-> python/ModulesandPackagesGroup(["Modules and Packages"]) python(("Python")) -.-> python/ObjectOrientedProgrammingGroup(["Object-Oriented Programming"]) python(("Python")) -.-> python/PythonStandardLibraryGroup(["Python Standard Library"]) python/FunctionsGroup -.-> python/function_definition("Function Definition") python/FunctionsGroup -.-> python/build_in_functions("Build-in Functions") python/ModulesandPackagesGroup -.-> python/using_packages("Using Packages") python/ObjectOrientedProgrammingGroup -.-> python/classes_objects("Classes and Objects") python/PythonStandardLibraryGroup -.-> python/data_collections("Data Collections") subgraph Lab Skills python/function_definition -.-> lab-132491{{"Acceso a atributos y métodos enlazados"}} python/build_in_functions -.-> lab-132491{{"Acceso a atributos y métodos enlazados"}} python/using_packages -.-> lab-132491{{"Acceso a atributos y métodos enlazados"}} python/classes_objects -.-> lab-132491{{"Acceso a atributos y métodos enlazados"}} python/data_collections -.-> lab-132491{{"Acceso a atributos y métodos enlazados"}} end

Comprender el acceso a atributos en Python

En Python, los objetos son un concepto fundamental. Pueden almacenar datos en atributos, que son como contenedores con nombre para valores. Puedes pensar en los atributos como variables que pertenecen a un objeto. Hay varias formas de acceder a estos atributos. El método más sencillo y comúnmente utilizado es la notación de punto (.). Sin embargo, Python también ofrece funciones específicas que te brindan más flexibilidad al trabajar con atributos.

La notación de punto

Comencemos creando un objeto Stock y veamos cómo podemos manipular sus atributos utilizando la notación de punto. La notación de punto es una forma sencilla e intuitiva de acceder y modificar los atributos de un objeto.

Primero, abre una nueva terminal y inicia la shell interactiva de Python. Aquí es donde puedes escribir y ejecutar código Python línea por línea.

## Open a new terminal and run Python interactive shell
python3

## Import the Stock class from the stock module
from stock import Stock

## Create a Stock object
s = Stock('GOOG', 100, 490.1)

## Get an attribute
print(s.name)    ## Output: 'GOOG'

## Set an attribute
s.shares = 50
print(s.shares)  ## Output: 50

## Delete an attribute
del s.shares
## If we try to access s.shares now, we'll get an AttributeError

En el código anterior, primero importamos la clase Stock del módulo stock. Luego creamos una instancia de la clase Stock llamada s. Para obtener el valor del atributo name, usamos s.name. Para cambiar el valor del atributo shares, simplemente asignamos un nuevo valor a s.shares. Y si queremos eliminar un atributo, usamos la palabra clave del seguida del nombre del atributo.

Funciones de acceso a atributos

Python proporciona cuatro funciones integradas (built - in functions) que son muy útiles para la manipulación de atributos. Estas funciones te dan más control al trabajar con atributos, especialmente cuando necesitas manejarlos de forma dinámica.

  1. getattr() - Esta función se utiliza para obtener el valor de un atributo.
  2. setattr() - Te permite establecer el valor de un atributo.
  3. delattr() - Puedes usar esta función para eliminar un atributo.
  4. hasattr() - Esta función verifica si un atributo existe en un objeto.

Veamos cómo usar estas funciones:

## Create a new Stock object
s = Stock('GOOG', 100, 490.1)

## Get an attribute
print(getattr(s, 'name'))       ## Output: 'GOOG'

## Set an attribute
setattr(s, 'shares', 50)
print(s.shares)                 ## Output: 50

## Check if an attribute exists
print(hasattr(s, 'name'))       ## Output: True
print(hasattr(s, 'symbol'))     ## Output: False

## Delete an attribute
delattr(s, 'shares')
print(hasattr(s, 'shares'))     ## Output: False

Estas funciones son especialmente útiles cuando necesitas trabajar con atributos de forma dinámica. En lugar de usar nombres de atributos codificados de forma rígida, puedes usar nombres de variables. Por ejemplo, si tienes una variable que almacena el nombre de un atributo, puedes pasar esa variable a estas funciones para realizar operaciones en el atributo correspondiente. Esto te da más flexibilidad en tu código, especialmente cuando se trata de diferentes objetos y atributos de forma más dinámica.

Usando getattr() para el procesamiento genérico de objetos

La función getattr() es una herramienta poderosa en Python que te permite acceder a los atributos de un objeto de forma dinámica. Esto es especialmente útil cuando quieres procesar objetos de manera genérica. En lugar de escribir código específico para un tipo de objeto en particular, puedes usar getattr() para trabajar con cualquier objeto que tenga los atributos requeridos. Esta flexibilidad hace que tu código sea más reutilizable y adaptable.

Procesando múltiples atributos

Comencemos aprendiendo cómo acceder a múltiples atributos de un objeto utilizando la función getattr(). Este es un escenario común cuando necesitas extraer información específica de un objeto.

Primero, abre una shell interactiva de Python si cerraste la anterior. Puedes hacer esto ejecutando el siguiente comando en tu terminal:

## Open a Python interactive shell if you closed the previous one
python3

A continuación, importaremos la clase Stock y crearemos un objeto Stock. La clase Stock representa una acción con atributos como name, shares y price.

## Import the Stock class and create a stock object
from stock import Stock
s = Stock('GOOG', 100, 490.1)

Ahora, definiremos una lista de nombres de atributos a los que queremos acceder. Esta lista nos ayudará a iterar sobre los atributos y a imprimir sus valores.

## Define a list of attribute names
fields = ['name', 'shares', 'price']

Finalmente, usaremos un bucle for para iterar sobre la lista de nombres de atributos y acceder a cada atributo utilizando getattr(). Imprimiremos el nombre del atributo y su valor en cada iteración.

## Access each attribute using getattr()
for name in fields:
    print(f"{name}: {getattr(s, 'name')}" if name == 'name' else f"{name}: {getattr(s, name)}")

Cuando ejecutes este código, verás la siguiente salida:

name: GOOG
shares: 100
price: 490.1

Esta salida muestra que pudimos acceder e imprimir los valores de múltiples atributos del objeto Stock utilizando la función getattr().

Valores predeterminados con getattr()

La función getattr() también ofrece una característica útil: la posibilidad de especificar un valor predeterminado si el atributo al que estás intentando acceder no existe. Esto puede evitar que tu código lance un AttributeError y hacerlo más robusto.

Veamos cómo funciona esto. Primero, intentaremos acceder a un atributo que no existe en el objeto Stock. Usaremos getattr() y proporcionaremos un valor predeterminado de 'N/A'.

## Try to access an attribute that doesn't exist
print(getattr(s, 'symbol', 'N/A'))  ## Output: 'N/A'

En este caso, dado que el atributo symbol no existe en el objeto Stock, getattr() devuelve el valor predeterminado 'N/A'.

Ahora, comparemos esto con acceder a un atributo existente. Accederemos al atributo name, que sí existe en el objeto Stock.

## Compare with an existing attribute
print(getattr(s, 'name', 'N/A'))    ## Output: 'GOOG'

Aquí, getattr() devuelve el valor real del atributo name, que es 'GOOG'.

Procesando una colección de objetos

La función getattr() se vuelve aún más poderosa cuando necesitas procesar una colección de objetos. Veamos cómo podemos usarla para procesar una cartera de acciones.

Primero, importaremos la función read_portfolio del módulo stock. Esta función lee una cartera de acciones desde un archivo CSV y devuelve una lista de objetos Stock.

## Import the portfolio reading function
from stock import read_portfolio

A continuación, usaremos la función read_portfolio para leer la cartera desde un archivo CSV llamado portfolio.csv.

## Read the portfolio from CSV file
portfolio = read_portfolio('portfolio.csv')

Finalmente, usaremos un bucle for para iterar sobre la lista de objetos Stock en la cartera. Para cada acción, usaremos getattr() para acceder a los atributos name y shares e imprimir sus valores.

## Print the name and shares of each stock
for stock in portfolio:
    print(f"Stock: {getattr(stock, 'name')}, Shares: {getattr(stock, 'shares')}")

Este enfoque hace que tu código sea más flexible porque puedes trabajar con nombres de atributos como cadenas. Estas cadenas se pueden pasar como argumentos o almacenar en estructuras de datos, lo que te permite cambiar fácilmente los atributos a los que quieres acceder sin modificar la lógica central de tu código.

Creando un formateador de tablas utilizando el acceso a atributos

En programación, el acceso a atributos es un concepto fundamental que nos permite interactuar con las propiedades de los objetos. Ahora, vamos a poner en práctica lo que hemos aprendido sobre el acceso a atributos. Crearemos una utilidad útil: un formateador de tablas. Este formateador tomará una colección de objetos y los mostrará en un formato tabular, lo que hará que los datos sean más fáciles de leer y entender.

Creando el módulo tableformat.py

Primero, necesitamos crear un nuevo archivo de Python. Este archivo contendrá el código para nuestro formateador de tablas.

Para crear el archivo, sigue estos pasos:

  1. En el WebIDE, haz clic en el menú "File".
  2. En el menú desplegable, selecciona "New File".
  3. Guarda el archivo recién creado como tableformat.py en el directorio /home/labex/project/.

Ahora que tenemos nuestro archivo, escribamos el código para la función print_table() dentro de tableformat.py. Esta función se encargará de formatear e imprimir nuestros objetos en una tabla.

def print_table(objects, fields):
    """
    Print a collection of objects as a formatted table.

    Args:
        objects: A sequence of objects
        fields: A list of attribute names
    """
    ## Print the header
    headers = fields
    for header in headers:
        print(f"{header:>10}", end=' ')
    print()

    ## Print the separator line
    for header in headers:
        print("-" * 10, end=' ')
    print()

    ## Print the data
    for obj in objects:
        for field in fields:
            value = getattr(obj, field)
            print(f"{value:>10}", end=' ')
        print()

Analicemos lo que hace esta función:

  1. Toma dos argumentos: una secuencia de objetos y una lista de nombres de atributos. La secuencia de objetos es los datos que queremos mostrar, y la lista de nombres de atributos le dice a la función qué propiedades de los objetos mostrar.
  2. Imprime una fila de encabezado. La fila de encabezado contiene los nombres de los atributos que nos interesan.
  3. Imprime una línea separadora. Esta línea ayuda a separar visualmente el encabezado de los datos.
  4. Para cada objeto en la secuencia, imprime el valor de cada atributo especificado. Utiliza la función getattr() para acceder al valor del atributo de cada objeto.

Ahora, probemos nuestra función print_table() para ver si funciona como se espera.

## Open a Python interactive shell
python3

## Import our modules
from stock import read_portfolio
import tableformat

## Read the portfolio data
portfolio = read_portfolio('portfolio.csv')

## Print the portfolio as a table with name, shares, and price columns
tableformat.print_table(portfolio, ['name', 'shares', 'price'])

Cuando ejecutes el código anterior, deberías ver la siguiente salida:

      name     shares      price
---------- ---------- ----------
        AA        100       32.2
       IBM         50       91.1
       CAT        150      83.44
      MSFT        200      51.23
        GE         95      40.37
      MSFT         50       65.1
       IBM        100      70.44

Una de las grandes ventajas de nuestra función print_table() es su flexibilidad. Podemos cambiar las columnas que se muestran simplemente cambiando la lista fields.

## Just show shares and name
tableformat.print_table(portfolio, ['shares', 'name'])

Ejecutar este código te dará la siguiente salida:

    shares       name
---------- ----------
       100         AA
        50        IBM
       150        CAT
       200       MSFT
        95         GE
        50       MSFT
       100        IBM

El poder de este enfoque radica en su generalidad. Podemos usar la misma función print_table() para imprimir tablas de cualquier tipo de objeto, siempre y cuando sepamos los nombres de los atributos que queremos mostrar. Esto hace que nuestro formateador de tablas sea una herramienta muy útil en nuestro conjunto de herramientas de programación.

✨ Revisar Solución y Practicar

Comprendiendo los métodos enlazados en Python

En Python, los métodos son un tipo especial de atributos que se pueden llamar. Cuando accedes a un método a través de un objeto, obtienes lo que llamamos un "método enlazado". Un método enlazado es esencialmente un método que está vinculado a un objeto específico. Esto significa que tiene acceso a los datos del objeto y puede operar sobre ellos.

Accediendo a métodos como atributos

Comencemos a explorar los métodos enlazados utilizando nuestra clase Stock. Primero, veremos cómo acceder a un método como atributo de un objeto.

## Open a Python interactive shell
python3

## Import the Stock class and create a stock object
from stock import Stock
s = Stock('GOOG', 100, 490.10)

## Access the cost method without calling it
cost_method = s.cost
print(cost_method)  ## Output: <bound method Stock.cost of <stock.Stock object at 0x...>>

## Call the method
result = cost_method()
print(result)  ## Output: 49010.0

## You can also do this in one step
print(s.cost())  ## Output: 49010.0

En el código anterior, primero importamos la clase Stock y creamos una instancia de ella. Luego accedemos al método cost del objeto s sin llamarlo realmente. Esto nos da un método enlazado. Cuando llamamos a este método enlazado, calcula el costo basado en los datos del objeto. También puedes llamar directamente al método en el objeto en un solo paso.

Usando getattr() con métodos

Otra forma de acceder a métodos es utilizando la función getattr(). Esta función te permite obtener un atributo de un objeto por su nombre.

## Get the cost method using getattr
cost_method = getattr(s, 'cost')
print(cost_method)  ## Output: <bound method Stock.cost of <stock.Stock object at 0x...>>

## Call the method
result = cost_method()
print(result)  ## Output: 49010.0

## Get and call in one step
result = getattr(s, 'cost')()
print(result)  ## Output: 49010.0

Aquí, usamos getattr() para obtener el método cost del objeto s. Al igual que antes, podemos llamar al método enlazado para obtener el resultado. E incluso puedes obtener y llamar al método en una sola línea.

El método enlazado y su objeto

Un método enlazado siempre mantiene una referencia al objeto desde el que se accedió. Esto significa que incluso si almacenas el método en una variable, todavía sabe a qué objeto pertenece y puede acceder a los datos del objeto.

## Store the cost method in a variable
c = s.cost

## Call the method
print(c())  ## Output: 49010.0

## Change the object's state
s.shares = 75

## Call the method again - it sees the updated state
print(c())  ## Output: 36757.5

En este ejemplo, almacenamos el método cost en una variable c. Cuando llamamos a c(), calcula el costo basado en los datos actuales del objeto. Luego cambiamos el atributo shares del objeto s. Cuando llamamos a c() de nuevo, utiliza los datos actualizados para calcular el nuevo costo.

Explorando los internos del método enlazado

Un método enlazado tiene dos atributos importantes que nos dan más información sobre él.

  • __self__: Este atributo se refiere al objeto al que está enlazado el método.
  • __func__: Este atributo es el objeto función real que representa el método.
## Get the cost method
c = s.cost

## Examine the bound method attributes
print(c.__self__)  ## Output: <stock.Stock object at 0x...>
print(c.__func__)  ## Output: <function Stock.cost at 0x...>

## You can manually call the function with the object
print(c.__func__(c.__self__))  ## Output: 36757.5 (same as c())

Aquí, accedemos a los atributos __self__ y __func__ del método enlazado c. Podemos ver que __self__ es el objeto s, y __func__ es la función cost. Incluso podemos llamar manualmente a la función pasando el objeto como argumento, y nos da el mismo resultado que llamar directamente al método enlazado.

Ejemplo con un método que toma argumentos

Veamos cómo funcionan los métodos enlazados con un método que toma argumentos, como el método sell().

## Get the sell method
sell_method = s.sell

## Examine the method
print(sell_method)  ## Output: <bound method Stock.sell of <stock.Stock object at 0x...>>

## Call the method with an argument
sell_method(25)
print(s.shares)  ## Output: 50

## Call the method manually using __func__ and __self__
sell_method.__func__(sell_method.__self__, 10)
print(s.shares)  ## Output: 40

En este ejemplo, obtenemos el método sell como un método enlazado. Cuando lo llamamos con un argumento, actualiza el atributo shares del objeto s. También podemos llamar manualmente al método utilizando los atributos __func__ y __self__, pasando el argumento también.

Comprender los métodos enlazados te ayuda a entender cómo funciona el sistema de objetos de Python por debajo, lo que puede ser útil para la depuración, la metaprogramación y la creación de patrones de programación avanzados.

Resumen

En este laboratorio, has aprendido sobre el sistema de acceso a atributos de Python y sus mecanismos subyacentes. Ahora sabes cómo acceder a los atributos de los objetos utilizando la notación de punto y funciones como getattr(), setattr(), delattr() y hasattr(). Además, comprendes cómo usar getattr() para el procesamiento genérico y flexible de objetos y cómo crear un formateador de tablas para cualquier colección de objetos.

También has comprendido el concepto de métodos enlazados y cómo mantienen una conexión con sus objetos. Estos conceptos fundamentales son cruciales para técnicas avanzadas de programación en Python, como la introspección, la reflexión y la metaprogramación. Comprender el acceso a atributos te permite escribir código más flexible y potente que puede manejar varios tipos de objetos.