Présentation des décorateurs de méthodes intégrés

PythonPythonBeginner
Pratiquer maintenant

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

💡 Ce tutoriel est traduit par l'IA à partir de la version anglaise. Pour voir la version originale, vous pouvez cliquer ici

Introduction

Cette section traite de quelques décorateurs intégrés utilisés en combinaison avec les définitions de méthodes.

Décorateurs prédéfinis

Il existe des décorateurs prédéfinis utilisés pour spécifier des types spéciaux de méthodes dans les définitions de classes.

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

    @staticmethod
    def spam(a):
     ...

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

    @property
    def name(self):
     ...

Allons-y un par un.

Méthodes statiques

@staticmethod est utilisé pour définir une soi-disant méthode de classe statique (du C++/Java). Une méthode statique est une fonction qui fait partie de la classe, mais qui ne fonctionne pas sur les instances.

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

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

Les méthodes statiques sont parfois utilisées pour implémenter le code d'assistance interne pour une classe. Par exemple, le code pour aider à gérer les instances créées (gestion de la mémoire, ressources système, persistance, verrouillage, etc.). Elles sont également utilisées par certains patrons de conception (non discutés ici).

Méthodes de classe

@classmethod est utilisé pour définir des méthodes de classe. Une méthode de classe est une méthode qui reçoit l'objet classe en tant que premier paramètre au lieu de l'instance.

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

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

Les méthodes de classe sont le plus souvent utilisées comme outil pour définir des constructeurs alternatifs.

class Date:
    def __init__(self,year,month,day):
        self.year = year
        self.month = month
        self.day = day
    @classmethod
    def today(cls):
        ## Remarquez comment la classe est passée en tant qu'argument
        tm = time.localtime()
        ## Et utilisée pour créer une nouvelle instance
        return cls(tm.tm_year, tm.tm_mon, tm.tm_mday)

d = Date.today()

Les méthodes de classe résolvent certains problèmes complexes liés aux fonctionnalités telles que l'héritage.

class Date:
  ...
    @classmethod
    def today(cls):
        ## Obtient la bonne classe (par exemple `NewDate`)
        tm = time.localtime()
        return cls(tm.tm_year, tm.tm_mon, tm.tm_mday)

class NewDate(Date):
  ...

d = NewDate.today()

Exercice 7.11 : Utilisation des méthodes de classe

Dans vos fichiers report.py et portfolio.py, la création d'un objet Portfolio est un peu embrouillée. Par exemple, le programme report.py a du code comme ceci :

def read_portfolio(filename, **opts):
    '''
    Lit un fichier de portefeuille d'actions dans une liste de dictionnaires avec les clés
    name, shares et 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)

et le fichier portfolio.py définit Portfolio() avec un initialisateur étrange comme ceci :

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

Franchement, la chaîne de responsabilité est un peu confuse car le code est dispersé. Si une classe Portfolio est supposée contenir une liste d'instances Stock, peut-être devriez-vous modifier la classe pour qu'elle soit un peu plus claire. Comme ceci :

## 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 vous voulez lire un portefeuille à partir d'un fichier CSV, peut-être devriez-vous en faire une méthode de classe :

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

Pour utiliser cette nouvelle classe Portfolio, vous pouvez maintenant écrire du code comme ceci :

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

Apportez ces modifications à la classe Portfolio et modifiez le code de report.py pour utiliser la méthode de classe.

✨ Vérifier la solution et pratiquer

Sommaire

Félicitations ! Vous avez terminé le laboratoire sur les méthodes décorées. Vous pouvez pratiquer d'autres laboratoires sur LabEx pour améliorer vos compétences.