Introdução aos Decoradores de Métodos Embutidos

Beginner

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

Introdução

Esta seção discute alguns decoradores (decorators) embutidos que são usados em combinação com definições de métodos.

Decoradores Predefinidos

Existem decoradores (decorators) predefinidos usados para especificar tipos especiais de métodos em definições de classe.

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

    @staticmethod
    def spam(a):
        ...

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

    @property
    def name(self):
        ...

Vamos analisar cada um deles.

Métodos Estáticos

@staticmethod é usado para definir os chamados métodos de classe estáticos (do C++/Java). Um método estático é uma função que faz parte da classe, mas que não opera em instâncias.

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

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

Métodos estáticos são, por vezes, usados para implementar código de suporte interno para uma classe. Por exemplo, código para ajudar a gerenciar instâncias criadas (gerenciamento de memória, recursos do sistema, persistência, bloqueio, etc.). Eles também são usados por certos padrões de projeto (não discutidos aqui).

Métodos de Classe

@classmethod é usado para definir métodos de classe. Um método de classe é um método que recebe o objeto classe como o primeiro parâmetro, em vez da instância.

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

>>> f = Foo()
>>> f.bar()
<__main__.Foo object at 0x971690>   ## The instance `f`
>>> Foo.spam()
<class '__main__.Foo'>              ## The class `Foo`
>>>

Métodos de classe são frequentemente usados como uma ferramenta para definir construtores alternativos.

class Date:
    def __init__(self,year,month,day):
        self.year = year
        self.month = month
        self.day = day
    @classmethod
    def today(cls):
        ## Notice how the class is passed as an argument
        tm = time.localtime()
        ## And used to create a new instance
        return cls(tm.tm_year, tm.tm_mon, tm.tm_mday)

d = Date.today()

Métodos de classe resolvem alguns problemas complexos com recursos como herança.

class Date:
    ...
    @classmethod
    def today(cls):
        ## Gets the correct class (e.g. `NewDate`)
        tm = time.localtime()
        return cls(tm.tm_year, tm.tm_mon, tm.tm_mday)

class NewDate(Date):
    ...

d = NewDate.today()

Exercício 7.11: Métodos de Classe na Prática

Nos seus arquivos report.py e portfolio.py, a criação de um objeto Portfolio é um pouco confusa. Por exemplo, o programa report.py tem um código como este:

def read_portfolio(filename, **opts):
    '''
    Read a stock portfolio file into a list of dictionaries with keys
    name, shares, and 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)

e o arquivo portfolio.py define Portfolio() com um inicializador estranho como este:

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

Francamente, a cadeia de responsabilidade é um pouco confusa porque o código está disperso. Se uma classe Portfolio deve conter uma lista de instâncias Stock, talvez você devesse mudar a classe para ser um pouco mais clara. Assim:

## 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)
    ...

Se você quiser ler um portfólio de um arquivo CSV, talvez devesse criar um método de classe para isso:

## 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 nova classe Portfolio, você pode agora escrever um código como este:

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

Faça estas alterações na classe Portfolio e modifique o código report.py para usar o método de classe.

Resumo

Parabéns! Você completou o laboratório de Métodos Decorados. Você pode praticar mais laboratórios no LabEx para aprimorar suas habilidades.