PROJET AUTOBLOG


Sam & Max: Python, Django, Git et du cul

Site original : Sam & Max: Python, Django, Git et du cul

⇐ retour index

Mise à jour

Mise à jour de la base de données, veuillez patienter...

Créer un décorateur à la volée

vendredi 12 juillet 2013 à 07:39

Sur les décorateurs, normalement, vous avez tout ce qu’il faut pour être au point.

Néanmoins en informatique la moitié de la connaissance, c’est l’usage, pas la fonctionnalité. Car il y beaucoup d’usages auxquels on ne pense pas.

Particulièrement, je vous avais déjà expliqué que les fonctions étaient des objets comme les autres en Python, et qu’on pouvait donc les créer à la volée, les retourner, les passer en paramètre, et même leur coller des attributs.

Or les décorateurs ne sont jamais que des fonctions.

Maintenant, souvenez vous, le décorateurs property permet de faire ceci :

class Mamouth(object):
 
    _valeur = "3 calots"
 
    @property
    def valeur(self):
        return self._valeur.upper()
 
    @valeur.setter
    def valeur(self, valeur):
        self._valeur = valeur.strip()
 
>>> bille = Mamouth()
>>> bille.valeur
u'3 CALOTS'
>>> bille.valeur = "une pépite           "
>>> bille.valeur
>>> print(bille.valeur)
UNE PÉPITE

La syntaxe qui doit attirer votre attention est @valeur.setter. En effet, d’où vient ce décorateur ?

On comprend mieux ce qui s’est passé avec ce test :

>>> Mamouth.valeur.setter
<built-in method setter of property object at 0x1a1baf8>

setter est tout simplement un attribut de la méthode valeur. Par ailleurs, c’est une fonction et un décorateur.

Pourquoi faire cela ? Et bien tout simplement parce que cela permet d’attacher une fonction qui ne sert que dans un cas (ici ça ne sert qu’à créer le setter de la propriété valeur) à son contexte.

Bien entendu vous pouvez faire ça vous même, il suffit de le vouloir très fort et de croire en le pouvoir de l’amour.

Par exemple, imaginez un décorateur qui permet d’attacher un comportement de sérialisation à une fonction. On ne veut pas modifier la fonction, mais on veut qu’elle puisse automatiquement, pour quelques caractères de plus, pouvoir aussi retourner du JSON ou du pickle.

import json
import pickle
 
def serializable(func):
 
    # Contrairement à la plupart des décorateurs, on ne va pas retourner
    # un wrapper, mais bien la fonction originale. Simplement on lui aura ajouté
    # des attributs
 
    func.as_json = lambda *a, **k: json.dumps(func(*a, **k))
    func.as_pickle = lambda *a, **k: pickle.dumps(func(*a, **k))
 
    return func

Et ça s’utilise ainsi :

import locale
 
from calendar import TimeEncoding, day_name, day_abbr
 
# obtenir les noms de jours localisés est complètement rocambolesque en python
def get_day_name(day_number, locale, short=False):
    """
        Retourne le nom d'un jour dans la locale sélectionnée.
 
        Exemple :
 
        >>> get_day_name(0,  ('fr_FR', 'UTF-8'))
        'lundi'
    """
    with TimeEncoding(locale) as encoding:
        s = day_abbr[day_number] if short else day_name[day_number]
        return s.decode(encoding) if encoding is not None else s
 
@serializable
def get_days_names(locale=locale.getdefaultlocale(), short=False):
    """
        Un dictionnaire contenant un mapping entre les numéros des jours
        de semaine et leurs noms selon la locale donnée.
    """
 
    return {i: get_day_name(i, locale) for i in xrange(7)}

En usage ordinaire, la fonction retourne bien ce qui est prévu :

>>> get_days_names()
{0: 'lundi', 1: 'mardi', 2: 'mercredi', 3: 'jeudi', 4: 'vendredi', 5: 'samedi', 6: 'dimanche'}
>>> get_days_names(locale=('en_US', 'UTF-8'))
{0: 'Monday', 1: 'Tuesday', 2: 'Wednesday', 3: 'Thursday', 4: 'Friday', 5: 'Saturday', 6: 'Sunday'}

Mais on peut choisir le format à la sortie :

>>> get_days_names.as_json()
'{"0": "lundi", "1": "mardi", "2": "mercredi", "3": "jeudi", "4": "vendredi", "5": "samedi", "6": "dimanche"}'
>>> get_days_names.as_pickle(locale=('en_US', 'UTF-8'))
"(dp0\nI0\nS'Monday'\np1\nsI1\nS'Tuesday'\np2\nsI2\nS'Wednesday'\np3\nsI3\nS'Thursday'\np4\nsI4\nS'Friday'\np5\nsI5\nS'Saturday'\np6\nsI6\nS'Sunday'\np7\ns."

Ici, on a attacher une fonction à une autre fonction, en mettant la deuxième dans un attribut de la première.

Comme les décorateurs sont des fonctions, rien ne vous empêche de faire pareil avec un décorateur, et c’est de cette manière que @property attache un décorateur setter à chaque méthode.



Télécharger le code des articles

flattr this!

Error happened! 0 - count(): Argument #1 ($value) must be of type Countable|array, null given In: /var/www/ecirtam.net/autoblogs/autoblogs/autoblog.php:428 http://ecirtam.net/autoblogs/autoblogs/sametmaxcom_a844ada43a979e3b1395ab9acb6afafb84340999/?Cr%C3%A9er-un-d%C3%A9corateur-%C3%A0-la-vol%C3%A9e #0 /var/www/ecirtam.net/autoblogs/autoblogs/autoblog.php(999): VroumVroum_Blog->update() #1 /var/www/ecirtam.net/autoblogs/autoblogs/sametmaxcom_a844ada43a979e3b1395ab9acb6afafb84340999/index.php(1): require_once('...') #2 {main}