Introducción a los decoradores de métodos integrados

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

Esta sección discute algunos decoradores integrados que se utilizan en combinación con las definiciones de métodos.

Decoradores predefinidos

Hay decoradores predefinidos que se utilizan para especificar tipos especiales de métodos en las definiciones de clases.

class Foo:
    def bar(self,a):
     ...

    @staticmethod
    def spam(a):
     ...

    @classmethod
    def grok(cls,a):
     ...

    @property
    def name(self):
     ...

Vamos a ver uno por uno.

Métodos estáticos

@staticmethod se utiliza para definir los llamados métodos estáticos de una clase (del C++/Java). Un método estático es una función que es parte de la clase, pero que no opera sobre instancias.

class Foo(object):
    @staticmethod
    def bar(x):
        print('x =', x)

>>> Foo.bar(2) ## x=2
>>>

Los métodos estáticos a veces se utilizan para implementar el código de soporte interno para una clase. Por ejemplo, código para ayudar a administrar las instancias creadas (gestión de memoria, recursos del sistema, persistencia, bloqueo, etc.). También se utilizan en ciertos patrones de diseño (no se discuten aquí).

Métodos de clase

@classmethod se utiliza para definir métodos de clase. Un método de clase es un método que recibe el objeto de la clase como primer parámetro en lugar de la instancia.

class Foo:
    def bar(self):
        print(self)
    @classmethod
    def spam(cls):
        print(cls)

>>> f = Foo()
>>> f.bar()
<__main__.Foo object at 0x971690>   ## La instancia `f`
>>> Foo.spam()
<class '__main__.Foo'>              ## La clase `Foo`
>>>

Los métodos de clase se utilizan con más frecuencia como una herramienta para definir constructores alternativos.

class Date:
    def __init__(self,year,month,day):
        self.year = year
        self.month = month
        self.day = day
    @classmethod
    def today(cls):
        ## Observe cómo la clase se pasa como argumento
        tm = time.localtime()
        ## Y se utiliza para crear una nueva instancia
        return cls(tm.tm_year, tm.tm_mon, tm.tm_mday)

d = Date.today()

Los métodos de clase resuelven algunos problemas complicados con características como la herencia.

class Date:
 ...
    @classmethod
    def today(cls):
        ## Obtiene la clase correcta (por ejemplo, `NewDate`)
        tm = time.localtime()
        return cls(tm.tm_year, tm.tm_mon, tm.tm_mday)

class NewDate(Date):
 ...

d = NewDate.today()

Ejercicio 7.11: Métodos de clase en la práctica

En sus archivos report.py y portfolio.py, la creación de un objeto Portfolio está un poco confusa. Por ejemplo, el programa report.py tiene código como este:

def read_portfolio(filename, **opts):
    '''
    Lee un archivo de cartera de acciones en una lista de diccionarios con claves
    name, shares y price.
    '''
    with open(filename) as lines:
        portdicts = fileparse.parse_csv(lines,
                                        select=['name','shares','price'],
                                        types=[str,int,float],
                                        **opts)

    portfolio = [ Stock(**d) for d in portdicts ]
    return Portfolio(portfolio)

y el archivo portfolio.py define Portfolio() con un inicializador extraño como este:

class Portfolio:
    def __init__(self, holdings):
        self._holdings = holdings
  ...

Frankmente, la cadena de responsabilidad es un poco confusa porque el código está disperso. Si una clase Portfolio está supuesta a contener una lista de instancias de Stock, quizás debería cambiar la clase para que sea un poco más clara. Así:

## portfolio.py

import stock

class Portfolio:
    def __init__(self):
        self._holdings = []

    def append(self, holding):
        if not isinstance(holding, stock.Stock):
            raise TypeError('Expected a Stock instance')
        self._holdings.append(holding)
  ...

Si desea leer una cartera desde un archivo CSV, quizás debería crear un método de clase para ello:

## portfolio.py

import fileparse
import stock

class Portfolio:
    def __init__(self):
        self._holdings = []

    def append(self, holding):
        if not isinstance(holding, stock.Stock):
            raise TypeError('Expected a Stock instance')
        self._holdings.append(holding)

    @classmethod
    def from_csv(cls, lines, **opts):
        self = cls()
        portdicts = fileparse.parse_csv(lines,
                                        select=['name','shares','price'],
                                        types=[str,int,float],
                                        **opts)

        for d in portdicts:
            self.append(stock.Stock(**d))

        return self

Para usar esta nueva clase Portfolio, ahora puede escribir código como este:

>>> from portfolio import Portfolio
>>> with open('portfolio.csv') as lines:
...     port = Portfolio.from_csv(lines)
...
>>>

Haga estos cambios a la clase Portfolio y modifique el código de report.py para usar el método de clase.

✨ Revisar Solución y Practicar

Resumen

¡Felicitaciones! Has completado el laboratorio de Métodos Decorados. Puedes practicar más laboratorios en LabEx para mejorar tus habilidades.