Как в python снаружи проверить значения, переданные в функцию

    Небольшое HowTo для начинающих python-программистов о том как извне проверить некоторые переданные в функцию значения. На самом деле мне это понадобилось в Django, но ничего специфичного для фреймворка в этом нет и…

    UPD Лирическое отступление: Изначально тут я написал много глупостей в силу своего кривого понимания работы декораторов. Теперь решение гораздо более правильное и прямое. Более того я даже внятно могу его объяснить, за что огромная благодарность комментаторам.

    Для начала рекомендую ознакомиться с работой декораторов (очень тщательно!).

    Проблема у меня возникла буквально следующая:

    Есть некоторая функция вида для Django перед выполнением которой нужно проверить есть ли некоторые переменные в профиле юзера.

    Конкретное решение такое:

    def check_nickname(funct):
    
        def wrapper(request, *args, **kwargs):
    
            if request.user.profile.nickname:
                return funct(request, *args, **kwargs)
            else:
                from django.shortcuts import render_to_response
                from django.template import RequestContext
                return render_to_response('need_profile.html', RequestContext(request))
    
        return wrapper

    Функция check_nickname является декоратором к функции вида Django, про которую точно известно, что в неё передаётся параметр request определённого типа. Декорируемая функция передаётся во wrapper который и возвращается вместо декорируемой функции.

    Принцип работы декоратора:

    @f1
    def func(x): pass
    
    #эквивалентно этому:
    
    def func(x): pass
    func = f1(func)
    


    Т.е. фактически вместо оригинальной функции вызывается функция wrapper возвращаемая декоратором check_nickname. И именно wrapper получает все параметры предназначенные для оригинальной функции вида, и на их основании уже строится логика следующих действий.

    Ещё раз спасибо Deepwalker за разъяснения.
    Ads
    AdBlock has stolen the banner, but banners are not teeth — they will be back

    More

    Comments 16

      +7
      Или я статью криво прочитал или как, но декоратор wraps(fnc) просто выставляет в декорируемой им функции __name__, __doc__ и тд на значения из функции fnc. Никакого волшебства он не делает и всё будет работать и без него. Он нужен в основном для правильных стэк трейсов и дебага.
        –1
        Извините, но я не очень себе представляю как можно получить доступ к параметрам декорируемой функции «и без него» не написав аналогичный декоратор. Про волшебство я ничего не писал…
        +3
        > Я затрудняюсь человеческим языком, не запутав читателя в терминах, вразумительно сформулировать то, что тут происходит

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

        check_nickname не декоратор, это фабрика декораторов. использовать вы ее будете как @check_nickname(), то есть вызовете, она вернет декоратор, который и применится к функции.

        check — реальный декоратор, примет в качестве параметра функцию, и вернет вместо нее другую. Вот тут и нужен wraps, чтобы всякие атрибуты функции типа __name__, __doc__ etc скопировать на новую функцию.

        Остался wrapper — обычная функция, которая будет вызвана вместо оригинальной функции, с параметрами соответственно. Может перешаманить параметры, может обработать результат вызова обернутой функции. А может вообще ее не вызывать, и вернуть что-нибудь другое.

        Замечания по коду — check_nickname принимает зачем то args и kwargs — они не используются, не пишите «на всякий случай» никогда, это сродни ""«try… except:»"".
          +1
          Ах, да. Так как check_nickname собственно никакой смысловой нагрузки не несет, то его можно спокойно выкинуть — вам здесь фабрика ни к чему собственно.
            0
            действительно вы правы. тут мой косяк — бездумно слизал код с @login_required, подправил код.
              0
              *args, **kwargs зачем на декораторе?
                0
                убрал, опять же «слизал»…
          +2
          Я заметил, что декоратор @login_required не требует передачи в себя каких-либо данных, т.е. он их получает из декорируемой функции сам.

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

            0
            Нюансы терминологии. Декоратор декорирует всё же функцию, а не её данные. Сначала он получает её, а потом уже доступается к её данным с помощью wraps и именно об этом заметка.
              +1
              Да не «доступается» он с помощью wraps, можете выкинуть этот wraps спокойно, и все будет работать.
              Он вообще никуда не доступается, он возвращает функцию взамен оригинальной, вот и вся недолга.
                0
                Собственно для того я его и использовал — вернуть функцию… Но я его удалил и всё работает. Получается в декоратор check_nickname передаётся функция, которая исполняется внутри wrapper, который в свою очередь, и возвращается декоратором взамен оригинальной функции. Соответственно в оригинальном коде вызов идёт уже к wrapper и он получает все параметры обёрнутой функции, проверяет их и либо исполняет её, либо нет! Спасибо огромное за ваши комменнтарии. Всё-таки есть польза от этой заметки, хоть и не такая как я изначально предполагал :) Мне действительно нехватало понимания принципа работы декораторов. Сейчас перепишу заметку…
              0
              Но вы были правы в том, что мне нужно лучше разобраться с декораторами.
              +1
              Написали свой первый декоратор на питоне и решили поделиться этим на хабре?
                0
                Второй, в любом случае, судя по счётчику избранного, эта заметка оказалась полезной не только для меня…
                0
                > if request.user.profile.nickname

                Потерялось двоеточие
                  0
                  Добавил

                Only users with full accounts can post comments. Log in, please.