2017-05-17 2 views
2

Je m'enseigne moi-même à coder à l'aide de tutoriels en ligne. J'ai rencontré des «décorateurs», je peux sembler comprendre comment cela fonctionne mais quelque chose me dérange. Voici le code donné:Décorateurs, fonctions d'appel sans parenthèses?

def decor(func): 
    def wrap(): 
     print("-----------") 
     func() 
     print("-----------") 
    return wrap 

def print_text(): 
    print("Hello World") 

decorated = decor(print_text) 
decorated() 

output: 
----------- 
Hello World 
----------- 

Les choses que je veux comprendre sont:

  • Pourquoi avez-vous appeler « enveloppe de retour » au lieu de « wrap de retour() »? Alors que si vous ne le faites pas, vous obtiendrez un objet "TypeError: 'NoneType" qui n'est pas appelable

  • Lorsque j'ai affecté la valeur de la variable décorée, pourquoi ai-je dû utiliser "print_text" plutôt que "print_text"() "alors que cela provoquera le même TypeError si je le fais?

  • Lorsque j'ai utilisé la variable" décoré ", pourquoi ai-je dû l'appeler comme une fonction (ajouter() à la fin). appeler à l'aide « print (décoré) » « décorée » ou il dit quelque chose de complètement différent

    ?

Désolé pour les questions idiotes. Mais je ne fais que commencer alors s'il vous plaît ours avec moi. Aussi s'il vous plaît faites vos réponses pour les débutants. Merci

Répondre

7

En Python, à peu près tout est un objet. Les fonctions sont objets aussi. Vous pouvez les référencer par leur nom:

>>> def print_text(): 
...  print("Hello World") 
... 
>>> print_text # **no** call here, this is just referencing the object 
<function print_text at 0x10e3f1c80> 
>>> print_text() # With a call, so now we *run* the function 
Hello World 

Ajout () au nom dit Python pour appeler la fonction, qui l'a fait exécuter réellement le corps de la fonction, sans appel, il est juste de vous montrer ce que les références de nom .

Vous pouvez également affecter des objets fonction à d'autres noms. Ces autres noms peuvent encore être appelés, en invoquant la fonction:

>>> other_name = print_text 
>>> other_name 
<function print_text at 0x10e3f1c80> 
>>> other_name() 
Hello World 

Alors other_name est juste une autre référence au même objet, et en ajoutant () (une expression d'appel) provoque l'objet de fonction à exécuter. print_text() et other_name() faire exactement la même chose, exécutez le code à l'intérieur de la fonction.

C'est ce que le nom func à l'intérieur de decor() fait référence à; c'est une référence au même objet de fonction. Vous l'avez transmis avec decor(print_text). Plus tard, à l'intérieur de wrapper(), l'expression func() appelle cet objet fonction. Si vous avez passé à la place print_text(), vous passerais dans l'objet None cette fonction est revenu, et None ne peut pas être appelé:

>>> return_value = print_text() 
Hello World 
>>> return_value is None 
True 
>>> return_value() 
Traceback (most recent call last): 
    File "<stdin>", line 1, in <module> 
TypeError: 'NoneType' object is not callable 

Ensuite, return wrapper retourne le nouvel objet de fonction wrapper à l'appelant. Si vous l'avez fait return wrapper(), vous obtiendrez le résultat de la fonction, pas l'objet fonction lui-même.Le but d'un décorateur est de remplacer l'objet fonction original par un nouvel objet qui fait des choses supplémentaires, c'est pourquoi un décorateur retourne cet objet de remplacement (dans votre exemple wrapper) de sorte qu'à l'avenir quand vous appelez decorated(), vous appelez cette fonction wrapper en faisant quelque chose supplémentaire avant et après avoir appelé la fonction d'origine (via le nom func, qui référence print_text()).

Alors qu'est-ce decor(some_function) fait est un retour nouvel objet fonction, qui va imprimer quelque chose, appelez l'objet de la fonction qui a été transmis, puis imprimer autre chose. Ce nouvel objet fonction peut ensuite être utilisé pour remplacer l'ancien objet fonction.