Maison > développement back-end > Tutoriel Python > Exemple de partage de code qui vous permet d'avoir une compréhension approfondie des décorateurs Python

Exemple de partage de code qui vous permet d'avoir une compréhension approfondie des décorateurs Python

黄舟
Libérer: 2017-06-18 11:33:37
original
1396 Les gens l'ont consulté

Le décorateur en Python est un obstacle pour entrer en Python. Il est là, que vous le franchissiez ou non. Le concept de décorateurs en Python confond souvent les gens, je vais donc aujourd'hui analyser les décorateurs en Python

1. Portée

En python, la portée est divisée en deux types : portée mondiale et portée locale.

La portée globale est la variable et le nom de la fonction définis au niveau du fichier. La portée locale se trouve à l'intérieur de la fonction définie .

Concernant la portée, je dois comprendre deux points : a. Les variables définies localement ne sont pas accessibles globalement. b Les variables définies globalement sont accessibles localement, mais les variables définies globalement ne peuvent pas être modifiées (bien sûr, il existe des méthodes qui peuvent être utilisées). modifié)

Regardons l'exemple suivant :


x = 1
def funx():
  x = 10
  print(x) # 打印出10

funx()
print(x) # 打印出1
Copier après la connexion

S'il n'y a pas de variable définie x localement, alors la fonction En interne, elle recherchera x de l'intérieur vers l'extérieur. S'il n'est pas trouvé, une erreur sera signalée. Les variables globales peuvent être référencées n'importe où dans le fichier, mais les modifications ne peuvent être effectuées globalement que si la variable requise l'est. introuvable localement, il sera recherché en externe et une erreur sera signalée s'il n'est pas trouvé.


2. Fonctions avancées

x = 1
def funx():
  print(x) # 打印出1

funx()
print(x) # 打印出1

x = 1
def funx():
  def func1():
    print(x) # 打印出1
  func1()

funx()
print(x) # 打印出1
Copier après la connexion

Nous savons que le nom de la fonction pointe en fait vers l'adresse d'un espace mémoire, puisqu'il s'agit d'une adresse, nous pouvons utiliser cette fonctionnalité.

un nom de fonction peut être utilisé comme valeur

b.


c.. Le nom de la fonction peut être utilisé comme paramètre
def delete(ps):
  import os
  filename = ps[-1]
  delelemetns = ps[1]
  with open(filename, encoding='utf-8') as f_read,\
    open('tmp.txt', 'w', encoding='utf-8') as f_write:
    for line in iter(f_read.readline, ''):
      if line != '\n': # 处理非空行
        if delelemetns in line:
          line = line.replace(delelemetns,'')
        f_write.write(line)
  os.remove(filename)
  os.rename('tmp.txt',filename)

def add(ps):
  filename = ps[-1]
  addelemetns = ps[1]
  with open(filename, 'a', encoding='utf-8') as fp:
    fp.write("\n", addelemetns)

def modify(ps):
  import os
  filename = ps[-1]
  modify_elemetns = ps[1]
  with open(filename, encoding='utf-8') as f_read, \
      open('tmp.txt', 'w', encoding='utf-8') as f_write:
    for line in iter(f_read.readline, ''):
      if line != '\n': # 处理非空行
        if modify_elemetns in line:
          line = line.replace(modify_elemetns, '')
        f_write.write(line)
  os.remove(filename)
  os.rename('tmp.txt', filename)


def search(cmd):
  filename = cmd[-1]
  pattern = cmd[1]
  with open(filename, 'r', encoding="utf-8") as f:
    for line in f:
      if pattern in line:
        print(line, end="")
    else:
      print("没有找到")

dic_func ={'delete': delete, 'add': add, 'modify': modify, 'search': search}

while True:
  inp = input("请输入您要进行的操作:").strip()
  if not inp:
    continue
  cmd_1 = inp.split()
  cmd = cmd_1[0]
  if cmd in dic_func:
    dic_func[cmd](cmd_1)
  else:
    print("Error")
Copier après la connexion


Par conséquent, si l'un des deux ci-dessus conditions est remplie, on peut l'appeler une fonction avancée.
def outer():
  def inner():
    pass
  return inner

s = outer()
print(s)

######输出结果为#######
<function outer.<locals>.inner at 0x000000D22D8AB8C8>
Copier après la connexion

3. Fonction de fermeture


La fonction de fermeture doit remplir deux conditions : 1. Fonction définie à l'intérieur de la fonction 2. Contient une portée externe plutôt qu'une portée globale Référence de portée
def index():
  print("index func")

def outer(index):
  s = index
  s()
  
outer(index)

######输出结果#########

index func
Copier après la connexion

Ce qui suit illustre la fonction de fermeture à travers quelques exemples :

Exemple 1 : Ce qui suit définit uniquement une fonction à l'intérieur de la fonction, mais ce n'est pas une fermeture function.

Exemple 2 : Ce qui suit définit une fonction à l'intérieur de la fonction et fait également référence à une variable externe x. Est-ce donc une fonction de fermeture ? 🎜>


En repensant à la définition de la fonction de fermeture, est-ce qu'elle satisfait les deux ? Si vous êtes intelligent, vous devez constater que la seconde n'est pas satisfaite. Oui, la variable x ici est une variable globale, pas une variable qui agit de l'extérieur sur le domaine. Regardons l'exemple suivant :

def outer():
  def inner():
    print("inner func excuted")
  inner() # 调用执行inner()函数
  print("outer func excuted")
outer() # 调用执行outer函数

####输出结果为##########
inner func excuted
outer func excuted
Copier après la connexion


Évidemment, l'exemple ci-dessus satisfait aux conditions de la fonction de fermeture. Or, sachez qu’en tant que fonction de fermeture, il doit remplir les deux conditions ci-dessus, dont l’une est indispensable. Cependant, dans des circonstances normales, nous renverrons une valeur à la fonction de fermeture. Ne parlons pas de pourquoi ici. Dans le contenu suivant, vous verrez l'utilisation de cette valeur de retour

x = 1
def outer():
  def inner():
    print("x=%s" %x) # 引用了一个非inner函数内部的变量
    print("inner func excuted")
  inner() # 执行inner函数
  print("outer func excuted")

outer()
#####输出结果########
x=1
inner func excuted
outer func excuted
Copier après la connexion

.


Définissons maintenant la fonction de fermeture de manière abstraite. C'est une entité composée d'une fonction et de son environnement de référence associé. Lors de l'implémentation de contraintes profondes, vous devez créer quelque chose qui représente explicitement l'environnement de référence et le regrouper avec le sous-programme approprié, afin que le bundle devienne une fermeture. Dans l'exemple ci-dessus, nous pouvons constater que la fonction de fermeture doit contenir sa propre fonction et une variable externe pour être véritablement appelée fonction de fermeture. Si aucune variable externe n’y est liée, alors la fonction ne peut pas être considérée comme une fonction de fermeture.

def outer():
  x = 1
  def inner():
    print("x=%s" %x)
    print("inner func excuted")
  inner()
  print("outer func excuted")

outer()

#####输出结果#########
x=1
inner func excuted
outer func excuted
Copier après la connexion
Alors, comment savoir combien de variables de référence externes possède une fonction de fermeture ? Regardez le code suivant


def outer():
  x = 1
  def inner():
    print("x=%s" %x)
    print("inner func excuted")
  print("outer func excuted")
  return inner # 返回内部函数名
  
outer()
Copier après la connexion
Les résultats montrent cela à l'intérieur. le inner , faisant référence à deux variables locales externes. Si une variable non locale est référencée, alors la sortie ici est Aucune.

Caractéristiques de la fonction de fermeture :

1 Livré avec la portée 2. Calcul retardé


Puis fermeture. Que fait la fonction package ? Nous savons clairement que lorsque la fonction de fermeture est définie, elle sera liée à un environnement externe. Tout cela peut être considéré comme une fonction de fermeture, nous pouvons alors utiliser cette fonction de liaison pour compléter certaines fonctions spéciales.

def outer():
  x = 1
  y = 2

  def inner():
    print("x= %s" %x)
    print("y= %s" %y)

  print(inner.closure)
  return inner

outer()

######输出结果#######
(<cell at 0x000000DF9EA965B8: int object at 0x000000006FC2B440>, <cell at 0x000000DF9EA965E8: int object at 0x000000006FC2B460>)
Copier après la connexion
Exemple 3 : Téléchargez le code source de la page en fonction de l'URL entrante

Certaines personnes peuvent dire que cela ne remplit pas les conditions du fonction de fermeture Ah ! je n'ai pas fait référence à une variable externe non globale. En fait, ce n'est pas le cas, comme nous l'avons dit précédemment, tant que les variables à l'intérieur de la fonction appartiennent à la fonction. Ensuite, je suis à index(url), cette URL appartient également à l'intérieur de la fonction, mais nous avons omis une étape, donc la fonction ci-dessus est également une fonction de fermeture.

4. Décorateur


Avec la base ci-dessus, il est facile de comprendre le décorateur

from urllib.request import urlopen

def index(url)
  def get()
    return urlopen(url).read()
  return get

python = index("http://www.python.org") # 返回的是get函数的地址
print(python()) # 执行get函数《并且将返回的结果打印出来
baidu = index("http://www.baidu.com")
print(baidu())
Copier après la connexion
Décorateur : La fonction externe transmise est la fonction décorée. name, la fonction interne renvoie le nom de la fonction décorée.

Caractéristiques : 1. Ne modifie pas la méthode d'appel de la fonction décorée 2. Ne modifie pas le code source de la fonction décorée

a. Décorateur sans argument

Il existe les exemples suivants : nous devons calculer le temps d'exécution du code.


import time, random

def index():
  time.sleep(random.randrange(1, 5))
  print("welcome to index page")
Copier après la connexion

  根据装饰器的特点,我们不能对index()进行任何修改,而且调用方式也不能变。这时候,我们就可以使用装饰器来完成如上功能.


import time, random

def outer(func): # 将index的地址传递给func
  def inner():
    start_time = time.time()
    func()  # fun = index 即func保存了外部index函数的地址
    end_time = time.time()
    print("运行时间为%s"%(end_time - start_time))
  return inner # 返回inner的地址

def index():
  time.sleep(random.randrange(1, 5))
  print("welcome to index page")

index = outer(index) # 这里返回的是inner的地址,并重新赋值给index

index()
Copier après la connexion

  但是,有些情况,被装饰的函数需要传递参数进去,有些函数又不需要参数,那么如何来处理这种变参数函数呢?下面来看看有参数装饰器的使用情况.

  b.有参装饰器


def outer(func): # 将index的地址传递给func
  def inner(*args, **kwargs):
    start_time = time.time()
    func(*args, **kwargs)  # fun = index 即func保存了外部index函数的地址
    end_time = time.time()
    print("运行时间为%s"%(end_time - start_time))
  return inner # 返回inner的地址
Copier après la connexion

  下面来说说一些其他情况的实例。

  如果被装饰的函数有返回值


def timmer(func):
  def wrapper(*args,**kwargs):
    start_time = time.time()
    res=func(*args,**kwargs) #res来接收home函数的返回值
    stop_time=time.time()
    print(&#39;run time is %s&#39; %(stop_time-start_time))
    return res 
  return wrapper

def home(name):
  time.sleep(random.randrange(1,3))
  print(&#39;welecome to %s HOME page&#39; %name)
  return 123123123123123123123123123123123123123123
Copier après la connexion

  这里补充一点,加入我们要执行被装饰后的函数,那么应该是如下调用方式:

  home = timmer(home) # 等式右边返回的是wrapper的内存地址,再将其赋值给home,这里的home不在是原来的的那个函数,而是被装饰以后的函数了。像home = timmer(home)这样的写法,python给我们提供了一个便捷的方式------语法糖@.以后我们再要在被装饰的函数之前写上@timmer,它的效果就和home = timmer(home)是一样的。

  如果一个函数被多个装饰器装饰,那么执行顺序是怎样的。


import time
import random

def timmer(func):
  def wrapper():
    start_time = time.time()
    func()
    stop_time=time.time()
    print(&#39;run time is %s&#39; %(stop_time-start_time))
  return wrapper
def auth(func):
  def deco():
    name=input(&#39;name: &#39;)
    password=input(&#39;password: &#39;)
    if name == &#39;egon&#39; and password == &#39;123&#39;:
      print(&#39;login successful&#39;)
      func() #wrapper()
    else:
      print(&#39;login err&#39;)
  return deco

@auth  # index = auth(timmer(index))         
@timmer # index = timmer(index)
def index():
 
  time.sleep(3)
  print(&#39;welecome to index page&#39;)

index()
Copier après la connexion

  实验结果表明,多个装饰器装饰一个函数,其执行顺序是从下往上。

  关于装饰器,还有一些高级用法,有兴趣的可以自己研究研究。

Ce qui précède est le contenu détaillé de. pour plus d'informations, suivez d'autres articles connexes sur le site Web de PHP en chinois!

Étiquettes associées:
source:php.cn
Déclaration de ce site Web
Le contenu de cet article est volontairement contribué par les internautes et les droits d'auteur appartiennent à l'auteur original. Ce site n'assume aucune responsabilité légale correspondante. Si vous trouvez un contenu suspecté de plagiat ou de contrefaçon, veuillez contacter admin@php.cn
Tutoriels populaires
Plus>
Derniers téléchargements
Plus>
effets Web
Code source du site Web
Matériel du site Web
Modèle frontal