À propos des décorateurs Python

Cette fois, je vais parler des décorateurs Python.

Introduction.

En étudiant avec la programmation Python experte [^ expert_python], le concept de décorateurs Python est apparu. Je n'ai pas compris ce que je disais dans le contenu du livre, alors j'ai fait beaucoup de recherches.

[^ expart_python]: la 2e édition de la programmation Expert Python révisée a été publiée le 26 février 2018. Pour ceux qui étudient désormais, la 2ème édition est recommandée.

Qu'est-ce qu'un décorateur?

Décorer est une modification. Cependant, un décorateur est simplement une fonction pour modifier une certaine fonction et son mécanisme. Par exemple, supposons que vous ayez une fonction. Jetons un coup d'œil à la fonction test () suivante et à son script d'exécution.

Exemple de script 1

test.py


def test():
    print('Hello Decorator')

test()

Le résultat de l'exécution est le suivant.

Hello Decorator

Eh bien, c'est naturel.

Nous décorerons cette fonction de test.

Un exemple simple de décorateur

Voici ce que le script ci-dessus est décoré avec un décorateur:

sample_deco1.py


def deco(func):
    def wrapper(*args, **kwargs):
        print('--start--')
        func(*args, **kwargs)
        print('--end--')
    return wrapper

@deco
def test():
    print('Hello Decorator')

test()

Le résultat de l'exécution est le suivant.

--start--
Hello Decorator
--end--

Voyons maintenant ce que cela se passe.

Une brève description de Nakami

La 1ère ligne def deco (func): à la 6ème ligne return wrapper est la fonction de définition du décorateur pour la décoration. Décorez selon cette fonction. La 8ème ligne @ deco décore la fonction suivante avec la fonction de définition du décorateur appelée" deco ". Ce sera.

Parlons maintenant du contenu de la fonction de définition du décorateur. L'important est la 3ème ligne print ('--start -') à la 5ème ligne print ('--end -').

Ligne 1 def deco (func): est la déclaration de la fonction décoratrice. Bien que func soit spécifié comme argument, la fonction décorée test () elle-même est passée comme argument.

Ligne 2 def wrapper (* args, ** kwargs): est une fonction pour écrire le contenu du décorateur actuel. Si vous ne comprenez pas bien, continuons tout en saisissant ce genre de chose. J'expliquerai en détail plus tard.

Maintenant, les 3ème à 5ème lignes importantes. La quatrième ligne, func (* args, ** kwargs), exécute la fonction passée en argument. Dans la 3ème ligne print ('--start -') et la 6ème ligne print ('--end -'), la 5ème ligne func (* args, ** kwargs) Indique le traitement à exécuter avant et après.

Ainsi, * args, ** kwargs sont les arguments de la fonction à décorer. Eh bien, ce n'est pas grave si vous écrivez une fonction comme celle-ci sans y penser (peut-être)

Alors, que pouvez-vous faire avec un décorateur?

En utilisant le décorateur, vous pouvez ajouter vous-même le traitement avant et après le traitement de la fonction existante. J'ai pensé à quel genre de situation faire une telle chose. Je n'ai moi-même pas encore beaucoup appris à ce sujet, mais en utilisant un décorateur, je peux ajouter un processus qui s'exécute automatiquement lorsqu'une fonction fournie en tant que bibliothèque est appelée. Je vais.

En Python, il est possible de réécrire la fonction elle-même sous la forme d'un remplacement utilisé en Java etc. Cependant, il n'y a pas de problème avec le traitement existant, mais je souhaite également effectuer un traitement différent en même temps. Je pense que le décorateur est utilisé dans une telle situation.

Lors de mes recherches, j'ai découvert un programme qui utilise un décorateur pour évaluer une certaine fonction.

Eh bien, une fois que vous avez créé une fonction de décorateur pour le repère, vous pouvez mesurer chaque repère simplement en décorant diverses fonctions avec @. C'est pratique.

Exemple de décorateur dans une méthode avec une valeur de retour

Dans l'exemple ci-dessus, la méthode décorée n'avait aucune valeur de retour. Mais, bien sûr, les méthodes ont parfois une valeur de retour. Voici un exemple de décorateur pour une méthode qui a une valeur de retour.

sample_deco2.py


def deco2(func):
    import os
    def wrapper(*args,**kwargs):
        res = '--start--' + os.linesep
        res += func(*args,**kwargs) + '!' + os.linesep
        res += '--end--'
        return res
    return wrapper

@deco2
def test2():
    return('Hello Decorator')

print(test2())

Le résultat de l'exécution de ce script est le suivant.

--start--
Hello Decorator!
--end--

Ah! ?? Le résultat de l'exécution n'a pas changé! !! Quoi? Non, cela a légèrement changé. Une partie de la sortie est passée de «Hello Decorator» à «Hello Decorator!». Le contenu du programme a considérablement changé. J'expliquerai ce programme en le comparant au programme précédent.

Premièrement, par rapport au programme précédent, la méthode décorée def test2 (): a maintenant une valeur de retour de chaîne return'Hello Decorator'. Et print (test2 ()) imprime la chaîne retournée.

Auparavant, la méthode à décorer était une méthode pour générer une chaîne de caractères, mais cette fois, la méthode à décorer est une méthode pour générer une chaîne de caractères. Si vous décorez une méthode qui produit quelque chose, vous devez écrire le décorateur pour que le résultat généré soit renvoyé.

Nous prendrons une place différente dans le contenu du décorateur. Ligne 5 res = func (* args, ** kwargs) + '!' Ligne 7 return res Ligne 8 return wrapper

Dans la 5ème ligne, func (** kwargs) est exécuté et '!' Est ajouté à la fin de la chaîne de caractères retournée et stocké dans res. La ligne 7 renvoie res. Il est retourné à la méthode qui est ensuite décorée avec le return wrapper à la ligne 8.

Qu'est-ce que ça veut dire

Les décorateurs vous permettent non seulement de décorer des méthodes qui renvoient des valeurs, mais ils vous permettent également de traiter les valeurs de retour des méthodes que vous décorez. C'est très intéressant. Si vous pensez qu'il est possible d'ajouter un processus spécifique au résultat de sortie du programme de décoration ...? Ce sera très amusant.

En outre, par exemple, les notations incluses dans des balises telles que html et xml peuvent être décrites par une méthode qui génère uniquement le contenu à l'aide d'un décorateur.

cette? Par exemple, si vous écrivez du html avec un décorateur, vous souhaitez l'imbriquer ... Oui. Les décorateurs peuvent également être imbriqués.

Décorateurs Nest

Il est également possible de combiner plusieurs décorateurs. Par exemple, la situation est la suivante.

deco_sample3.py


def deco_html(func):
    def wrapper(*args, **kwargs):
        res = '<html>'
        res = res + func(*args, **kwargs)
        res = res + '</html>'
        return res
    return wrapper

def deco_body(func):
    def wrapper(*args, **kwargs):
        res = '<body>'
        res = res + func(*args, **kwargs)
        res = res + '</body>'
        return res
    return wrapper

@deco_html
@deco_body
def test():
    return 'Hello Decorator'

print(test())

Le résultat de l'exécution est le suivant.

<html><body>Hello Decorator!</body></html>

Ce programme n'est pas difficile si vous connaissez les programmes précédents. Il n'y a que deux décorateurs qui décorent def test (str):. Il sera traité dans l'ordre de celui ci-dessous.

Ça devient assez amusant. Il semble que les décorateurs puissent faire diverses choses. cette? Au fait, n'est-il pas possible d'utiliser un décorateur pour une méthode qui a des arguments? Je peux le faire.

Décorez une méthode avec des arguments

Créez la décoration de la méthode avec l'argument comme suit.

deco_sample4.py


def deco_p(func):
    def wrapper(*args, **kwargs):
        res = '<p>'
        res = res + func(args[0], **kwargs)
        res = res + '</p>'
        return res
    return wrapper

@deco_p
def test(str):
    return str

print(test('Hello Decorator!'))

Le résultat de l'exécution est le suivant.

<p>Hello Decorator!</p>

Au fait, quelle est la différence avec le programme précédent? C'est func (args [0], ** kwargs). En fait, le décorateur est prêt à recevoir des arguments, que la méthode de décoration ait ou non des arguments. Si vous voulez utiliser un argument, prenez l'argument de ʻargs et passez-le à func`. Et ce qui est intéressant, c'est que «args» est un tapple.

Que signifie être un taple? Pour le moment, au moins la méthode intégrée len () peut être utilisée. Cela signifie que vous pouvez modifier le comportement du décorateur en fonction du nombre d'arguments. Je me demande si je peux le passer comme argument au décorateur en premier lieu ... Vous pouvez le passer.

Passez l'argument au décorateur

deco_sample5.py


def deco_tag(tag):
    def _deco_tag(func):
        def wrapper(*args, **kwargs):
            res = '<'+tag+'>'
            res = res + func(*args, **kwargs)
            res = res + '</'+tag+'>'
            return res
        return wrapper
    return _deco_tag

@deco_tag('html')
@deco_tag('body')
def test():
    return 'Hello Decorator!'

print(test())

Le résultat de sortie est le suivant.

<html><body>Hello Decorator!</body></html>

Je ne vais pas m'en lasser, mais ici je suis en train de nicher des décorateurs.

fin

Cette fois, j'ai essayé différents décorateurs et j'ai écrit le contenu ici. Les décorateurs sont intéressants. Je pense que je peux faire différentes choses. Par exemple, si vous combinez cela avec un générateur ... il semble que vous puissiez vraiment faire différentes choses. L'URL de référence que j'ai écrite au tout début a une explication plus détaillée du décorateur, donc si vous êtes intéressé, s'il vous plaît.

À bientôt.

Postscript 2018.7.6

J'ai écrit cet article en janvier 2015. Cela fait trois ans et demi qu'il a été publié, et je suis reconnaissant qu'il y ait encore des gens qui aiment l'article. Après trois ans et demi, j'ai également acquis une meilleure compréhension des décorateurs Python.

J'ajouterai une histoire qui peut être faite maintenant.

Un autre exemple

Je n'y ai pas pensé à l'époque, mais maintenant je vais vous montrer comment l'utiliser avec un décorateur. (Je ne sais pas si je vais vraiment le faire)

Par exemple, si vous disposez des modules suivants:

my_method.py


def my_method(*args, **kwargs):
    if 'test_key' in kwargs:
        print('test_La valeur de la clé est[{}]'.format(kwargs['test_key']))
    else:
        print('test_Il n'y a pas de valeur dans la clé.')

my_method()
my_method(test_key="Valeur pour les tests")

Le résultat de l'exécution du module est le suivant.

test_Il n'y a pas de valeur dans la clé.
test_La valeur de la clé est[Valeur pour les tests]

Vous pouvez changer le comportement d'un seul coup en utilisant le décorateur.

my_method_2.py


def my_decorator(target_function):
    def wrapper_function(*args, **kwargs):
        kwargs['test_key'] = 'Valeur réécrite par le décorateur'
        return target_function(*args, **kwargs)
    return wrapper_function

@my_decorator
def my_method(*args, **kwargs):
    if 'test_key' in kwargs:
        print('test_La valeur de la clé est[{}]'.format(kwargs['test_key']))
    else:
        print('test_Il n'y a pas de valeur dans la clé.')

my_method()
my_method(test_key="Valeur pour les tests")

Le résultat de l'exécution est le suivant.

test_La valeur de la clé est[Valeur réécrite par le décorateur]
test_La valeur de la clé est[Valeur réécrite par le décorateur]

Vous pouvez changer les arguments en même temps comme ceci. Qu'utilisez vous pour ça? Je n'ai pas un bon exemple pour répondre à cela, mais je pense qu'il est bon de savoir que vous pouvez également le faire.

Le décorateur est Syntax Sugar

Le décorateur est Syntax Sugar. Le sucre de syntaxe est une grammaire conçue pour faciliter l'écriture de programmes à certaines fins. Non limité à Python, il existe également des grammaires de sucre de syntaxe dans d'autres langues.

Être un sucre de syntaxe signifie qu'il existe une autre façon de l'écrire.

Par exemple, si vous disposez des modules suivants:

def twice(func):
    def wrapper(*args, **kwargs):
        return func(*args, **kwargs) * 2
    return wrapper

@twice
def add(x, y):
    return x + y

print(add(1, 3))

Le résultat de cette exécution est le même que celui de la prochaine exécution.

def twice(func):
    def wrapper(*args, **kwargs):
        return func(*args, **kwargs) * 2
    return wrapper

def add(x, y):
    return x + y

print(twice(add)(1, 3))

J'ai passé la fonction d'ajout à la fonction appelée deux fois et passé «1, 3» à la fonction qui est sortie et j'ai imprimé le résultat de l'exécution.

Il est difficile de comprendre ce que vous voulez faire si vous l'écrivez comme ça. C'est pourquoi je pense que le décorateur a été créé.

URL de référence

Recommended Posts

À propos de Python Decorator
À propos des décorateurs Python
À propos des tranches Python
À propos de la notation d'inclusion de python
À propos de Python tqdm.
À propos du rendement Python
À propos de python, classe
À propos de l'héritage Python
À propos de python, range ()
[Python] À propos du multi-processus
À propos de Python for loop
[Python] Mémo sur les fonctions
Résumé sur Python3 + OpenCV3
À propos de Python, pour ~ (plage)
[Python] Mémo sur les erreurs
À propos de l'environnement de développement Python
Python: à propos des arguments de fonction
Python, à propos de la gestion des exceptions
À propos de Python Pyramid Traversal
À propos de Python3 ... (objet Ellipsis)
[Python] Chapitre 01-01 À propos de Python (First Python)
[Python] À propos de l'entrée standard
À propos de __all__ en python
[Python] En savoir plus sur pip
Prise en charge de Fabric pour Python 3
Python
À propos des objets et des classes Python
À propos des variables et des objets Python
À propos du module Python venv
fonction de mémorandum python pour débutant
À propos de la fonction enumerate (python)
À propos de divers encodages de Python 3
À propos de Python, len () et randint ()
À propos de Perl, Python, PHP, Ruby
À propos de la date et du fuseau horaire Python
Mémorandum sur la corrélation [Python]
Un mémorandum sur le simulacre de Python
À propos des opérateurs de comparaison de chaînes Python
À propos de Python et des expressions régulières
À propos des fonctionnalités de Python
À propos de "for _ in range ():" de python
À propos des opérations Python et OS
Python # À propos de la référence et de la copie
À propos de Python sort () et reverse ()
Une note sur [python] __debug__
Initialisation de variables globales à l'aide de décorateurs Python
Remarque Python: à propos de la comparaison en utilisant is
À propos de l'installation des séries Pwntools et Python2
Python: une note sur les classes 1 "Résumé"
[Python] Écrivons brièvement la notation d'inclusion
À propos de Python dict et des fonctions triées
[Python] Qu'est-ce que @? (À propos des décorateurs)
Ce qui était surprenant dans les classes Python
[Python] Que sont @classmethod et les décorateurs?
[Python] À propos des classes Executor et Future
À propos de Python, à partir et à l'importation, comme
J'ai essayé d'étudier le processus avec Python