为什么装饰器在每次调用装饰函数时不执行?

时间:2020-02-24 01:49:13

标签: python decorator

我编写了以下代码来学习python中的闭包和装饰器。

该代码可以在iPad上的Pythonista中正常运行。

但是装饰器无法像我想的那样工作。装饰器用于使函数在每次调用时以唯一的随机颜色打印出来。但是看起来装饰器对于该函数的所有调用仅被调用一次。有人可以解释为什么吗?

import random
import console 

def random_color(func): 
  r = random.random()
  g = random.random()
  b = random.random()
  print(f'console.set_color({r},{g},{b})')
  console.set_color(r,g,b)
  return func

@random_color  # run set_tag function through decorator function. 
def set_tag(tag):  
  def enclose_text(text):
    print( f'<{tag}>{text}</{tag}>')    
  return enclose_text 

# save enclose_text function with a remembered tag
h1 = set_tag('h1')
p  = set_tag('p')
br = set_tag('br')

# execute enclose_text with different text strings 
h1('Chapter One')
p('It was a dreary day. The rain had begun to set in ...')
br('')
h1('Chapter Two')
p('By the second day, the sun had returned to full strength.')

所有行的输出颜色相同。下次运行时,所有行的颜色都相同-但与第一次执行时不同。我希望装饰器使每个标签具有随机的颜色。

有人可以解释为什么吗?

下面是输出:

<h1>Chapter One</h1>
<p>It was a dreary day. The rain had begun to set in ...</p>
<br></br>
<h1>Chapter Two</h1>
<p>By the second day, the sun had returned to full strength.</p>

1 个答案:

答案 0 :(得分:4)

装饰器在定义函数时执行;装饰器语法只是函数应用程序的语法糖。

@random_color  # run set_tag function through decorator function. 
def set_tag(tag):  
  def enclose_text(text):
    print( f'<{tag}>{text}</{tag}>')    
  return enclose_text 

等同于

def set_tag(tag):  
  def enclose_text(text):
    print( f'<{tag}>{text}</{tag}>')    
  return enclose_text 

set_tag = random_color(set_tag)

您应该改为这样定义装饰器:

def random_color(func): 
    def wrapper(*args, **kwargs):
        r = random.random()
        g = random.random()
        b = random.random()
        print(f'console.set_color({r},{g},{b})')
        console.set_color(r,g,b)
        return func(*args, **kwargs)
  return wrapper

也就是说,random_color应该返回一个设置控制台颜色的函数,然后调用原始函数。

此外,set_tag不是您要修饰的功能:它是set_tag创建的功能:

def set_tag(tag):
    @random_color 
    def enclose_text(text):
        print( f'<{tag}>{text}</{tag}>')    
    return enclose_text 

在此之前,set_tag是一种函数,它将选择一种随机颜色,将控制台设置为使用该颜色,然后返回一个将生成标记的函数。我假设对set_color的调用会在此时影响终端,而不是最终print被调用时。现在,它是一个函数,该函数返回一个既选择随机颜色 并使用该颜色生成标签的函数。