Почему декораторы не выполняются каждый раз, когда вызывается декорированная функция? - PullRequest
2 голосов
/ 24 февраля 2020

Я написал следующий код для изучения замыканий и декораторов в python.

Код отлично работает в Pythonista на iPad.

Но декоратор работает не так, как я думал. Декоратор предназначен для того, чтобы функция выводила уникальный случайный цвет при каждом вызове. Но похоже, что декоратор вызывается только один раз для всех вызовов функции. Может кто-нибудь объяснить, почему?

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 Ответ

4 голосов
/ 24 февраля 2020

Декоратор выполняется, когда функция определена ; синтаксис декоратора - это просто синтаксис c сахар для применения функции.

@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 в конечном счете вызывается. Теперь это функция, которая возвращает функцию, которая выбирает случайный цвет , а генерирует тег, используя этот цвет.

...