python装饰器为什么要双层嵌套函数
PHPz
PHPz 2017-04-18 09:41:45
0
6
840

今天学习了一下python的装饰器,比较难理解的是大家举最简单的装饰例子时都是双层嵌套:

但是单层函数,不也能实现装饰功能吗?python把装饰器设置成双层的目的是什么呢?@到底代表什么运作机制。

PHPz
PHPz

学习是最好的投资!

répondre à tous(6)
刘奇

Comme mentionné par plusieurs experts ci-dessus, la fonction de décorateur (@grammar) est :

@foo
def bar():
    ...

équivaut à :

bar = foo(bar)

traduit en chinois est :

Utilisez la fonction représentée par @ comme argument pour appeler la fonction @, et attribuez la valeur au nom de la fonction @

représenté par la fonction

Parce que cette action est très similaire à la décoration (modification, agrandissement, ajustement, restriction...) originale bar, on l'appelle donc décorateur :

Par définition, un décorateur est une fonction qui prend une autre fonction et étend le comportement de cette dernière sans la modifier explicitement.

Bien que

soit considéré comme une décoration, le bar exécuté n'est plus la même personne que l'original bar renverra un tout nouvel objet, généralement une fonction, mais il est très probable que le foo soit une décoration. la chose renvoyée n'est rien du tout. Pas même une fonction :

def foo(func):
    return None

@foo
def bar():
    ...

Dans l'exemple ci-dessus, foo est devenu un décorateur très étrange, car ce qu'il renvoie est None, qui non seulement n'a pas de décoration, mais détruit également la fonction

Retour à l'exemple que vous avez donné au début :

def foo(func):
    print('foo')
    func()

@foo
def func_a():
    print('func_a')

func_a()

Votre foo n'a pas de déclaration return, ce qui signifie que foo reviendra None Ce que vous avez écrit est fondamentalement un destructeur (je plaisante). illusion. C'est parce qu'au moment où la syntaxe @ entre en jeu, la syntaxe print est exécutée

Mais vous pouvez essayer d'appeler func_a, vous constaterez qu'une erreur est générée, car func_a n'est pas du tout une fonction, et bien sûr l'effet que vous souhaitez obtenir ne peut pas être réutilisé


Les décorateurs ne doivent pas nécessairement utiliser des fonctions locales ou des fonctions imbriquées, mais nous laissons généralement le décorateur renvoyer une fonction. Je pense que c'est un point très important. Après tout, nous pensons tous intuitivement aux fonctions décorées.

Il existe des milliers de décorateurs, et certains peuvent en effet utiliser une seule couche d'imbrication, comme l'enregistrement de fonctions :

registry = []

def register(func):
    print('register {}'.format(func))
    registry.append(func)
    return func  # 還是應該要 return func

La raison est que cette action ne doit être traitée qu'une seule fois lors de la décoration. Vous ne voulez pas l'enregistrer à chaque fois que vous appelez la fonction. Tout ce dont nous avons besoin est la fonction d'origine.

Mais pour des actions comme imprimer le journal ou calculer le temps etc., nous devons toujours utiliser des techniques d'imbrication, car nous voulons imprimer et calculer à chaque fois que nous appelons une fonction. need est une nouvelle fonction. Cette fonction est créée par une fonction locale, qui produira inévitablement une cascade et une imbrication. Il existe également des décorateurs plus complexes avec des paramètres qui peuvent utiliser plus de deux niveaux d'imbrication

.

Résumé

Que vous souhaitiez emboîter ou non dépend du but recherché, mais n'oubliez pas de restituer la fonction après la décoration


Questions auxquelles j'ai répondu : Python-QA

迷茫

Peut-être que vous ne comprenez pas ce que je veux dire. Mon idée est assez confuse. Ma question est de savoir pourquoi Python est conçu comme ça, car au début, je pensais qu'il était inutile d'imbriquer deux couches de fonctions et une. une couche de fonctions suffit. Imprimer un journal, l'heure, etc. Ce n'est pas la philosophie de python. J'y ai réfléchi pendant une nuit et je l'ai compris moi-même. Les amis dans le besoin peuvent y jeter un œil et s'émerveiller. à la merveille de ce modèle de conception. La méthode de raisonnement inverse utilisée consiste à clarifier les exigences de base puis à raisonner à rebours pour comprendre pourquoi chaque étape est écrite comme telle.

def outer(func):          #3.此处关键了,因为我们是倒推,下面的结构已经固定了,
    def inner(x):         #outer(f1)返回给f1的值,必须是一个函数!outer自己也是函数
        print('loding')   #可以返回自己嘛!但是注意哦,它已经有且必须有一个func参数,来传递原生函数名
        func(x)        #也就是f1函数名变量的入口,那么他返回给新的f1函数,就会使原生函数多个参数,
        print('Done')     #改变我们最初的目的,新f1与老f1调用方法要无区别,那么咋整
    return inner          #嵌套一个函数inner,他接收f1参数,外层outer接收函数名f1
def f1(x):                #至此装饰器是两层函数嵌套,当f1没有参数时,依然需要双层
    print('f1 func',x)    #因为必须返回一个函数,返回outer本身,就需要加func参数
@outer                    #而我们又追求不改变原生调用f1(),他是没有参数的
def f2(x):                #所以一个装饰器必须至少双层函数嵌套,第一层传递原生函数名
    print('f2 func',x)    #第二层执行装饰功能,这设计真是牛逼,逆推一晚上才有点理解。
@outer                   
def f3(x):
    print('f3 func',x)
                          #2.那么F1要指向一个新函数
                          #并且这个函数能被F2 F3都指向
f1  = outer(f1)              #所以它是一个可以传递函数名变量的函数
#f1 = outer(f1('tings'))  #ps:带上参数一起传递,这也是一种可能,但是没有价值,
                          #装饰器使用时每个都要写一遍,其实就已经不是原生方法调用了,
                          #因为要给语法糖@输送'things'参数,不符合我们初衷。
                          
f1('tings1')              #1.首先明确我们的根本需求,外部调用方法要完全相同,这也是装饰器的意义。
f2('tings2')
f3('tings3')
Ty80
    Le décorateur
  1. arrive dans la phase 定义 au lieu de la phase 执行

  2. .
  3. La fonction décorateur outer doit renvoyer un 被装饰的函数, notez qu'elle doit renvoyer un 定义, pas 调用

L'explication détaillée en bas est très bonne.

刘奇

Beaucoup de personnes ont dû partager cela. J'ai également déjà écrit un blog. Si le sujet est intéressant, vous pouvez le consulter :

Explication détaillée des décorateurs Python

PHPzhong
@foo
def bar()

Équivalent à bar = foo(bar), comprenez simplement ceci

PHPzhong

Le deuxième étage est à droite, la fonction est déjà exécutée lors de l'attribution d'une valeur à la barre.

Derniers téléchargements
Plus>
effets Web
Code source du site Web
Matériel du site Web
Modèle frontal