Декораторы Python не передают аргументы? Не волнуйтесь, эта статья поможет вам

Python

Эта статья взята из личного паблика: TechFlow, оригинальность непроста, прошу внимания


СегодняЧасть 13 тем PythonВ предыдущей статье мы представили определение и базовое использование декораторов Python, а в этой статье давайте изучим некоторые расширенные возможности использования декораторов Python. Те, кто не знаком с декораторами, или кто пропустил содержание предыдущей статьи, могут нажать на портал ниже.

Получите декоратор Python в одной статье, больше не паникуйте после прочтения интервью

В предыдущей статье мы тщательно рассмотрели сущность и использование декоратора спереди назад и изучили его базовое использование, которого достаточно.Справляется с 80% сценариев. Но всегда есть 20% сценариев, которые не могут быть решены с помощью базовых методов.В настоящее время нам нужно изучать все более и более полные другие способы использования.

как я хочуУправляйте функциональностью декоратора через параметр, эта проблема на самом деле довольно распространена. Взяв за пример время записи, мы все знаем, что время может быть записано во многих форматах, таких как 2020-05-04, 20200504, 05.04.2020, и если это бэкенд, то оно тоже будет записано. Отметка времени. Например, теперь мы реализовали декоратор журнала для регистрации нашего метода.Теперь мы хотим контролировать формат времени, выводимый при регистрации.Это требование не может быть решено с помощью простейшего декоратора.

На данный момент, если мы хотим решить проблему, мы должны ввести параметры, что означает, что мы должны добавить параметры в декоратор. Но вопрос, как добавить этот параметр и куда его добавить?

Определить параметры декоратора

Прежде чем мы представим конкретное использование, давайте рассмотрим код декоратора:

def mydec(func):
    @wraps(func)
    def mywrap(*args, **kw):
        print('hello this is decorator1')
        func(*args, **kw)
    return mywrap

@mydec def helloWorld(): print('hello, world')

Это самый простой вид декоратора, о котором мы говорили в прошлый раз.Если мы хотим сейчас передать тип параметра, мы можем контролировать вывод декоратора. нравится:

@mydec(type_='test')
def helloWorld():
    print('hello, world')

Мы можем задаться вопросом, должны ли мы добавить type_ к параметру метода mydec, но если вы попробуете это сделать, вы обнаружите, что это не сработает, и вы получите сообщение об ошибке:

Ошибка. Буквальный смысл ошибки понять несложно, но причина вызывает недоумение. Эта ошибка говорит о том, что функция mydecНа один необходимый параметр меньше функции, это функция, которую мы хотим обернуть, но разве это не передается автоматически? Как она может подсказать нам, что этот параметр отсутствует?

Если суть этой проблемы не понять, то декоратору будет трудно стать большим, потому что только четко поняв это, мы сможем понять странное и продвинутое использование декоратора. Но очень жаль, что многие материалы лишь кратко знакомят с тем, как его использовать, и редко исследуют причины, стоящие за этим, что может запутать новичков при обучении. Я тоже много думал пока учился, и наконец-то разобрался.Грубо говоря, очень просто, но разобраться в нем непросто.

На самом деле основная причина этой ошибки в том, что декоратор с параметрами в аннотации и декоратор без параметров совершенно разные.

Давайте рассмотрим использование декораторов без параметров, таких как:

@mydec
def hello_world():
    pass

Когда мы выполняем hello_world(), это эквивалентно выполнению mydec(hello_world)(). Понятно? Давайте расширим эту строку кода, которая на самом деле является результатом совместного выполнения следующих двух строк кода:

cur = mydec(hello_world)
cur()

Что, если функция hello_world принимает параметры?

@mydec
def hello_world(*args, **kw):
    pass

Тогда при выполнении это выглядит так:

cur = mydec(hello_world)
cur(*args, **kw)

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

@mydec(type_='test')
def helloWorld():
    print('hello, world')

cur = mydec(hello_world, type_)
cur()

Но, к сожалению, интерпретатор Python не предназначен для этого. Это декоратор с добавленными к нему параметрамиСделайте слой упаковки, то есть когда функция hello_world, переданная выше, выполняется, она эквивалентна следующему коду:

cur1 = mydec(type_)
cur2 = cur1(hello_world)
cur2()

Именно из-за лишнего слоя инкапсуляции порядок передачи параметров функции и декоратора в декоратор другой, и порядок тоже другой. После понимания этого все намного проще.Поскольку интерпретатор Python добавляет дополнительный слой при интерпретации параметров декоратора, если мы хотим реализовать декоратор с параметрами, нам нужно всего лишь инкапсулировать в декораторе еще один слой. Например, это можно записать так:

def mydec(type_=None):
    def decorate(func):
        @wraps(func)
        def mywrap():
            if type_ is not None:
                print(type_)
            func()
        return mywrap
    return decorate

Итак, мы можем выполнить его снова:

Что делать с параметрами по умолчанию

Здесь все кажется идеальным, но на самом делеесть большая проблемаигнорируется нами.

Эта проблема связана с параметром по умолчанию. Когда мы определяли декоратор ранее, мы установили параметр type_ как необязательный. Это также соответствует нашей реальной ситуации, если в этом нет необходимости, параметры можно не указывать. Но это приводит к проблеме: для декораторов без параметров некоторые люди привыкли писать mydec(), а некоторые привыкли писать mydec. Если мы попробуем mydec, мы обнаружим, что запись этого даст ошибку:

Эта ошибка точно такая же, как и ошибка выше, и причина та же, во всех из них отсутствует параметр func. Но очень странно, почему отсутствует func?

Причина проста, потому что мы убираем скобки и декоратор возвращается к предыдущей двухуровневой структуре!

cur = mydec(hello_world)
cur(*args, **kw)

Это очень сложно.Структура нашего декоратора не должна быть изменена.Если мы используем двухслойную структуру,мы не можем передавать параметры,а что если мы не будем передавать параметры,можно только заставить программистов унифицировать все стили?Добавить скобки ? Это, конечно, тоже способ, так есть ли лучший способ? Есть ли способ объединить эти две логики?

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

Частную функцию легко понять, это также функция высшего порядка, на самом деле этоЗакрытие. Сценарии использования частичных функций Для функций с несколькими параметрами, используя частичные функции, можно зафиксировать значение нескольких параметров, что упрощает функцию передачи параметров. Давайте рассмотрим пример, в котором мы создаем pow-функцию, которая вычисляет x в n-й степени:

import math
def pow(x, n):
    return math.pow(x, n)

Эта функция должна передавать два параметра х и п. Если нам нужно только вычислить квадрат в настоящее время, мы можем использовать замыкание,исправить в нем параметр n, создайте для этого новую функцию. Например:

def mypow(n):
    def func(x):
        return pow(x, n) 
    return func

Суть частичной функции в таком замыкании, но это упрощает наш код:

from functools import partial

pow2 = partial(pow, n=2)
pow2(6)

Чтобы использовать частичные функции, нам нужно только передать исходную функцию для обработки и фиксированные значения параметров. Мы можем решить проблему прямо сейчас, используя частичные функции в декораторах. Напомним, что декораторы без параметров представляют собой два уровня вложенности функций, а декораторы с параметрами — три уровня вложенности. Затем мы используем partial, чтобы добавить дополнительный уровень вложенности для случая с параметрами:

def mydec(func=None, type_=None):
    # 不带参数的话,func会是None,这时候我们固定参数即可
    if func is None:
        return partial(mydec, type_=type_)

@wraps(func) def mywrap(): if type_ is not None: print(type_) func() return mywrap

Давайте посмотрим на детали. Когда мы не передаем параметры, мы на самом деле выполняем cur = mydec(func). В это время func не пуста, поэтому оператор if не будет запущен, поэтому он вернет непосредственно моя упаковка. Если передан параметр, на этот раз func имеет значение None, что вызовет частичный вход if. Обратите внимание, что здесь мыФункция, переданная в partial, все еще mydec., то есть мы зафиксировали параметр type_, а вызов все равно возвращает mywrap, что равносильно добавлению слоя в двухслойную структуру с параметрами через partial, что унифицирует логику.

конец

Сегодняшняя концепция намного сложнее, чем предыдущие декораторы,Это может быть нелегко понять в данный момент, на самом деле, это очень нормально. Это не просто проблема декоратора, это не просто проблема Python, это, в конечном счете, особенность функционального программирования. Преимущество функционального программирования в том, что оно очень гибкое и очень удобное в использовании, но и недостатки очевидны.Код сложно поддерживать, его трудно читать и сложно понять.. Типичному новичку легко, а глубокому очень сложно. Так что если вы чувствуете, что какое-то время не можете понять, это не ваша проблема.С одной стороны, нам нужно культивировать собственное программно-переходное мышление, а с другой стороны, мы также должны быть знакомы с использованием декораторов в Python.

Наконец, небольшое отступление, из-за Sekiro и Nioh я в последнее время стал немного одержим хардкорными играми. Когда я впервые начал играть, мне было очень сложно, я часто застревал, а боссы часто умирали десятки раз. Позже я постепенно находил хитрость и мгновенно обнаружил, что этот тип игры и даже все игры стали проще.

Дело не только в том, что я с ним знаком, а в том, что я начал думать об этом, играя в игру, я начал думать о том, какие трюки придумали эти боссы? Какую свободу действий оставляет нам дизайнер? Каким правилам следует следовать? Чем больше вы думаете об этом, тем больше будет трюк. После большого количества боев многие боссы будут иметь трудности только на первый взгляд.Пока вы знакомы с рутиной после двух-трех боев, вы можете пройти уровень. Постепенно я обнаружил, что многие вещи в жизни на самом деле такие же, как боссы в играх.Это просто сложно на первый взгляд, Когда я увидел это в первый раз, я почувствовал, что не могу начать, мне было трудно понять, я чувствовал, что это гигант, поэтому это было сложно. Но пока есть настойчивость,Храброе сердце,учитьсяСпокойно и рационально анализируйте, на самом деле просто бумажный тигр.

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

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