2016-04-05 4 views
5

Я немного разбираюсь в декораторах из большого учебника по thecodeship, но нашел себя в некоторой степени путаным с помощью одного примера.Пример декоратора Python

Сначала приведен простой пример, за которым следует объяснение, для какого декоратора.

def p_decorate(func): 
    def func_wrapper(name): 
     return "<p>{0}</p>".format(func(name)) 
    return func_wrapper 

def get_text(name): 
    return "lorem ipsum, {0} dolor sit amet".format(name) 

my_get_text = p_decorate(get_text) 

print my_get_text("John") 

Теперь это имеет смысл для меня. Декоратор - это просто обертка для функции. И в объяснении этих парней он говорит, что декоратор - это функция, которая принимает другую функцию в качестве аргумента, генерирует новую функцию и возвращает сгенерированную функцию, которая будет использоваться где угодно.

А теперь эквивалентно выше код:

def p_decorate(func): 
    def func_wrapper(name): 
     return "<p>{0}</p>".format(func(name)) 
    return func_wrapper 

@p_decorate 
def get_text(name): 
    return "lorem ipsum, {0} dolor sit amet".format(name) 

print get_text("John") 

Я считаю, что я понимаю, как декоратор инициализируется, когда не дали никаких аргументов. Поправьте меня, если я ошибаюсь.

  • декоратор по умолчанию переходит в функции get_text и потому, что p_decorate возвращает функцию func_wrapper, мы в конечном итоге с истинным утверждением get_text = func_wrapper.

Что для меня важно, это первый эквивалент кода, потому что я вижу и понимаю, как работает декоратор.

Что очень меня смущает следующий код:

def tags(tag_name): 
    def tags_decorator(func): 
     def func_wrapper(name): 
      return "<{0}>{1}</{0}>".format(tag_name, func(name)) 
     return func_wrapper 
    return tags_decorator 

@tags("p") 
def get_text(name): 
    return "Hello "+name 

print get_text("John") 

Опять же, поправьте меня, если я ошибаюсь, но это мое понимание.

  • Декоратор принимает строку тега «p» вместо имени функции по умолчанию . И, в свою очередь, функция tags_decorator предполагает, что параметр, который будет передан, является функцией , украшаемой, get_text.

Возможно, было бы полезно увидеть эквивалентный блок кода в форме «не-декоратор», но я не могу оглянуться, как это будет выглядеть. Я также не понимаю, почему возвращаются tags_decorator и func_wrapper. Какова цель возвращения двух разных функций, если декоратору нужно только вернуть 1 функцию для обертывания get_text.

Как примечание стороны, это действительно сводится к следующему.

  • Может ли этот блок быть упрощен чем-то меньшим, чем набор из 3 функций?
  • Может ли декораторы принять более 1 аргумент для упрощения кода?

ответ

7

В пределах, все после @ выполняется, чтобы продукции декоратора.В первом примере, что следует после того, как @ просто название:

@p_decorate 

так Python смотрит p_decorate и называет его с декорированной функции в качестве аргумента:

get_text = p_decorate(get_text) 

(упрощенно немного, get_text первоначально не привязан к исходной функции, но вы уже получили суть).

В вашем втором примере выражение декоратора немного сложнее, она включает в себя вызов:

@tags("p") 

так Python использует tags("p") найти декоратор. В конце концов, это то, что тогда выполняется:

get_text = tags("p")(get_text) 

выход из tags("p") является декоратор здесь! Я вызываю функцию tags как декоратор завод, он производит декоратор при вызове. Когда вы вызываете tags(), он возвращает tags_decorator(). Это настоящий декоратор здесь.

Вы могли бы вместо того, чтобы удалить функцию декоратора и жёстко значение "p" и использовать его непосредственно:

def tags_decorator_p(func): 
    def func_wrapper(name): 
     return "<{0}>{1}</{0}>".format("p", func(name)) 
    return func_wrapper 

@tags_decorator_p 
def get_text(name): 
    # ... 

, но тогда вам придется создавать отдельные декораторов для каждого возможного значения аргумента tags(). Это значение фабрики декораторов, вы можете добавить параметры декоратору и изменить способ декорирования вашей функции.

Декоратор завод может принимать любое количество аргументов; это просто функция, которую вы вызываете для создания декоратора. Декоратор сам может принимать только один аргумент, функция для украшения.

Я сказал, в пределах в начале моего ответа по причине; синтаксис выражения, следующего за @, допускает только точечное имя (foo.bar.baz, поэтому доступ к атрибуту) и вызов ((arg1, arg2, keyword=arg3)). См. reference documentation. В оригинале PEP 318 указано:

Утверждение декоратора ограничено тем, что он может принять - произвольные выражения не будут работать. Гвидо предпочел это из-за чувства кишки [17].

+0

спасибо. И я сделал короткое редактирование после вашего ответа. Почему именно эти функции возвращают 2 функции, а не 1? – Max

+1

@Max: см. Мое последнее редактирование. Один из них - декоратор (изготовитель фабрики декораторов), другой - обертка, заменяющая оригинальную функцию, результат декоратора. –

+0

Теперь я понимаю, что цель 2 вызова функций. Я могу сделать специальный декоратор с фабрикой декоратора, чтобы дополнительно указать мою последнюю обертку. – Max

Смежные вопросы