<!DOCTYPE html>
<html class="h-100" data-bs-theme="light" data-mantine-color-scheme="light" lang="ru" prefix="og: https://ogp.me/ns#">
<head>
<meta content="width=device-width, initial-scale=1.0" name="viewport">
<meta content="IE=Edge" http-equiv="X-UA-Compatible">
<link crossorigin="true" href="https://cdn.hexlet.io" rel="preconnect">
<link href="https://mc.yandex.ru" rel="preconnect">
<meta content="aa2vrdtq64dub8knuf83lwywit311w" name="facebook-domain-verification">
<link href="/favicon.ico" rel="icon" sizes="any">
<link href="/favicon.svg" rel="icon" type="image/svg+xml">
<link href="/apple-touch-icon.png" rel="apple-touch-icon">
<link href="/manifest.webmanifest" rel="manifest">
<script>
//<![CDATA[
window.gon={};gon.ym_counter="25559621";gon.is_bot=true;gon.applications={};gon.current_user={"id":null,"last_viewed_notification_id":null,"email":null,"state":null,"first_name":"","last_name":"","created_at":"2026-02-26 20:31:45 UTC","current_program":null,"current_team":null,"full_name":"","guest":true,"can_use_paid_features":false,"is_hexlet_employee":false,"sanitized_phone_number":"","can_subscribe":true,"can_renew_education":false};gon.token="IG6QC59HlG8mlpALlT7aNsKnjBast9-PpycqHEPCtQTPv1s8bTk5D5DVtJOZMSpBAq6hvKSAIS0ax7BIEcVSag";gon.locale="ru";gon.language="ru";gon.theme="light";gon.rails_env="production";gon.mobile=false;gon.google={"analytics_key":"UA-1360700-51","optimize_key":"GTM-5QDVFPF"};gon.captcha={"google_v3_site_key":"6LenGbgZAAAAAM7HbrDbn5JlizCSzPcS767c9vaY","yandex_site_key":"ysc1_Vyob5ZPPUdPBsu0ykt8bVFdzsfpoVjQChLGl2b4g19647a89","verification_failed":null};gon.social_signin=false;gon.typoreporter_google_form_id="1FAIpQLSeibfGq-KvWQ2Fyru-zkFFRVTLBuzXAHAoEyN1p49FtDmNoNA";
//]]>
</script>
<meta charset="utf-8">
<title>Декораторы | Python: Функции</title>
<meta name="description" content="Декораторы / Python: Функции: Узнаем о декораторах — интересном и мощном инструменте, который выделяет Python среди многих других языков, поддерживающих использование ФВП">
<link rel="canonical" href="https://ru.hexlet.io/courses/python-functions/lessons/decorators/theory_unit">
<meta name="robots" content="noarchive">
<meta property="og:title" content="Декораторы">
<meta property="og:title" content="Python: Функции">
<meta property="og:description" content="Декораторы / Python: Функции: Узнаем о декораторах — интересном и мощном инструменте, который выделяет Python среди многих других языков, поддерживающих использование ФВП">
<meta property="og:url" content="https://ru.hexlet.io/courses/python-functions/lessons/decorators/theory_unit">
<meta name="csrf-param" content="authenticity_token" />
<meta name="csrf-token" content="X73Iqv42Q33Q-t9cH9M0vUDjnfHQmd_UNtUcl86E0fywbAOdDEjuHWa5-8QT3MTKgOqwW9iuIXaLNYbDnIM2kg" />
<script src="/vite/assets/inertia-DfXos102.js" crossorigin="anonymous" type="module"></script><link rel="modulepreload" href="/vite/assets/chunk-DsPFFUou.js" as="script" crossorigin="anonymous">
<link rel="modulepreload" href="/vite/assets/preload-helper-BJ4cLWpC.js" as="script" crossorigin="anonymous">
<link rel="modulepreload" href="/vite/assets/init-BrRXra1y.js" as="script" crossorigin="anonymous">
<link rel="modulepreload" href="/vite/assets/ahoy-DrlRQ-1D.js" as="script" crossorigin="anonymous">
<link rel="modulepreload" href="/vite/assets/analytics-cb8xch9l.js" as="script" crossorigin="anonymous">
<link rel="modulepreload" href="/vite/assets/ErrorFallbackBlock-naDSYSy9.js" as="script" crossorigin="anonymous">
<link rel="modulepreload" href="/vite/assets/Surface-DL2bpZA-.js" as="script" crossorigin="anonymous">
<link rel="modulepreload" href="/vite/assets/gon-D3e4yh1x.js" as="script" crossorigin="anonymous">
<link rel="modulepreload" href="/vite/assets/mantine-CGMYrt2Y.js" as="script" crossorigin="anonymous">
<link rel="modulepreload" href="/vite/assets/utils-DRqSHbQE.js" as="script" crossorigin="anonymous">
<link rel="modulepreload" href="/vite/assets/routes-CCH8ilKF.js" as="script" crossorigin="anonymous">
<link rel="modulepreload" href="/vite/assets/extends-C-EagtpE.js" as="script" crossorigin="anonymous">
<link rel="modulepreload" href="/vite/assets/inheritsLoose-BBd-DCVI.js" as="script" crossorigin="anonymous">
<link rel="modulepreload" href="/vite/assets/objectWithoutPropertiesLoose-DRHXDhjp.js" as="script" crossorigin="anonymous">
<link rel="modulepreload" href="/vite/assets/index.esm-DAqKOkZ0.js" as="script" crossorigin="anonymous">
<link rel="modulepreload" href="/vite/assets/Button-CGPUux8l.js" as="script" crossorigin="anonymous">
<link rel="modulepreload" href="/vite/assets/CloseButton-D1euiPao.js" as="script" crossorigin="anonymous">
<link rel="modulepreload" href="/vite/assets/Group-BX48WcuU.js" as="script" crossorigin="anonymous">
<link rel="modulepreload" href="/vite/assets/Loader-BQEY8g6v.js" as="script" crossorigin="anonymous">
<link rel="modulepreload" href="/vite/assets/Modal-Cy3HByv7.js" as="script" crossorigin="anonymous">
<link rel="modulepreload" href="/vite/assets/OptionalPortal-1Hza5P2w.js" as="script" crossorigin="anonymous">
<link rel="modulepreload" href="/vite/assets/Stack-CtjJzfw4.js" as="script" crossorigin="anonymous">
<link rel="modulepreload" href="/vite/assets/Textarea-Ck64llAy.js" as="script" crossorigin="anonymous">
<link rel="modulepreload" href="/vite/assets/Box-B5-OOzBf.js" as="script" crossorigin="anonymous">
<link rel="modulepreload" href="/vite/assets/DirectionProvider-Dc9zdUke.js" as="script" crossorigin="anonymous">
<link rel="modulepreload" href="/vite/assets/events-DJQOhap0.js" as="script" crossorigin="anonymous">
<link rel="modulepreload" href="/vite/assets/use-reduced-motion-D2owz4wa.js" as="script" crossorigin="anonymous">
<link rel="modulepreload" href="/vite/assets/use-disclosure-zKtK5W1r.js" as="script" crossorigin="anonymous">
<link rel="modulepreload" href="/vite/assets/use-hotkeys-Cnc_Rwkb.js" as="script" crossorigin="anonymous">
<link rel="modulepreload" href="/vite/assets/random-id-DOQyszCZ.js" as="script" crossorigin="anonymous">
<link rel="modulepreload" href="/vite/assets/notifications.store-C-3AFSMn.js" as="script" crossorigin="anonymous">
<link rel="modulepreload" href="/vite/assets/exports-C_MrNx_T.js" as="script" crossorigin="anonymous">
<link rel="modulepreload" href="/vite/assets/axios-BEvgo0ym.js" as="script" crossorigin="anonymous">
<link rel="modulepreload" href="/vite/assets/dayjs.min-BkKovM-s.js" as="script" crossorigin="anonymous">
<link rel="modulepreload" href="/vite/assets/i18next-BlSq9s7B.js" as="script" crossorigin="anonymous">
<link rel="modulepreload" href="/vite/assets/client-U9M77rxp.js" as="script" crossorigin="anonymous">
<link rel="modulepreload" href="/vite/assets/react-dom-DaLxUz_h.js" as="script" crossorigin="anonymous">
<link rel="modulepreload" href="/vite/assets/useTranslation-Bx1Cdrkz.js" as="script" crossorigin="anonymous">
<link rel="modulepreload" href="/vite/assets/compiler-runtime-6XxiPFnt.js" as="script" crossorigin="anonymous">
<link rel="modulepreload" href="/vite/assets/jsx-runtime-CwjcCKJi.js" as="script" crossorigin="anonymous">
<link rel="modulepreload" href="/vite/assets/react-CkL4ZRHB.js" as="script" crossorigin="anonymous">
<link rel="stylesheet" href="/vite/assets/application-BqhCP46M.js" />
<script src="/vite/assets/application-Df9RExpe.js" crossorigin="anonymous" type="module"></script><link rel="modulepreload" href="/vite/assets/chunk-DsPFFUou.js" as="script" crossorigin="anonymous">
<link rel="modulepreload" href="/vite/assets/autocomplete-VMNbxKGl.js" as="script" crossorigin="anonymous">
<link rel="modulepreload" href="/vite/assets/routes-CCH8ilKF.js" as="script" crossorigin="anonymous">
<link rel="modulepreload" href="/vite/assets/createPopper-C3aM9r1M.js" as="script" crossorigin="anonymous">
<link rel="modulepreload" href="/vite/assets/js.cookie-D1-O8zkX.js" as="script" crossorigin="anonymous"><link rel="stylesheet" href="/vite/assets/application-C8HjmMaq.css" media="screen" />
<script>
window.ym = function(){(ym.a=ym.a||[]).push(arguments)};
window.addEventListener('load', function() {
setTimeout(function() {
ym.l = 1*new Date();
ym(window.gon.ym_counter, "init", {
clickmap: true,
trackLinks: true,
accurateTrackBounce: true,
webvisor: true
});
// Загружаем скрипт
var k = document.createElement('script');
k.async = 1;
k.src = 'https://mc.yandex.ru/metrika/tag.js';
document.head.appendChild(k);
ym(window.gon.ym_counter, 'getClientID', function(clientID) {
window.ymClientId = clientID;
});
}, 1500);
});
</script>
<!-- Google Tag Manager - deferred -->
<script>
// dataLayer stub сразу — пуши работают до загрузки скрипта
window.dataLayer = window.dataLayer || [];
// Сам скрипт — отложенно после load
window.addEventListener('load', function() {
setTimeout(function() {
dataLayer.push({'gtm.start': new Date().getTime(), event: 'gtm.js'});
var j = document.createElement('script');
j.async = true;
j.src = 'https://www.googletagmanager.com/gtm.js?id=GTM-WK88TH';
document.head.appendChild(j);
}, 1500);
});
</script>
<!-- End Google Tag Manager -->
</head>
<body>
<noscript>
<div>
<img alt="" src="https://mc.yandex.ru/watch/25559621" style="position:absolute; left:-9999px;">
</div>
</noscript>
<header class="sticky-top bg-body">
<nav class="navbar navbar-expand-lg">
<div class="container-xxl">
<a class="navbar-brand" href="/"><img alt="Логотип Хекслета" height="24" src="https://ru.hexlet.io/vite/assets/logo_ru_light-BpiEA1LT.svg" width="96">
</a><button aria-controls="collapsable" aria-expanded="false" aria-label="Меню" class="navbar-toggler border-0 mb-0 mt-1" data-bs-target="#collapsable" data-bs-toggle="collapse">
<span class="navbar-toggler-icon"></span>
</button>
<div class="collapse navbar-collapse" id="collapsable">
<ul class="navbar-nav mb-lg-0 mt-lg-1">
<li class="nav-item dropdown">
<button aria-haspopup class="btn nav-link" data-bs-toggle="dropdown" type="button">
Все курсы
<span class="bi bi-chevron-down align-middle ms-1"></span>
</button>
<ul class="dropdown-menu">
<li>
<a class="dropdown-item d-flex py-2" href="/courses"><div class="fw-bold me-auto">Все что есть</div>
<div class="text-muted">117</div>
</a></li>
<li>
<hr class="dropdown-divider">
</li>
<li class="dropdown-item">
<b>Популярные категории</b>
</li>
<li>
<a class="dropdown-item py-2" href="/courses_devops">Курсы по DevOps
</a></li>
<li>
<a class="dropdown-item py-2" href="/courses_data_analytics">Курсы по аналитике данных
</a></li>
<li>
<a class="dropdown-item py-2" href="/courses_programming">Курсы по программированию
</a></li>
<li>
<a class="dropdown-item py-2" href="/courses_testing">Курсы по тестированию
</a></li>
<li>
<hr class="dropdown-divider">
</li>
<li class="dropdown-item">
<b>Популярные курсы</b>
</li>
<li>
<a class="dropdown-item py-2" href="/programs/devops-engineer-from-scratch">DevOps-инженер с нуля
</a></li>
<li>
<a class="dropdown-item py-2" href="/programs/go">Go-разработчик
</a></li>
<li>
<a class="dropdown-item py-2" href="/programs/java">Java-разработчик
</a></li>
<li>
<a class="dropdown-item py-2" href="/programs/python">Python-разработчик
</a></li>
<li>
<a class="dropdown-item py-2" href="/programs/qa-auto-engineer-java">Автоматизатор тестирования на Java
</a></li>
<li>
<a class="dropdown-item py-2" href="/programs/data-analytics">Аналитик данных
</a></li>
<li>
<a class="dropdown-item py-2" href="/programs/frontend">Фронтенд-разработчик
</a></li>
</ul>
</li>
<li class="nav-item dropdown">
<button aria-haspopup class="btn nav-link" data-bs-toggle="dropdown" type="button">
О Хекслете
<span class="bi bi-chevron-down align-middle"></span>
</button>
<ul class="dropdown-menu bg-body">
<li>
<a class="dropdown-item py-2" href="/pages/about">О нас
</a></li>
<li>
<a class="dropdown-item py-2" href="/blog">Блог
</a></li>
<li>
<span class="dropdown-item py-2 external-link" data-href="https://special.hexlet.io/hse-research" role="button">Результаты (Исследование)
</span></li>
<li>
<span class="dropdown-item py-2 external-link" data-href="https://career.hexlet.io" role="button">Хекслет Карьера
</span></li>
<li>
<a class="dropdown-item py-2" href="/testimonials">Отзывы студентов
</a></li>
<li>
<span class="dropdown-item py-2 external-link" data-href="https://t.me/hexlet_help_bot" role="button">Поддержка (В ТГ)
</span></li>
<li>
<span class="dropdown-item py-2 external-link" data-href="https://special.hexlet.io/referal-program/?promo_creative=priglasite-druzei&promo_name=referal-program&promo_position=promo_position&promo_start=010724&promo_type=link" role="button">Реферальная программа
</span></li>
<li>
<span class="dropdown-item py-2 external-link" data-href="https://special.hexlet.io/certificate" role="button">Подарочные сертификаты
</span></li>
<li>
<span class="dropdown-item py-2 external-link" data-href="https://hh.ru/employer/4307094" role="button">Вакансии
</span></li>
<li>
<span class="dropdown-item d-flex external-link" rel="noopener noreferrer nofollow" data-href="https://b2b.hexlet.io" data-target="_blank" role="button">Компаниям
</span></li>
<li>
<span class="dropdown-item d-flex external-link" rel="noopener noreferrer nofollow" data-href="https://hexly.ru/" data-target="_blank" role="button">Колледж
</span></li>
<li>
<span class="dropdown-item d-flex external-link" rel="noopener noreferrer nofollow" data-href="https://hexlyschool.ru/" data-target="_blank" role="button">Частная школа
</span></li>
</ul>
</li>
<li><a class="nav-link" href="/subscription/new">Подписка</a></li>
</ul>
<ul class="navbar-nav flex-lg-row align-items-lg-center gap-2 ms-auto">
<li>
<a class="nav-link" aria-label="Переключить тему" href="/theme/switch?new_theme=dark"><span aria-hidden="true" class="bi bi-moon"></span>
</a></li>
<li>
<span data-target="_self" class="nav-link external-link" data-href="/u/new" role="button"><span>Регистрация</span>
</span></li>
<li>
<span data-target="_self" class="nav-link external-link" data-href="https://ru.hexlet.io/session/new" role="button"><span>Вход</span>
</span></li>
</ul>
</div>
</div>
</nav>
</header>
<div class="x-container-xxxl">
</div>
<main class="mb-6 min-vh-100 h-100">
<link rel="preload" as="image" href="https://hexlet.io/rails/active_storage/representations/proxy/eyJfcmFpbHMiOnsiZGF0YSI6MzczMSwicHVyIjoiYmxvYl9pZCJ9fQ==--f5df4883f3f678321cb4fa96e9ce657bd5ee1adf/eyJfcmFpbHMiOnsiZGF0YSI6eyJmb3JtYXQiOiJ3ZWJwIiwicmVzaXplX3RvX2xpbWl0IjpbNDAwLDQwMF0sInNhdmVyIjp7InF1YWxpdHkiOjg1fX0sInB1ciI6InZhcmlhdGlvbiJ9fQ==--5b6f46dacd1af664f27558553a58076185091823/Static%20website-cuate.png"/><link rel="preload" as="image" href="https://hexlet.io/rails/active_storage/representations/proxy/eyJfcmFpbHMiOnsiZGF0YSI6MzkyMCwicHVyIjoiYmxvYl9pZCJ9fQ==--71cd9d863b21d7bfbd927cf623a7a2baaf4530ca/eyJfcmFpbHMiOnsiZGF0YSI6eyJmb3JtYXQiOiJ3ZWJwIiwicmVzaXplX3RvX2xpbWl0IjpbNDAwLDQwMF0sInNhdmVyIjp7InF1YWxpdHkiOjg1fX0sInB1ciI6InZhcmlhdGlvbiJ9fQ==--5b6f46dacd1af664f27558553a58076185091823/Bug%20fixing-cuate.png"/><link rel="preload" as="image" href="https://hexlet.io/rails/active_storage/representations/proxy/eyJfcmFpbHMiOnsiZGF0YSI6Mzk2NSwicHVyIjoiYmxvYl9pZCJ9fQ==--84278a1852c9c6fb13b80a69f395bac6e47a422e/eyJfcmFpbHMiOnsiZGF0YSI6eyJmb3JtYXQiOiJ3ZWJwIiwicmVzaXplX3RvX2xpbWl0IjpbNDAwLDQwMF0sInNhdmVyIjp7InF1YWxpdHkiOjg1fX0sInB1ciI6InZhcmlhdGlvbiJ9fQ==--5b6f46dacd1af664f27558553a58076185091823/Cloud%20sync-bro.png"/><link rel="preload" as="image" href="/vite/assets/development-BVihs_d5.png"/><div id="app" data-page="{"component":"web/courses/lessons/theory_unit","props":{"errors":{},"locale":"ru","language":"ru","httpsHost":"https://ru.hexlet.io","host":"ru.hexlet.io","colorScheme":"light","auth":{"user":{"id":null,"last_viewed_notification_id":null,"email":null,"state":null,"first_name":"","last_name":"","created_at":"2026-02-26T20:31:44.932Z","current_program":null,"current_team":null,"full_name":"","guest":true,"can_use_paid_features":false,"is_hexlet_employee":false,"sanitized_phone_number":"","can_subscribe":true,"can_renew_education":false}},"cloudflareTurnstileSiteKey":"0x4AAAAAAA15KmeFXzd2H0Xo","vkIdClientId":"51586979","yandexIdClientId":"88d071f1d3384eb4bd1deb37910235c7","formAuthToken":"Qw4z6YCQj-sbZt8RFdT9ktRtHHl-4ArqXY7vm00mfVOs3_jecu4ii60l-4kZ2w3lFGQx03bX9EjgbnXPHyGaPQ","topics":[{"id":45746,"title":"Добрый день!\nНе совсем понимаю в тестах момент со счетчиком:\n```\n assert xor(42) + xor(xor(42)) == 255\n assert counter == [2], \"No extra xor calls should be happened\"\n```\nПочему здесь не идет дальнейшее пополнение счетчика, функция же продолжает вызываться?","plain_title":"Добрый день! Не совсем понимаю в тестах момент со счетчиком: assert xor(42) + xor(xor(42)) == 255 assert counter == [2], \"No extra xor calls should be happened\" Почему здесь не идет дальнейшее пополнение счетчика, функция же продолжает вызываться? ","creator":{"public_name":"Анастасия Никонова","id":170043,"is_tutor":false},"comments":[{"creator":{"public_name":"Aleksei Pirogov","id":72206,"is_tutor":true},"id":98891,"body":"Продолжает вызываться **функция-обёртка**, которая внутри себя хранит запомненные значения и только **по необходимости** вызывает оригинальную функцию - если нужное значение ещё не было вычислено.\n\nВ этом и состоит суть задания - организовать такое запоминание. И тесты проверяют, правильно ли вы запоминаете значения и не вызываете ли функцию без необходимости.","topic_id":45746}],"communitable":{"parent_entity_name":null,"parent_entity_url":null,"entity_name":"Декораторы","entity_url":null,"active":true}},{"id":49074,"title":"https://ru.hexlet.io/code_reviews/335884\nМожет кто нибудь помочь ? в чем проблема? вроде все должно работать. ","plain_title":"https://ru.hexlet.io/code_reviews/335884 Может кто нибудь помочь ? в чем проблема? вроде все должно работать. ","creator":{"public_name":"S H","id":315391,"is_tutor":false},"comments":[{"creator":{"public_name":"S H","id":315391,"is_tutor":false},"id":105311,"body":"**Валентин Хомутенко**, \nЯ добавил return, но теперь уже совсем не понятная ошибка:\n> assert xor(xor(42)) == 42\nE assert 213 == 42\nE + where 213 = <function memoized.<locals>.inner at 0x7fb51d761af0>(42)\nE + where 42 = <function memoized.<locals>.inner at 0x7fb51d761af0>(42)\n\nКак тут вообще понимать что не так ? ","topic_id":49074},{"creator":{"public_name":"Валентин Хомутенко","id":294613,"is_tutor":true},"id":105298,"body":"Привет!\nВ коде есть ошибка. Можно попробовать скопировать себе и позапускать разные функции с этим декоратором и посмотреть вывод.","topic_id":49074},{"creator":{"public_name":"S H","id":315391,"is_tutor":false},"id":105307,"body":"**Валентин Хомутенко**, \nhttps://ru.hexlet.io/code_reviews/335884\n\nНе могу понять почему при вызове f(f(42)) первый элемент нормально проходит как INT, а второй уже как None (NoneType). Посути должен быть 42, а потом вротой элемент это результат первой функции 420. ","topic_id":49074},{"creator":{"public_name":"Валентин Хомутенко","id":294613,"is_tutor":true},"id":105588,"body":"Можно запустить в режиме отладки и посмотреть, что и почему происходит там.","topic_id":49074}],"communitable":{"parent_entity_name":null,"parent_entity_url":null,"entity_name":"Декораторы","entity_url":null,"active":true}},{"id":41343,"title":"Доброго дня. А можно такой вопрос:\n\nМне осталось непонятно, возможно ли вернуть изначалаьное использование функции?\n\nНапример, мы определеили какую-ту функцию (пусть называется def decorator), внутри написали обертку и вернули её из decorator. Дальше - написали ф-цию которую будем декорировать (например, def decorated) Приставили к ней @decorator, и теперь она будет при вызове исполнятся с этим декоратором. А что, если нужно чередовать последовательность поведения? Т.е. сначала вызать ее с @decorator, потом 3 раза в изначальном виде, и потом опять с @decorator, такое возможно? ","plain_title":"Доброго дня. А можно такой вопрос: Мне осталось непонятно, возможно ли вернуть изначалаьное использование функции? Например, мы определеили какую-ту функцию (пусть называется def decorator), внутри написали обертку и вернули её из decorator. Дальше - написали ф-цию которую будем декорировать (например, def decorated) Приставили к ней @decorator, и теперь она будет при вызове исполнятся с этим декоратором. А что, если нужно чередовать последовательность поведения? Т.е. сначала вызать ее с @decorator, потом 3 раза в изначальном виде, и потом опять с @decorator, такое возможно? ","creator":{"public_name":"Petr Che","id":147023,"is_tutor":false},"comments":[{"creator":{"public_name":"Aleksei Pirogov","id":72206,"is_tutor":true},"id":90121,"body":"Если функция уже обёрнута и автор декоратора не позаботился о том, чтобы сохранить ссылку на оригинальную функцию, то получить доступ к оригиналу будет сложно (хоть и возможно).\n\nВообще, если вам зочется \"иногда оборачивать, в иногда - нет\", то вам не нужен декоратор. Более того, если функция, которая работает так, как вы описали, то есть по какому-то внутреннему принципу меняет поведение, то я не завидую тем, кто будет эту функцию использовать. Такое поведение - худший вариант недетерминизма. ","topic_id":41343},{"creator":{"public_name":"Aleksei Pirogov","id":72206,"is_tutor":true},"id":90238,"body":"В принципе то, что вы описали, может быть полезно во время тестирования декоратора. Только не нужно в этом случае декоратор сразу применять, стоит использовать его как обычную функцию:\n```python\ndef decorator(function):\n ...\n\ndef function(arg):\n ...\n\n# где-то в тестах\n\nif condition:\n # применяем функцию с декоратором\n x = decorator(function)(y)\nelse:\n # применяем только функцию\n x = function(y)\n```","topic_id":41343},{"creator":{"public_name":"Petr Che","id":147023,"is_tutor":false},"id":90228,"body":"понятно, спасибо","topic_id":41343}],"communitable":{"parent_entity_name":null,"parent_entity_url":null,"entity_name":"Декораторы","entity_url":null,"active":true}},{"id":82909,"title":"Здравствуйте! Не смог решить упражнение, потому что проверка падала на `> assert counter == [2], \"No extra xor calls should be happened\"\nE AssertionError: No extra xor calls should be happened\nE assert [5] == [2]` . Взял подсказку и оказалось , что мое решение ничем не отличается от учительского, подскажите , где ошибка, либо , если ее нет, то запишите в очередь на устранения недочета такой вариант решения https://ru.hexlet.io/code_reviews/883906","plain_title":"Здравствуйте! Не смог решить упражнение, потому что проверка падала на > assert counter == [2], \"No extra xor calls should be happened\" E AssertionError: No extra xor calls should be happened E assert [5] == [2] . Взял подсказку и оказалось , что мое решение ничем не отличается от учительского, подскажите , где ошибка, либо , если ее нет, то запишите в очередь на устранения недочета такой вариант решения https://ru.hexlet.io/code_reviews/883906 ","creator":{"public_name":"Богдан Кустов","id":536145,"is_tutor":false},"comments":[{"creator":{"public_name":"Artyom Kropp","id":381127,"is_tutor":true},"id":168784,"body":"Добрый день! Она запоминает после объявления, но в вашем случае каждый раз при вызове `inner()` будет создаваться новый словарь.","topic_id":82909},{"creator":{"public_name":"Богдан Кустов","id":536145,"is_tutor":false},"id":168608,"body":"Понял в чем разница, у меня memory = {} был внутри функции замыкания, а у учителя в внешней функции memoized. Обьясните , пожалуййста , почему это работает , если сказано , что функция-замыкание запоминает все уже после ее обьявления, а не до?","topic_id":82909}],"communitable":{"parent_entity_name":null,"parent_entity_url":null,"entity_name":"Декораторы","entity_url":null,"active":true}},{"id":45496,"title":"Добрый день! Задачу решил, но код получился громоздкий (https://ru.hexlet.io/code_reviews/293829), так как у меня по нормальному изменить глобальную переменную не получалось, я обратился к ней через inner.dict.\nВообще я гуглил счетчик, чтобы понять механизм, dict это я уже сам придумал, и это заработало, вот только что это получилось(я обратился к методу функции?)? Не подскажете, где я могу про это почитать?","plain_title":"Добрый день! Задачу решил, но код получился громоздкий (https://ru.hexlet.io/code_reviews/293829), так как у меня по нормальному изменить глобальную переменную не получалось, я обратился к ней через inner.dict. Вообще я гуглил счетчик, чтобы понять механизм, dict это я уже сам придумал, и это заработало, вот только что это получилось(я обратился к методу функции?)? Не подскажете, где я могу про это почитать? ","creator":{"public_name":"Mikhail Korolkevich","id":143361,"is_tutor":false},"comments":[{"creator":{"public_name":"Aleksei Pirogov","id":72206,"is_tutor":true},"id":98390,"body":"Вам не нужна глобальная переменная, вам нужно замыкание! Внутренняя функция должна замыкать внешнюю по отношении к ней переменную, хранящую словарь.","topic_id":45496},{"creator":{"public_name":"Aleksei Pirogov","id":72206,"is_tutor":true},"id":98418,"body":"> глобальная она по отношению к замыканию\n\nЭто называется \"свободная переменная\" :) Всё же \"глобальные переменные\" - глобальные для всех, а не для чего-то одного.\n\n> но я не понял, что это такое \"inner.dict\"\n\nВы добавили объекту функции `inner` атрибут `dict`. Подробнее про объекты и атрибуты вы узнаете в курсе по ООП. А пока вы можете считать, что это тоже своего рода глобальная переменная: она видна везде, где видна сама функция. Замыкания в этом плане сильнее защищены от внешнего доступа.","topic_id":45496},{"creator":{"public_name":"Mikhail Korolkevich","id":143361,"is_tutor":false},"id":98399,"body":"Я не правильно выразился, глобальная она по отношению к замыканию. В решении я не задействовал глобальных переменных в прямом смысле слова, но я не понял, что это такое \"inner.dict\"","topic_id":45496}],"communitable":{"parent_entity_name":null,"parent_entity_url":null,"entity_name":"Декораторы","entity_url":null,"active":true}},{"id":42950,"title":"Весь день на пролет сидел, но так пока и не смог победить это упражнение(((. Куда копать? Пытался гуглить - ссылки на self и тд. Решаем теми методами, которые изучали, или вперед лезть надо?","plain_title":"Весь день на пролет сидел, но так пока и не смог победить это упражнение(((. Куда копать? Пытался гуглить - ссылки на self и тд. Решаем теми методами, которые изучали, или вперед лезть надо? ","creator":{"public_name":"poul BELOBORODOV","id":149861,"is_tutor":false},"comments":[{"creator":{"public_name":"poul BELOBORODOV","id":149861,"is_tutor":false},"id":93387,"body":"В итоге - бубен был большой! Я это сделал... Но обьясните, почему словарь не переинациализируется с новым вызовом функции?","topic_id":42950},{"creator":{"public_name":"Aleksei Pirogov","id":72206,"is_tutor":true},"id":93448,"body":"> Решаем теми методами, которые изучали, или вперед лезть надо?\n\nТеми, которые изучали. Упражнения к урокам не забегают вперёд.\n\n> Но обьясните, почему словарь не переинациализируется с новым вызовом функции?\n\nКакой словарь вы имеете в виду?","topic_id":42950}],"communitable":{"parent_entity_name":null,"parent_entity_url":null,"entity_name":"Декораторы","entity_url":null,"active":true}},{"id":40812,"title":"https://ru.hexlet.io/code_reviews/245481 подскажите куда двинуться :) ступор какой то:))","plain_title":"https://ru.hexlet.io/code_reviews/245481 подскажите куда двинуться :) ступор какой то:)) ","creator":{"public_name":"","id":236391,"is_tutor":false},"comments":[{"creator":{"public_name":"Aleksei Pirogov","id":72206,"is_tutor":true},"id":89132,"body":"В задании сказано, что функция будет с одним аргументом, а вы `*args, **kwargs` сделали. Это вас и путает. Сделайте один аргумент. Потом придумайте, как запоминать для этого аргумента результат.","topic_id":40812}],"communitable":{"parent_entity_name":null,"parent_entity_url":null,"entity_name":"Декораторы","entity_url":null,"active":true}},{"id":48284,"title":"> Заметьте, в определении inner я использовал аргументы *args, **kwargs, которые функция без изменения передаёт замкнутой функции function — именно так в Python объявляют \"всеядные\" функции, которые \"пробрасывают\" любые комбинации аргументов.\n\nВот эту информацию, да в подсказку упражнения \"Функции высшего порядка\" бы ;) А то фиг разберешь без опыта..\n\nПлюс по заданию. В памяти отложилась немного иная мемоизация: когда вызываешь метод с одним и тем же аргументом два раза и более подряд, то не происходят расчеты, а выдается результат. Однако, если вызывать другой аргумент, а потом вызвать опять предыдущий, то расчеты все равно выполняются. Засим может стоит добавить в пример еще пару строчек:\n\n```\n>>> f(1)\nCalculating...\n10\n>>> f(1)\n10\n>>> f(42)\nCalculating...\n420\n>>> f(42)\n420\n>>> f(1) ---- вот\n10 ---- это\n>>>\n\n```\n\nЛибо подсказать про словарь для особо умных людей, как я.","plain_title":"Заметьте, в определении inner я использовал аргументы args, *kwargs, которые функция без изменения передаёт замкнутой функции function — именно так в Python объявляют \"всеядные\" функции, которые \"пробрасывают\" любые комбинации аргументов. Вот эту информацию, да в подсказку упражнения \"Функции высшего порядка\" бы ;) А то фиг разберешь без опыта.. ","creator":{"public_name":"Павел Ким","id":50933,"is_tutor":false},"comments":[{"creator":{"public_name":"Сергей К.","id":5174,"is_tutor":false},"id":106306,"body":"Павел, спасибо! Я поправил пример в описании задачи.","topic_id":48284}],"communitable":{"parent_entity_name":null,"parent_entity_url":null,"entity_name":"Декораторы","entity_url":null,"active":true}},{"id":44262,"title":"Замыкани и декораторы даются сложно, но постепенно вроде понимание приходит.\nРешил опять в \"лоб\" через кучу проверок\nhttps://ru.hexlet.io/code_reviews/280542","plain_title":"Замыкани и декораторы даются сложно, но постепенно вроде понимание приходит. Решил опять в \"лоб\" через кучу проверок https://ru.hexlet.io/code_reviews/280542 ","creator":{"public_name":"Александр Сердюк","id":272261,"is_tutor":false},"comments":[{"creator":{"public_name":"Aleksei Pirogov","id":72206,"is_tutor":true},"id":95799,"body":"Главное, что решили в итоге! :)","topic_id":44262}],"communitable":{"parent_entity_name":null,"parent_entity_url":null,"entity_name":"Декораторы","entity_url":null,"active":true}},{"id":41756,"title":"\nЗастряла с этим запоминанием значений. Предполагаю, что нужна некая библиотека, в которую будут отправляться на хранение уже вычисленные ранее результаты. Это было бы наверное удобно делать через associative array. И из него отправлять готовый результат без вычислений если пришел повторный запрос. Но я никак не пойму куда втыкнуть эту самую библиотеку чтобы она собирала это все и не сбрасывала накопленные значения...\nВот ревью: https://ru.hexlet.io/code_reviews/255775?submission_id=323784 В интерпретаторе с примером из текста задания работает. Здесь тесты не проходит. Может все таки я не в том направлении думаю?","plain_title":"https://ru.hexlet.io/code_reviews/255775 Застряла с этим запоминанием значений. Предполагаю, что нужна некая библиотека, в которую будут отправляться на хранение уже вычисленные ранее результаты. Это было бы наверное удобно делать через associative array. И из него отправлять готовый результат без вычислений если пришел повторный запрос. Но я никак не пойму куда втыкнуть эту самую библиотеку чтобы она собирала это все и не сбрасывала накопленные значения... ","creator":{"public_name":"","id":264563,"is_tutor":false},"comments":[{"creator":{"public_name":"","id":264563,"is_tutor":false},"id":90937,"body":"Вот ревью: https://ru.hexlet.io/code_reviews/255775?submission_id=323784\nВ интерпретаторе с примером из текста задания работает. Здесь тесты не проходит. Может все таки я не в том направлении думаю?","topic_id":41756},{"creator":{"public_name":"","id":264563,"is_tutor":false},"id":91064,"body":"**Aleksei Pirogov**, тьфу ты) в начале название было правильным, а потом мне в голову пришло, что это от слова memory)))\nРешила - единственное до конца не поняла по какой причине словарь не сбрасывает значения на пустой при каждом запуске функции. ","topic_id":41756},{"creator":{"public_name":"Aleksei Pirogov","id":72206,"is_tutor":true},"id":91065,"body":"Я там тестов добавил. Сохраните свой код куда-нибудь, сбросьте прогресс упражнения, и попробуйте заново пройти практику. Есть ощущение, что новые тесты ваш код не пройдёт, если \"словарь не сбрасывает значения\" :)","topic_id":41756},{"creator":{"public_name":"Aleksei Pirogov","id":72206,"is_tutor":true},"id":91008,"body":"Ах да, тесты у вас падают всего лишь из-за того, что вы функцию назвали неверно: \"memoRized\" вместо \"memoized\". Нет в информатике \"momoRization\", есть \"memoization\" :)","topic_id":41756},{"creator":{"public_name":"Aleksei Pirogov","id":72206,"is_tutor":true},"id":91068,"body":"Спасибо за наводку, вместе сделали задание лучше!","topic_id":41756},{"creator":{"public_name":"Aleksei Pirogov","id":72206,"is_tutor":true},"id":91005,"body":"Библиотека не нужна. Всё, что нужно для решения, уже есть в языке. Нет никаких associative arrays в Python, есть словари (это ассоциативные массивы и есть, если что)!\n\nПроблема вашего решения в том, **где** вы храните словарь, запоминающий значения. Вы не должны этот словарь хранить в глобальной переменной, но вы именно это и делаете. Глобальный словарь будет один для всех мемоизированных функций, что, конечно же, не может быть правильным решением, потому что в нём одном смешаются результаты вызова всех функций.\n\nВ остальном ваш код похож на то, что было и нужно написать!","topic_id":41756},{"creator":{"public_name":"","id":264563,"is_tutor":false},"id":91067,"body":"**Aleksei Pirogov**, да - теперь первый вариант кода не работает. Теперь не схалявить))) С перебросом одной строчки в другое место все заработало)","topic_id":41756}],"communitable":{"parent_entity_name":null,"parent_entity_url":null,"entity_name":"Декораторы","entity_url":null,"active":true}}],"lesson":{"exercise":{"id":1117,"slug":"python_functions_decorators_exercise","name":null,"state":"active","kind":"exercise","language":"python","locale":"ru","has_web_view":false,"has_test_view":false,"reviewable":true,"readme":"## src/solution.py\n\nВам предстоит реализовать декоратор, добавляющий функции *мемоизацию*. Мемоизация — это запоминание уже вычисленных результатов для уже однажды встреченных аргументов.\n\nДля простоты будем считать, что мемоизироваться будут численные функции одного аргумента (аргумент — число, результат — то же число). Полученный результат должен возвращаться.\n\n```python\nfrom solution import memoized\n@memoized\ndef f(x):\n print('Calculating...')\n return x * 10\n\nf(1)\n# => Calculating...\n# 10\nf(1)\n# 10\nf(42)\n# => Calculating...\n# 420\nf(42)\n# 420\nf(1)\n# 10\n```\n\nЗаметьте, что для каждого нового аргумента выводится сообщение \"Calculating...\", но только лишь один раз.\n","prepared_readme":"## src/solution.py\n\nВам предстоит реализовать декоратор, добавляющий функции *мемоизацию*. Мемоизация — это запоминание уже вычисленных результатов для уже однажды встреченных аргументов.\n\nДля простоты будем считать, что мемоизироваться будут численные функции одного аргумента (аргумент — число, результат — то же число). Полученный результат должен возвращаться.\n\n```python\nfrom solution import memoized\n@memoized\ndef f(x):\n print('Calculating...')\n return x * 10\n\nf(1)\n# => Calculating...\n# 10\nf(1)\n# 10\nf(42)\n# => Calculating...\n# 420\nf(42)\n# 420\nf(1)\n# 10\n```\n\nЗаметьте, что для каждого нового аргумента выводится сообщение \"Calculating...\", но только лишь один раз.\n","has_solution":true,"entity_name":"Декораторы"},"units":[{"id":3266,"name":"theory","url":"/courses/python-functions/lessons/decorators/theory_unit"},{"id":3713,"name":"quiz","url":"/courses/python-functions/lessons/decorators/quiz_unit"},{"id":3716,"name":"exercise","url":"/courses/python-functions/lessons/decorators/exercise_unit"}],"links":[],"ordered_units":[{"id":3266,"name":"theory","url":"/courses/python-functions/lessons/decorators/theory_unit"},{"id":3713,"name":"quiz","url":"/courses/python-functions/lessons/decorators/quiz_unit"},{"id":3716,"name":"exercise","url":"/courses/python-functions/lessons/decorators/exercise_unit"}],"id":1545,"slug":"decorators","state":"approved","name":"Декораторы","course_order":190,"goal":"Узнаем о декораторах — интересном и мощном инструменте, который выделяет Python среди многих других языков, поддерживающих использование ФВП","self_study":null,"theory_video_provider":null,"theory_video_uid":null,"theory":"В программировании часто возникают задачи, когда нужно добавить поведение к уже существующим функциям или классам. Например, логирование, проверку входных данных или замер времени выполнения функции. В таких случаях использование декораторов может значительно упростить решение задачи.\n\nВ этом уроке мы рассмотрим, что такое декораторы и как их использовать, чтобы добавлять дополнительную функциональность к существующим функциям.\n\n## Что такое декораторы\n\nРанее мы рассматривали как можно \"запомнить\" передаваемые в функцию значения с помощью замыканий. Напомним как выглядит синтаксис:\n\n```python\ndef outer(arg1):\n def inner(arg2):\n return arg1 + arg2\n\n return inner\n\n\ntwo_add = outer(2)\ntwo_add(1) # 3\ntwo_add(3) # 5\ntwo_add(5) # 7\n```\n\nВ этом примере мы создаем функцию `outer()`, которая принимает аргумент `arg1` и возвращает внутреннюю функцию `inner()`. Внутренняя функция принимает аргумент `arg2` и возвращает сумму `arg1` и `agr2`.\n\nМожет запутать одновременное определение функции и возврат ее же. Но если мы вспомним, что определение функции это присваивание функции имени переменной, а также, что функции это данные, то пример можно представить как:\n\n```python\ndef outer(arg1):\n inner = lambda arg2: arg1 + arg2\n return inner\n```\n\nМы заменили именованную функцию лямбдой, и теперь присваивание ничем не отличается, как если бы мы присвоили переменной `inner` число или строку.\n\nНо если функции всего лишь данные, то можно ли их также \"запоминать\" в замыканиях?\n\n```python\ndef outer(func):\n def inner(arg):\n print(\"func = \", func)\n print(\"arg = \", arg)\n return func(arg)\n\n return inner\n\n\ndef square(x):\n return x**2\n\n\nprinter = outer(square)\n\nprinter(1) # 1\n# => func = <function square at 0x75e04623a7a0>\n# => arg = 1\n\nprinter(2) # 4\n# => func = <function square at 0x75e04623a7a0>\n# => arg = 2\n\nprinter(4) # 16\n# => func = <function square at 0x75e04623a7a0>\n# => arg = 4\n```\n\nОстановимся и разберем пример выше. В нем мы создаем замыкание, но теперь, вместо числа в замыкание передается функция. Внешняя функция запомнила переданную в нее функцию. Осталось лишь передать аргументы и вызвать замкнутую функцию с ними. В дополнение, мы смогли распечатать переданную функцию и ее аргументы, при этом код самой функции `square()` мы не меняли.\n\nФункции выше называются декораторами. **Декораторы** в Python — это функции, которые принимают другую функцию в качестве аргумента, добавляют к ней дополнительную функциональность и возвращают функцию с измененным поведением. Декораторы позволяют изменять поведение функций и классов с помощью добавления или изменения их функциональности без изменения самого кода.\n\nТакже, как вы могли заметить, декораторы это частный случай функции высшего порядка (принимаем функцию) и использования замыкания вместе.\n\n## Использование декораторов\n\nДекораторы используются в Python повсеместно: для логирования, кеширования, добавления нового функционала, используются в тестовых и веб-фреймворках.\n\nПредположим, что у нас есть функция, которая суммирует числа:\n\n```python\ndef sum(*nums):\n result = 0\n for num in nums:\n result += num\n return result\n```\n\nСоздадим декоратор, который добавит к этой функции функциональность для отладки:\n\n```python\ndef debug_decorator(func):\n # внутреннюю функцию принято называть wrapper - обертка\n def wrapper(*args, **kwargs):\n print(\"Args:\", args, kwargs)\n result = func(*args, **kwargs)\n print(\"Result:\", result)\n return result\n\n return wrapper\n```\n\nЭтот декоратор принимает функцию в качестве аргумента и возвращает новую функцию-обертку, которая добавляет отладочные сообщения в процесс выполнения исходной функции. Заметьте, что внутренняя функция принимает сразу все аргументы с помощью `*args` и `**kwargs`. Так мы можем создавать \"всеядные\" обертки.\n\nПрименим этот декоратор к функции `sum()`:\n\n```python\ndebugged_sum = debug_decorator(sum)\n\ndebugged_sum(1, 2, 3) # 6\n\n# => Args: (1, 2, 3) {}\n# => Result: 6\n```\n\nПроблема в коде выше, что теперь функция называется `debugged_sum()`, и нужно будет изменить весь код, что использовал `sum()`. Но ведь имя функции это всего лишь имя переменной, и мы можем перезаписать новую, декорированную функцию в ту же переменную.\n\n\n```python\nsum = debug_decorator(sum)\n\nsum(1, 2, 3) # 6\n# => Args: (1, 2, 3) {}\n# => Result: 6\n\n\ndef sum_odd_numbers(nums):\n odd = []\n for num in nums:\n if num % 2 != 0:\n odd.append(num)\n return sum(*odd)\n\n\nnumbers = [1, 2, 3, 4, 5]\nsum_odd_numbers(numbers) # 9\n# => Args: (1, 3, 5) {}\n# => Result: 9\n```\n\nТеперь функция `sum()` будет выполняться с дополнительными отладочными сообщениями. А также весь код, использующий функцию `sum()` будет использовать новую функцию.\n\nЧтобы не добавлять запись вида `func = decorator(func)` каждый раз, в Python добавили синтаксический сахар - `@decorator`\n\n```python\n# декоратор нужно добавить над определением функции, которую хотим декорировать\n@debug_decorator\ndef sum(*nums):\n result = 0\n for num in nums:\n result += num\n return result\n\n\nsum(1, 2) # 3\n# => Args: (1, 2) {}\n# => Result: 3\n```\n\nТакже мы можем создавать несколько декораторов для одной функции, которые будут применяться последовательно:\n\n```python\n@debug_decorator\n@time_decorator\ndef sum(*nums):\n result = 0\n for num in nums:\n result += num\n return result\n```\n\nВ этом примере сначала применится декоратор времени выполнения, а затем отладочный декоратор.\n\n## Состояние\n\nОтличительная черта функций, что они не хранят состояния. Функции, мы говорим о чистых функциях, лишь преобразуют входные данные в выходные. Или как еще говорят, \"отображают\" входное множество на выходное.\n\nНо замыкания позволяют запоминать значения и использовать их в последующих вычислениях.\n\n```python\n# Чистая функция - просто сортирует задачи по приоритету\ndef sort_tasks(tasks):\n return sorted(tasks, key=lambda x: x[\"priority\"], reverse=True)\n\n\n# Замыкание - хранит задачи и позволяет управлять ими\ndef create_task_manager():\n tasks = [] # хранилище задач в замыкании\n\n def manage(action, task=None):\n if action == \"add\" and task:\n tasks.append(task)\n elif action == \"remove\" and task:\n tasks.remove(task)\n elif action == \"clear\":\n tasks.clear()\n elif action == \"get_high_priority\":\n return [t for t in tasks if t[\"priority\"] >= 8]\n\n return sorted(tasks, key=lambda x: x[\"priority\"], reverse=True)\n\n return manage\n\n\n# Чистая функция - каждый раз новый результат\ntasks_list = [{\"name\": \"Task 1\", \"priority\": 5}, {\"name\": \"Task 2\", \"priority\": 9}]\nprint(sort_tasks(tasks_list)) # всегда один и тот же вывод для одних и тех же данных\n\n# Замыкание - хранит состояние и позволяет редактировать список задач\nmanager = create_task_manager()\nprint(\n manager(\"add\", {\"name\": \"Task 1\", \"priority\": 5})\n) # [{'name': 'Task 1', 'priority': 5}]\nprint(\n manager(\"add\", {\"name\": \"Task 2\", \"priority\": 9})\n) # [{'name': 'Task 2', 'priority': 9}, {'name': 'Task 1', 'priority': 5}]\nprint(manager(\"get_high_priority\")) # [{'name': 'Task 2', 'priority': 9}]\nprint(manager(\"clear\")) # []\n```\n\nЗамыкания, как и декораторы, позволяют нам создавать на базе функций собственные объекты и систему управления ими.\n\n## Декораторы с параметрами\n\nЧто если мы хотели бы вызывать обернутую функцию с разными настройками? Для этого нам понадобятся декораторы с параметрами.\n\nОбщий синтаксис декоратора с параметрами выглядит так:\n\n\n```python\ndef decorator_with_params(param1, param2, ...):\n def actual_decorator(func):\n def wrapper(*args, **kwargs):\n # Здесь можем использовать param1, param2, ...\n result = func(*args, **kwargs)\n return result\n return wrapper\n return actual_decorator\n```\n\nК обычному декоратору мы добавляем еще один слой с параметрами. И, конечно, в качестве параметров мы можем передавать другие функции:\n\n```python\ndef filter_by(key):\n def inner(func):\n def wrapper(*args):\n args = key(args)\n print(\"filtered args = \", args)\n return func(*args)\n\n return wrapper\n\n return inner\n\n\ndef odd(numbers):\n result = []\n for num in numbers:\n if num % 2 == 1:\n result.append(num)\n return result\n\n\n@filter_by(odd)\ndef sum(*nums):\n result = 0\n for num in nums:\n result += num\n return result\n\n\nsum(3, 4, 5, 6) # 8\n# => filtered args = [3, 5]\n```\n\nИ даже функции с собственными параметрами, используя замыкания:\n\n```python\ndef less_than(max):\n def inner(numbers):\n result = []\n for num in numbers:\n if num < max:\n result.append(num)\n return result\n\n return inner\n\n\n@filter_by(less_than(5))\ndef sum(*nums):\n result = 0\n for num in nums:\n result += num\n return result\n\n\nsum(3, 4, 5, 6) # 7\n# => filtered args = [3, 4]\n\n\n@filter_by(less_than(3))\ndef sum(*nums):\n result = 0\n for num in nums:\n result += num\n return result\n\n\nsum(3, 4, 5, 6) # 0\n# => filtered args = []\n```\n\nДекоратор `filter_by()` принимает функцию фильтрации `less_than()`, затем применяет ее к аргументам обернутой функции, и вызывает обернутую функцию с новыми аргументами.\n\n## Выводы\n\nМы изучили декораторы, мощный механизм для расширения функций. Существует большое количество готовых декораторов, доступных в стандартной библиотеке Python и других библиотеках. Некоторые из них позволяют кэшировать результаты функций, обеспечивать авторизацию и безопасность, профилировать код, проверять типы данных и многое другое.\n\nДекораторы могут повлиять на производительность кода, поэтому их следует использовать с умом и осторожностью. Также нужно учитывать, что декораторы могут усложнить отладку кода и сделать его менее понятным для других разработчиков.\n\nПри этом декораторы являются сильным инструментом, который позволяет легко добавлять дополнительную функциональность к существующим функциям и классам и не изменять их исходный код.\n"},"lessonMember":null,"courseMember":null,"course":{"start_lesson":{"exercise":null,"units":[{"id":3088,"name":"theory","url":"/courses/python-functions/lessons/about/theory_unit"}],"links":[],"ordered_units":[{"id":3088,"name":"theory","url":"/courses/python-functions/lessons/about/theory_unit"}],"id":1461,"slug":"about","state":"approved","name":"Введение","course_order":100,"goal":"Узнаем о курсе, его структуре, задачах и целях","self_study":null,"theory_video_provider":null,"theory_video_uid":null,"theory":"Функции — мощный инструмент с широкими возможностями. До сих пор мы сталкивались с самыми простыми видами функций — именованными. Кроме них в Python есть анонимные функции, которые значительно расширяют возможности языка.\n\nПри описании и вызове функций мы пользовались в основном позиционными аргументами, которые передаются в функцию в строго определенном порядке. Но кроме позиционных Python позволяет функциям иметь и именованные параметры. А еще функции в Python могут принимать переменное количество параметров или все параметры вместе внутри объекта-коллекции.\n\nФункции в Python — это объекты первого класса (first class objects). Они могут передаваться и приниматься по ссылке и даже конструироваться «на лету». В этом курсе мы рассмотрим работу с такими функциями поподробнее. А еще узнаем об интересной и мощной концепции декораторов, которая позволяет делать с функциями множество интересных вещей.\n\nОсновные понятия курса:\n\n* Позиционные параметры и аргументы\n* Переменное количество параметров\n* Объекты первого класса\n* Анонимные функции\n* Декораторы\n* Ленивые вычисления\n* Функции высшего порядка\n"},"id":186,"slug":"python-functions","challenges_count":14,"name":"Python: Функции","allow_indexing":true,"state":"approved","course_state":"finished","pricing_type":"paid","description":"На этом курсе вы изучите функции. Вы узнаете, что такое объекты первого рода, безымянные лямбда-функции и функции высшего порядка. Вы научитесь использовать функции внутри других функций, использовать встроенные операции map, filter, reduce и писать обертки для расширения функционала. Знания из этого курса помогут проектировать лаконичные и эффективные программы, отделять побочные эффекты от чистого кода.","kind":"additional","updated_at":"2026-01-20T11:45:54.235Z","language":"python","duration_cache":66900,"skills":["Использовать продвинутые возможности функций для написания лаконичного и эффективного кода","Отделять чистые функции от функций с побочными эффектами","Использовать функции высшего порядка (map/filter/reduce) для обработки списков","Отличать императивную парадигму программирования от декларативной (функциональной)","Реализовывать и применять декораторы к функциям"],"keywords":["деструктуризация","функции высшего порядка","замыкания","чистые функции","декораторы"],"lessons_count":15,"cover":"https://hexlet.io/rails/active_storage/representations/proxy/eyJfcmFpbHMiOnsiZGF0YSI6NzQwNywicHVyIjoiYmxvYl9pZCJ9fQ==--91d5004483c1f6b05d835e90bda1494e87c4dc44/eyJfcmFpbHMiOnsiZGF0YSI6eyJmb3JtYXQiOiJwbmciLCJyZXNpemVfdG9fZmlsbCI6WzYwMCw0MDBdfSwicHVyIjoidmFyaWF0aW9uIn19--6067466c2912ca31a17eddee04b8cf2a38c6ad17/image.png"},"recommendedLandings":[{"stack":{"id":7,"slug":"python","title":"Python-разработчик","audience":"for_beginners","start_type":"weekly","pricing_model":"purchase","priority":"high","kind":"profession","state":"published","stack_state":"finished","order":10,"duration_in_months":10},"id":7,"slug":"python","title":"Python-разработчик ","subtitle":"Изучите Python, Django, REST и Fast API для создания веб-приложений","subtitle_for_lists":"Изучите Python, Django, REST и Fast API для создания веб-приложений","locale":"ru","current":true,"duration_in_months_text":"10 месяцев","stack_slug":"python","price_text":"от 6 792 ₽","duration_text":"10 месяцев","cover_list_variant":"https://hexlet.io/rails/active_storage/representations/proxy/eyJfcmFpbHMiOnsiZGF0YSI6MzczMSwicHVyIjoiYmxvYl9pZCJ9fQ==--f5df4883f3f678321cb4fa96e9ce657bd5ee1adf/eyJfcmFpbHMiOnsiZGF0YSI6eyJmb3JtYXQiOiJ3ZWJwIiwicmVzaXplX3RvX2xpbWl0IjpbNDAwLDQwMF0sInNhdmVyIjp7InF1YWxpdHkiOjg1fX0sInB1ciI6InZhcmlhdGlvbiJ9fQ==--5b6f46dacd1af664f27558553a58076185091823/Static%20website-cuate.png"},{"stack":{"id":220,"slug":"qa-auto-engineer-python","title":"Автоматизатор тестирования на Python","audience":"for_programmers","start_type":"weekly","pricing_model":"purchase","priority":"high","kind":"profession","state":"published","stack_state":"finished","order":100,"duration_in_months":6},"id":331,"slug":"qa-auto-engineer-python","title":"Автоматизатор тестирования на Python","subtitle":"Изучите Python, фреймворки для тестирования, автоматизация UI и API","subtitle_for_lists":"Изучите Python, фреймворки для тестирования, автоматизация UI и API","locale":"ru","current":true,"duration_in_months_text":"6 месяцев","stack_slug":"qa-auto-engineer-python","price_text":"от 4 281 ₽","duration_text":"6 месяцев","cover_list_variant":"https://hexlet.io/rails/active_storage/representations/proxy/eyJfcmFpbHMiOnsiZGF0YSI6MzkyMCwicHVyIjoiYmxvYl9pZCJ9fQ==--71cd9d863b21d7bfbd927cf623a7a2baaf4530ca/eyJfcmFpbHMiOnsiZGF0YSI6eyJmb3JtYXQiOiJ3ZWJwIiwicmVzaXplX3RvX2xpbWl0IjpbNDAwLDQwMF0sInNhdmVyIjp7InF1YWxpdHkiOjg1fX0sInB1ciI6InZhcmlhdGlvbiJ9fQ==--5b6f46dacd1af664f27558553a58076185091823/Bug%20fixing-cuate.png"},{"stack":{"id":225,"slug":"devops-engineer-from-scratch","title":"DevOps-инженер с нуля","audience":"for_beginners","start_type":"weekly","pricing_model":"purchase","priority":"high","kind":"profession","state":"published","stack_state":"not_finished","order":50,"duration_in_months":14},"id":355,"slug":"devops-engineer-from-scratch","title":"DevOps-инженер с нуля","subtitle":"Полное погружение в DevOps: весь стек от Linux до Kubernetes","subtitle_for_lists":"Полное погружение в DevOps: весь стек от Linux до Kubernetes","locale":"ru","current":true,"duration_in_months_text":"14 месяцев","stack_slug":"devops-engineer-from-scratch","price_text":"от 6 792 ₽","duration_text":"14 месяцев","cover_list_variant":"https://hexlet.io/rails/active_storage/representations/proxy/eyJfcmFpbHMiOnsiZGF0YSI6Mzk2NSwicHVyIjoiYmxvYl9pZCJ9fQ==--84278a1852c9c6fb13b80a69f395bac6e47a422e/eyJfcmFpbHMiOnsiZGF0YSI6eyJmb3JtYXQiOiJ3ZWJwIiwicmVzaXplX3RvX2xpbWl0IjpbNDAwLDQwMF0sInNhdmVyIjp7InF1YWxpdHkiOjg1fX0sInB1ciI6InZhcmlhdGlvbiJ9fQ==--5b6f46dacd1af664f27558553a58076185091823/Cloud%20sync-bro.png"}],"lessonMemberUnit":null,"accessToLearnUnitExists":false,"accessToCourseExists":false},"url":"/courses/python-functions/lessons/decorators/theory_unit","version":"8f286f6358a90a7bef2263b3a6edf5a90a94fa42","encryptHistory":false,"clearHistory":false}"><style data-mantine-styles="true">:root, :host{--mantine-font-family: Arial, sans-serif;--mantine-font-family-headings: Arial, sans-serif;--mantine-heading-font-weight: normal;--mantine-radius-default: 0rem;--mantine-primary-color-filled: var(--mantine-color-indigo-filled);--mantine-primary-color-filled-hover: var(--mantine-color-indigo-filled-hover);--mantine-primary-color-light: var(--mantine-color-indigo-light);--mantine-primary-color-light-hover: var(--mantine-color-indigo-light-hover);--mantine-primary-color-light-color: var(--mantine-color-indigo-light-color);--mantine-spacing-xxl: calc(4rem * var(--mantine-scale));--mantine-font-size-xs: 12px;--mantine-font-size-sm: 14px;--mantine-font-size-md: 16px;--mantine-font-size-lg: clamp(16.0000px, calc(15.2727px + 0.2273vw), 18.0000px);--mantine-font-size-xl: clamp(16.0000px, calc(14.5455px + 0.4545vw), 20.0000px);--mantine-font-size-display-3: clamp(32.0000px, calc(26.1818px + 1.8182vw), 48.0000px);--mantine-font-size-display-2: clamp(36.0000px, calc(25.8182px + 3.1818vw), 64.0000px);--mantine-font-size-display-1: clamp(40.0000px, calc(25.4545px + 4.5455vw), 80.0000px);--mantine-font-size-h1: clamp(28.0000px, calc(23.6364px + 1.3636vw), 40.0000px);--mantine-font-size-h2: clamp(24.0000px, calc(21.0909px + 0.9091vw), 32.0000px);--mantine-font-size-h3: clamp(20.0000px, calc(17.0909px + 0.9091vw), 28.0000px);--mantine-font-size-h4: clamp(16.0000px, calc(13.0909px + 0.9091vw), 24.0000px);--mantine-font-size-h5: clamp(16.0000px, calc(14.5455px + 0.4545vw), 20.0000px);--mantine-font-size-h6: 1rem;--mantine-primary-color-0: var(--mantine-color-indigo-0);--mantine-primary-color-1: var(--mantine-color-indigo-1);--mantine-primary-color-2: var(--mantine-color-indigo-2);--mantine-primary-color-3: var(--mantine-color-indigo-3);--mantine-primary-color-4: var(--mantine-color-indigo-4);--mantine-primary-color-5: var(--mantine-color-indigo-5);--mantine-primary-color-6: var(--mantine-color-indigo-6);--mantine-primary-color-7: var(--mantine-color-indigo-7);--mantine-primary-color-8: var(--mantine-color-indigo-8);--mantine-primary-color-9: var(--mantine-color-indigo-9);--mantine-color-red-0: #ffeaea;--mantine-color-red-1: #fed4d4;--mantine-color-red-2: #f4a7a8;--mantine-color-red-3: #ec7878;--mantine-color-red-4: #e55050;--mantine-color-red-5: #e03131;--mantine-color-red-6: #e02829;--mantine-color-red-7: #c71a1c;--mantine-color-red-8: #b21218;--mantine-color-red-9: #9c0411;--mantine-color-violet-0: #fce9ff;--mantine-color-violet-1: #f1cfff;--mantine-color-violet-2: #e09bff;--mantine-color-violet-3: #d16fff;--mantine-color-violet-4: #be37fe;--mantine-color-violet-5: #b51afe;--mantine-color-violet-6: #b009ff;--mantine-color-violet-7: #9b00e4;--mantine-color-violet-8: #8a00cc;--mantine-color-violet-9: #7800b3;--mantine-color-indigo-0: #edecff;--mantine-color-indigo-1: #d6d5fe;--mantine-color-indigo-2: #aaa9f4;--mantine-color-indigo-3: #7b79eb;--mantine-color-indigo-4: #5451e4;--mantine-color-indigo-5: #3b37e0;--mantine-color-indigo-6: #2d2adf;--mantine-color-indigo-7: #1f1ec7;--mantine-color-indigo-8: #1819b2;--mantine-color-indigo-9: #0c149e;--mantine-color-cyan-0: #dffdff;--mantine-color-cyan-1: #caf5ff;--mantine-color-cyan-2: #99e8ff;--mantine-color-cyan-3: #64daff;--mantine-color-cyan-4: #3ccffe;--mantine-color-cyan-5: #24c8fe;--mantine-color-cyan-6: #00c2ff;--mantine-color-cyan-7: #00ade4;--mantine-color-cyan-8: #009acd;--mantine-color-cyan-9: #0085b5;--mantine-color-green-0: #e9fdec;--mantine-color-green-1: #d7f6dc;--mantine-color-green-2: #b0eab9;--mantine-color-green-3: #86df94;--mantine-color-green-4: #62d574;--mantine-color-green-5: #4ccf5f;--mantine-color-green-6: #3fcc54;--mantine-color-green-7: #2fb344;--mantine-color-green-8: #25a03b;--mantine-color-green-9: #138a2e;--mantine-color-yellow-0: #fff7e2;--mantine-color-yellow-1: #ffeecd;--mantine-color-yellow-2: #ffdc9c;--mantine-color-yellow-3: #ffc966;--mantine-color-yellow-4: #feb93a;--mantine-color-yellow-5: #feae1e;--mantine-color-yellow-6: #ffa90f;--mantine-color-yellow-8: #ca8200;--mantine-color-yellow-9: #af7000;--mantine-h1-font-size: clamp(28.0000px, calc(23.6364px + 1.3636vw), 40.0000px);--mantine-h1-font-weight: normal;--mantine-h2-font-size: clamp(24.0000px, calc(21.0909px + 0.9091vw), 32.0000px);--mantine-h2-font-weight: normal;--mantine-h3-font-size: clamp(20.0000px, calc(17.0909px + 0.9091vw), 28.0000px);--mantine-h3-font-weight: normal;--mantine-h4-font-size: clamp(16.0000px, calc(13.0909px + 0.9091vw), 24.0000px);--mantine-h4-font-weight: normal;--mantine-h5-font-size: clamp(16.0000px, calc(14.5455px + 0.4545vw), 20.0000px);--mantine-h5-font-weight: normal;--mantine-h6-font-size: 1rem;--mantine-h6-font-weight: normal;}
:root[data-mantine-color-scheme="dark"], :host([data-mantine-color-scheme="dark"]){--mantine-color-anchor: var(--mantine-color-text);--mantine-color-dimmed: #495057;--mantine-color-dark-filled: var(--mantine-color-dark-5);--mantine-color-dark-filled-hover: var(--mantine-color-dark-6);--mantine-color-dark-light: rgba(105, 105, 105, 0.15);--mantine-color-dark-light-hover: rgba(105, 105, 105, 0.2);--mantine-color-dark-light-color: var(--mantine-color-dark-0);--mantine-color-dark-outline: var(--mantine-color-dark-1);--mantine-color-dark-outline-hover: rgba(184, 184, 184, 0.05);--mantine-color-gray-filled: var(--mantine-color-gray-5);--mantine-color-gray-filled-hover: var(--mantine-color-gray-6);--mantine-color-gray-light: rgba(222, 226, 230, 0.15);--mantine-color-gray-light-hover: rgba(222, 226, 230, 0.2);--mantine-color-gray-light-color: var(--mantine-color-gray-0);--mantine-color-gray-outline: var(--mantine-color-gray-1);--mantine-color-gray-outline-hover: rgba(241, 243, 245, 0.05);--mantine-color-red-filled: var(--mantine-color-red-5);--mantine-color-red-filled-hover: var(--mantine-color-red-6);--mantine-color-red-light: rgba(236, 120, 120, 0.15);--mantine-color-red-light-hover: rgba(236, 120, 120, 0.2);--mantine-color-red-light-color: var(--mantine-color-red-0);--mantine-color-red-outline: var(--mantine-color-red-1);--mantine-color-red-outline-hover: rgba(254, 212, 212, 0.05);--mantine-color-pink-filled: var(--mantine-color-pink-5);--mantine-color-pink-filled-hover: var(--mantine-color-pink-6);--mantine-color-pink-light: rgba(250, 162, 193, 0.15);--mantine-color-pink-light-hover: rgba(250, 162, 193, 0.2);--mantine-color-pink-light-color: var(--mantine-color-pink-0);--mantine-color-pink-outline: var(--mantine-color-pink-1);--mantine-color-pink-outline-hover: rgba(255, 222, 235, 0.05);--mantine-color-grape-filled: var(--mantine-color-grape-5);--mantine-color-grape-filled-hover: var(--mantine-color-grape-6);--mantine-color-grape-light: rgba(229, 153, 247, 0.15);--mantine-color-grape-light-hover: rgba(229, 153, 247, 0.2);--mantine-color-grape-light-color: var(--mantine-color-grape-0);--mantine-color-grape-outline: var(--mantine-color-grape-1);--mantine-color-grape-outline-hover: rgba(243, 217, 250, 0.05);--mantine-color-violet-filled: var(--mantine-color-violet-5);--mantine-color-violet-filled-hover: var(--mantine-color-violet-6);--mantine-color-violet-light: rgba(209, 111, 255, 0.15);--mantine-color-violet-light-hover: rgba(209, 111, 255, 0.2);--mantine-color-violet-light-color: var(--mantine-color-violet-0);--mantine-color-violet-outline: var(--mantine-color-violet-1);--mantine-color-violet-outline-hover: rgba(241, 207, 255, 0.05);--mantine-color-indigo-filled: var(--mantine-color-indigo-5);--mantine-color-indigo-filled-hover: var(--mantine-color-indigo-6);--mantine-color-indigo-light: rgba(123, 121, 235, 0.15);--mantine-color-indigo-light-hover: rgba(123, 121, 235, 0.2);--mantine-color-indigo-light-color: var(--mantine-color-indigo-0);--mantine-color-indigo-outline: var(--mantine-color-indigo-1);--mantine-color-indigo-outline-hover: rgba(214, 213, 254, 0.05);--mantine-color-blue-filled: var(--mantine-color-blue-5);--mantine-color-blue-filled-hover: var(--mantine-color-blue-6);--mantine-color-blue-light: rgba(116, 192, 252, 0.15);--mantine-color-blue-light-hover: rgba(116, 192, 252, 0.2);--mantine-color-blue-light-color: var(--mantine-color-blue-0);--mantine-color-blue-outline: var(--mantine-color-blue-1);--mantine-color-blue-outline-hover: rgba(208, 235, 255, 0.05);--mantine-color-cyan-filled: var(--mantine-color-cyan-5);--mantine-color-cyan-filled-hover: var(--mantine-color-cyan-6);--mantine-color-cyan-light: rgba(100, 218, 255, 0.15);--mantine-color-cyan-light-hover: rgba(100, 218, 255, 0.2);--mantine-color-cyan-light-color: var(--mantine-color-cyan-0);--mantine-color-cyan-outline: var(--mantine-color-cyan-1);--mantine-color-cyan-outline-hover: rgba(202, 245, 255, 0.05);--mantine-color-teal-filled: var(--mantine-color-teal-5);--mantine-color-teal-filled-hover: var(--mantine-color-teal-6);--mantine-color-teal-light: rgba(99, 230, 190, 0.15);--mantine-color-teal-light-hover: rgba(99, 230, 190, 0.2);--mantine-color-teal-light-color: var(--mantine-color-teal-0);--mantine-color-teal-outline: var(--mantine-color-teal-1);--mantine-color-teal-outline-hover: rgba(195, 250, 232, 0.05);--mantine-color-green-filled: var(--mantine-color-green-5);--mantine-color-green-filled-hover: var(--mantine-color-green-6);--mantine-color-green-light: rgba(134, 223, 148, 0.15);--mantine-color-green-light-hover: rgba(134, 223, 148, 0.2);--mantine-color-green-light-color: var(--mantine-color-green-0);--mantine-color-green-outline: var(--mantine-color-green-1);--mantine-color-green-outline-hover: rgba(215, 246, 220, 0.05);--mantine-color-lime-filled: var(--mantine-color-lime-5);--mantine-color-lime-filled-hover: var(--mantine-color-lime-6);--mantine-color-lime-light: rgba(192, 235, 117, 0.15);--mantine-color-lime-light-hover: rgba(192, 235, 117, 0.2);--mantine-color-lime-light-color: var(--mantine-color-lime-0);--mantine-color-lime-outline: var(--mantine-color-lime-1);--mantine-color-lime-outline-hover: rgba(233, 250, 200, 0.05);--mantine-color-yellow-filled: var(--mantine-color-yellow-5);--mantine-color-yellow-filled-hover: var(--mantine-color-yellow-6);--mantine-color-yellow-light: rgba(255, 201, 102, 0.15);--mantine-color-yellow-light-hover: rgba(255, 201, 102, 0.2);--mantine-color-yellow-light-color: var(--mantine-color-yellow-0);--mantine-color-yellow-outline: var(--mantine-color-yellow-1);--mantine-color-yellow-outline-hover: rgba(255, 238, 205, 0.05);--mantine-color-orange-filled: var(--mantine-color-orange-5);--mantine-color-orange-filled-hover: var(--mantine-color-orange-6);--mantine-color-orange-light: rgba(255, 192, 120, 0.15);--mantine-color-orange-light-hover: rgba(255, 192, 120, 0.2);--mantine-color-orange-light-color: var(--mantine-color-orange-0);--mantine-color-orange-outline: var(--mantine-color-orange-1);--mantine-color-orange-outline-hover: rgba(255, 232, 204, 0.05);--app-cta-gradient: linear-gradient(90deg, var(--mantine-color-blue-9) 0%, var(--mantine-color-cyan-7) 100%);--app-color-surface: #2e2e2e;}
:root[data-mantine-color-scheme="light"], :host([data-mantine-color-scheme="light"]){--mantine-color-anchor: var(--mantine-color-text);--mantine-color-dimmed: #495057;--mantine-color-red-light: rgba(224, 40, 41, 0.1);--mantine-color-red-light-hover: rgba(224, 40, 41, 0.12);--mantine-color-red-outline-hover: rgba(224, 40, 41, 0.05);--mantine-color-violet-light: rgba(176, 9, 255, 0.1);--mantine-color-violet-light-hover: rgba(176, 9, 255, 0.12);--mantine-color-violet-outline-hover: rgba(176, 9, 255, 0.05);--mantine-color-indigo-light: rgba(45, 42, 223, 0.1);--mantine-color-indigo-light-hover: rgba(45, 42, 223, 0.12);--mantine-color-indigo-outline-hover: rgba(45, 42, 223, 0.05);--mantine-color-cyan-light: rgba(0, 194, 255, 0.1);--mantine-color-cyan-light-hover: rgba(0, 194, 255, 0.12);--mantine-color-cyan-outline-hover: rgba(0, 194, 255, 0.05);--mantine-color-green-light: rgba(63, 204, 84, 0.1);--mantine-color-green-light-hover: rgba(63, 204, 84, 0.12);--mantine-color-green-outline-hover: rgba(63, 204, 84, 0.05);--mantine-color-yellow-light: rgba(255, 169, 15, 0.1);--mantine-color-yellow-light-hover: rgba(255, 169, 15, 0.12);--mantine-color-yellow-outline-hover: rgba(255, 169, 15, 0.05);--app-color-surface: #f1f3f5;--app-cta-gradient: linear-gradient(90deg, var(--mantine-color-blue-filled) 0%, var(--mantine-color-cyan-5) 100%);}</style><style data-mantine-styles="classes">@media (max-width: 35.99375em) {.mantine-visible-from-xs {display: none !important;}}@media (min-width: 36em) {.mantine-hidden-from-xs {display: none !important;}}@media (max-width: 47.99375em) {.mantine-visible-from-sm {display: none !important;}}@media (min-width: 48em) {.mantine-hidden-from-sm {display: none !important;}}@media (max-width: 61.99375em) {.mantine-visible-from-md {display: none !important;}}@media (min-width: 62em) {.mantine-hidden-from-md {display: none !important;}}@media (max-width: 74.99375em) {.mantine-visible-from-lg {display: none !important;}}@media (min-width: 75em) {.mantine-hidden-from-lg {display: none !important;}}@media (max-width: 87.99375em) {.mantine-visible-from-xl {display: none !important;}}@media (min-width: 88em) {.mantine-hidden-from-xl {display: none !important;}}</style><div style="position:absolute;top:0rem" class=""></div><div style="max-width:var(--container-size-xl);height:100%;min-height:0rem" class=""><style data-mantine-styles="inline">.__m__-_R_5ub_{--grid-gutter:0rem;}</style><div style="height:100%;min-height:0rem" class="m_410352e9 mantine-Grid-root __m__-_R_5ub_"><div class="m_dee7bd2f mantine-Grid-inner" style="height:100%"><style data-mantine-styles="inline">.__m__-_R_rdub_{--col-flex-grow:auto;--col-flex-basis:91.66666666666667%;--col-max-width:91.66666666666667%;}@media(min-width: 48em){.__m__-_R_rdub_{--col-flex-grow:auto;--col-flex-basis:83.33333333333334%;--col-max-width:83.33333333333334%;}}</style><div style="min-width:0rem;height:100%;min-height:0rem;display:flex" class="m_96bdd299 mantine-Grid-col __m__-_R_rdub_"><style data-mantine-styles="inline">.__m__-_R_6qrdub_{margin-top:0rem;padding-inline:var(--mantine-spacing-xs);width:100%;}@media(min-width: 48em){.__m__-_R_6qrdub_{margin-top:var(--mantine-spacing-xl);width:80%;}}@media(min-width: 62em){.__m__-_R_6qrdub_{padding-inline:var(--mantine-spacing-xl);}}</style><div style="margin-inline:auto;max-width:var(--mantine-breakpoint-xl)" class="__m__-_R_6qrdub_"><div style="color:var(--mantine-color-dimmed)" class="m_4451eb3a mantine-Center-root" data-inline="true"><div style="--ti-size:var(--ti-size-xs);--ti-bg:transparent;--ti-color:var(--mantine-color-indigo-light-color);--ti-bd:calc(0.0625rem * var(--mantine-scale)) solid transparent;margin-inline-end:calc(0.125rem * var(--mantine-scale));color:inherit" class="m_7341320d mantine-ThemeIcon-root" data-variant="transparent" data-size="xs"><svg xmlns="http://www.w3.org/2000/svg" width="24" height="24" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2" stroke-linecap="round" stroke-linejoin="round" class="tabler-icon tabler-icon-lock "><path d="M5 13a2 2 0 0 1 2 -2h10a2 2 0 0 1 2 2v6a2 2 0 0 1 -2 2h-10a2 2 0 0 1 -2 -2v-6"></path><path d="M11 16a1 1 0 1 0 2 0a1 1 0 0 0 -2 0"></path><path d="M8 11v-4a4 4 0 1 1 8 0v4"></path></svg></div><p style="font-size:var(--mantine-font-size-sm)" class="mantine-focus-auto m_b6d8b162 mantine-Text-root">Python: Функции</p></div><h1 style="--title-fw:var(--mantine-h1-font-weight);--title-lh:var(--mantine-h1-line-height);--title-fz:var(--mantine-h1-font-size);margin-bottom:var(--mantine-spacing-xl)" class="m_8a5d1357 mantine-Title-root" data-order="1">Теория: Декораторы</h1><script type="application/ld+json">{"@context":"https://schema.org","@type":"LearningResource","name":"Декораторы","inLanguage":"ru","isPartOf":{"@type":"LearningResource","name":"Python: Функции"},"isAccessibleForFree":"False","hasPart":{"@type":"WebPageElement","isAccessibleForFree":"False","cssSelector":".paywalled"}}</script><div class=""><div style="--alert-color:var(--mantine-color-indigo-light-color);margin-bottom:var(--mantine-spacing-lg);font-size:var(--mantine-font-size-lg)" class="m_66836ed3 mantine-Alert-root" id="mantine-_R_remqrdub_" role="alert" aria-describedby="mantine-_R_remqrdub_-body" aria-labelledby="mantine-_R_remqrdub_-title"><div class="m_a5d60502 mantine-Alert-wrapper"><div class="m_667f2a6a mantine-Alert-icon"><svg xmlns="http://www.w3.org/2000/svg" width="24" height="24" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2" stroke-linecap="round" stroke-linejoin="round" class="tabler-icon tabler-icon-rocket "><path d="M4 13a8 8 0 0 1 7 7a6 6 0 0 0 3 -5a9 9 0 0 0 6 -8a3 3 0 0 0 -3 -3a9 9 0 0 0 -8 6a6 6 0 0 0 -5 3"></path><path d="M7 14a6 6 0 0 0 -3 6a6 6 0 0 0 6 -3"></path><path d="M14 9a1 1 0 1 0 2 0a1 1 0 1 0 -2 0"></path></svg></div><div class="m_667c2793 mantine-Alert-body"><div class="m_6a03f287 mantine-Alert-title"><span id="mantine-_R_remqrdub_-title" class="m_698f4f23 mantine-Alert-label">Полный доступ к материалам</span></div><div id="mantine-_R_remqrdub_-body" class="m_7fa78076 mantine-Alert-message"><div style="--group-gap:var(--mantine-spacing-md);--group-align:center;--group-justify:space-between;--group-wrap:wrap" class="m_4081bf90 mantine-Group-root"><p class="mantine-focus-auto m_b6d8b162 mantine-Text-root">Зарегистрируйтесь и получите доступ к этому и десяткам других курсов</p><a style="--button-height:var(--button-height-xs);--button-padding-x:var(--button-padding-x-xs);--button-fz:var(--mantine-font-size-xs);--button-bg:linear-gradient(45deg, var(--mantine-color-blue-filled) 0%, var(--mantine-color-cyan-filled) 100%);--button-hover:linear-gradient(45deg, var(--mantine-color-blue-filled) 0%, var(--mantine-color-cyan-filled) 100%);--button-color:var(--mantine-color-white);--button-bd:none" class="mantine-focus-auto mantine-active m_77c9d27d mantine-Button-root m_87cf2631 mantine-UnstyledButton-root" data-variant="gradient" data-size="xs" href="/u/new"><span class="m_80f1301b mantine-Button-inner"><span class="m_811560b9 mantine-Button-label">Зарегистрироваться</span></span></a></div></div></div></div></div><div class="paywalled m_d08caa0 mantine-Typography-root"><p>В программировании часто возникают задачи, когда нужно добавить поведение к уже существующим функциям или классам. Например, логирование, проверку входных данных или замер времени выполнения функции. В таких случаях использование декораторов может значительно упростить решение задачи.</p>
<p>В этом уроке мы рассмотрим, что такое декораторы и как их использовать, чтобы добавлять дополнительную функциональность к существующим функциям.</p>
<h2 id="heading-2-1">Что такое декораторы</h2>
<p>Ранее мы рассматривали как можно "запомнить" передаваемые в функцию значения с помощью замыканий. Напомним как выглядит синтаксис:</p>
<div style="margin-bottom:var(--mantine-spacing-lg)" class="m_e597c321 mantine-CodeHighlight-codeHighlight" dir="ltr"><div class="m_be7e9c9c mantine-CodeHighlight-controls"><button style="--ai-bg:transparent;--ai-hover:transparent;--ai-color:inherit;--ai-bd:none" class="mantine-focus-auto mantine-active m_d498bab7 mantine-CodeHighlight-control m_8d3f4000 mantine-ActionIcon-root m_87cf2631 mantine-UnstyledButton-root" data-variant="none" type="button" aria-label="Copy code"><span class="m_8d3afb97 mantine-ActionIcon-icon"><svg xmlns="http://www.w3.org/2000/svg" viewBox="0 0 24 24" stroke-width="2" stroke="currentColor" fill="none" stroke-linecap="round" stroke-linejoin="round"><path stroke="none" d="M0 0h24v24H0z" fill="none"></path><path d="M8 8m0 2a2 2 0 0 1 2 -2h8a2 2 0 0 1 2 2v8a2 2 0 0 1 -2 2h-8a2 2 0 0 1 -2 -2z"></path><path d="M16 8v-2a2 2 0 0 0 -2 -2h-8a2 2 0 0 0 -2 2v8a2 2 0 0 0 2 2h2"></path></svg></span></button></div><div style="--scrollarea-scrollbar-size:calc(0.25rem * var(--mantine-scale));--sa-corner-width:0px;--sa-corner-height:0px" class="m_f744fd40 mantine-CodeHighlight-scrollarea m_d57069b5 mantine-ScrollArea-root" dir="ltr"><div style="overflow-x:hidden;overflow-y:hidden;overscroll-behavior-inline:none" class="m_c0783ff9 mantine-ScrollArea-viewport" data-scrollbars="xy"><div class="m_b1336c6 mantine-ScrollArea-content"><pre class="m_2c47c4fd mantine-CodeHighlight-pre" style="padding:0"><code class="m_5caae6d3 mantine-CodeHighlight-code">def outer(arg1):
def inner(arg2):
return arg1 + arg2
return inner
two_add = outer(2)
two_add(1) # 3
two_add(3) # 5
two_add(5) # 7</code></pre></div></div></div><button class="mantine-focus-auto m_c9378bc2 mantine-CodeHighlight-showCodeButton m_87cf2631 mantine-UnstyledButton-root" data-hidden="true" type="button">Expand code</button></div>
<p>В этом примере мы создаем функцию <code style="margin-bottom:var(--mantine-spacing-lg)" class="m_dfe9c588 mantine-InlineCodeHighlight-inlineCodeHighlight m_e597c321 mantine-CodeHighlight-codeHighlight m_dfe9c588 mantine-InlineCodeHighlight-inlineCodeHighlight">outer()</code>, которая принимает аргумент <code style="margin-bottom:var(--mantine-spacing-lg)" class="m_dfe9c588 mantine-InlineCodeHighlight-inlineCodeHighlight m_e597c321 mantine-CodeHighlight-codeHighlight m_dfe9c588 mantine-InlineCodeHighlight-inlineCodeHighlight">arg1</code> и возвращает внутреннюю функцию <code style="margin-bottom:var(--mantine-spacing-lg)" class="m_dfe9c588 mantine-InlineCodeHighlight-inlineCodeHighlight m_e597c321 mantine-CodeHighlight-codeHighlight m_dfe9c588 mantine-InlineCodeHighlight-inlineCodeHighlight">inner()</code>. Внутренняя функция принимает аргумент <code style="margin-bottom:var(--mantine-spacing-lg)" class="m_dfe9c588 mantine-InlineCodeHighlight-inlineCodeHighlight m_e597c321 mantine-CodeHighlight-codeHighlight m_dfe9c588 mantine-InlineCodeHighlight-inlineCodeHighlight">arg2</code> и возвращает сумму <code style="margin-bottom:var(--mantine-spacing-lg)" class="m_dfe9c588 mantine-InlineCodeHighlight-inlineCodeHighlight m_e597c321 mantine-CodeHighlight-codeHighlight m_dfe9c588 mantine-InlineCodeHighlight-inlineCodeHighlight">arg1</code> и <code style="margin-bottom:var(--mantine-spacing-lg)" class="m_dfe9c588 mantine-InlineCodeHighlight-inlineCodeHighlight m_e597c321 mantine-CodeHighlight-codeHighlight m_dfe9c588 mantine-InlineCodeHighlight-inlineCodeHighlight">agr2</code>.</p>
<p>Может запутать одновременное определение функции и возврат ее же. Но если мы вспомним, что определение функции это присваивание функции имени переменной, а также, что функции это данные, то пример можно представить как:</p>
<div style="margin-bottom:var(--mantine-spacing-lg)" class="m_e597c321 mantine-CodeHighlight-codeHighlight" dir="ltr"><div class="m_be7e9c9c mantine-CodeHighlight-controls"><button style="--ai-bg:transparent;--ai-hover:transparent;--ai-color:inherit;--ai-bd:none" class="mantine-focus-auto mantine-active m_d498bab7 mantine-CodeHighlight-control m_8d3f4000 mantine-ActionIcon-root m_87cf2631 mantine-UnstyledButton-root" data-variant="none" type="button" aria-label="Copy code"><span class="m_8d3afb97 mantine-ActionIcon-icon"><svg xmlns="http://www.w3.org/2000/svg" viewBox="0 0 24 24" stroke-width="2" stroke="currentColor" fill="none" stroke-linecap="round" stroke-linejoin="round"><path stroke="none" d="M0 0h24v24H0z" fill="none"></path><path d="M8 8m0 2a2 2 0 0 1 2 -2h8a2 2 0 0 1 2 2v8a2 2 0 0 1 -2 2h-8a2 2 0 0 1 -2 -2z"></path><path d="M16 8v-2a2 2 0 0 0 -2 -2h-8a2 2 0 0 0 -2 2v8a2 2 0 0 0 2 2h2"></path></svg></span></button></div><div style="--scrollarea-scrollbar-size:calc(0.25rem * var(--mantine-scale));--sa-corner-width:0px;--sa-corner-height:0px" class="m_f744fd40 mantine-CodeHighlight-scrollarea m_d57069b5 mantine-ScrollArea-root" dir="ltr"><div style="overflow-x:hidden;overflow-y:hidden;overscroll-behavior-inline:none" class="m_c0783ff9 mantine-ScrollArea-viewport" data-scrollbars="xy"><div class="m_b1336c6 mantine-ScrollArea-content"><pre class="m_2c47c4fd mantine-CodeHighlight-pre" style="padding:0"><code class="m_5caae6d3 mantine-CodeHighlight-code">def outer(arg1):
inner = lambda arg2: arg1 + arg2
return inner</code></pre></div></div></div><button class="mantine-focus-auto m_c9378bc2 mantine-CodeHighlight-showCodeButton m_87cf2631 mantine-UnstyledButton-root" data-hidden="true" type="button">Expand code</button></div>
<p>Мы заменили именованную функцию лямбдой, и теперь присваивание ничем не отличается, как если бы мы присвоили переменной <code style="margin-bottom:var(--mantine-spacing-lg)" class="m_dfe9c588 mantine-InlineCodeHighlight-inlineCodeHighlight m_e597c321 mantine-CodeHighlight-codeHighlight m_dfe9c588 mantine-InlineCodeHighlight-inlineCodeHighlight">inner</code> число или строку.</p>
<p>Но если функции всего лишь данные, то можно ли их также "запоминать" в замыканиях?</p>
<div style="margin-bottom:var(--mantine-spacing-lg)" class="m_e597c321 mantine-CodeHighlight-codeHighlight" dir="ltr"><div class="m_be7e9c9c mantine-CodeHighlight-controls"><button style="--ai-bg:transparent;--ai-hover:transparent;--ai-color:inherit;--ai-bd:none" class="mantine-focus-auto mantine-active m_d498bab7 mantine-CodeHighlight-control m_8d3f4000 mantine-ActionIcon-root m_87cf2631 mantine-UnstyledButton-root" data-variant="none" type="button" aria-label="Copy code"><span class="m_8d3afb97 mantine-ActionIcon-icon"><svg xmlns="http://www.w3.org/2000/svg" viewBox="0 0 24 24" stroke-width="2" stroke="currentColor" fill="none" stroke-linecap="round" stroke-linejoin="round"><path stroke="none" d="M0 0h24v24H0z" fill="none"></path><path d="M8 8m0 2a2 2 0 0 1 2 -2h8a2 2 0 0 1 2 2v8a2 2 0 0 1 -2 2h-8a2 2 0 0 1 -2 -2z"></path><path d="M16 8v-2a2 2 0 0 0 -2 -2h-8a2 2 0 0 0 -2 2v8a2 2 0 0 0 2 2h2"></path></svg></span></button></div><div style="--scrollarea-scrollbar-size:calc(0.25rem * var(--mantine-scale));--sa-corner-width:0px;--sa-corner-height:0px" class="m_f744fd40 mantine-CodeHighlight-scrollarea m_d57069b5 mantine-ScrollArea-root" dir="ltr"><div style="overflow-x:hidden;overflow-y:hidden;overscroll-behavior-inline:none" class="m_c0783ff9 mantine-ScrollArea-viewport" data-scrollbars="xy"><div class="m_b1336c6 mantine-ScrollArea-content"><pre class="m_2c47c4fd mantine-CodeHighlight-pre" style="padding:0"><code class="m_5caae6d3 mantine-CodeHighlight-code">def outer(func):
def inner(arg):
print("func = ", func)
print("arg = ", arg)
return func(arg)
return inner
def square(x):
return x**2
printer = outer(square)
printer(1) # 1
# => func = <function square at 0x75e04623a7a0>
# => arg = 1
printer(2) # 4
# => func = <function square at 0x75e04623a7a0>
# => arg = 2
printer(4) # 16
# => func = <function square at 0x75e04623a7a0>
# => arg = 4</code></pre></div></div></div><button class="mantine-focus-auto m_c9378bc2 mantine-CodeHighlight-showCodeButton m_87cf2631 mantine-UnstyledButton-root" data-hidden="true" type="button">Expand code</button></div>
<p>Остановимся и разберем пример выше. В нем мы создаем замыкание, но теперь, вместо числа в замыкание передается функция. Внешняя функция запомнила переданную в нее функцию. Осталось лишь передать аргументы и вызвать замкнутую функцию с ними. В дополнение, мы смогли распечатать переданную функцию и ее аргументы, при этом код самой функции <code style="margin-bottom:var(--mantine-spacing-lg)" class="m_dfe9c588 mantine-InlineCodeHighlight-inlineCodeHighlight m_e597c321 mantine-CodeHighlight-codeHighlight m_dfe9c588 mantine-InlineCodeHighlight-inlineCodeHighlight">square()</code> мы не меняли.</p>
<p>Функции выше называются декораторами. <strong>Декораторы</strong> в Python — это функции, которые принимают другую функцию в качестве аргумента, добавляют к ней дополнительную функциональность и возвращают функцию с измененным поведением. Декораторы позволяют изменять поведение функций и классов с помощью добавления или изменения их функциональности без изменения самого кода.</p>
<p>Также, как вы могли заметить, декораторы это частный случай функции высшего порядка (принимаем функцию) и использования замыкания вместе.</p>
<h2 id="heading-2-2">Использование декораторов</h2>
<p>Декораторы используются в Python повсеместно: для логирования, кеширования, добавления нового функционала, используются в тестовых и веб-фреймворках.</p>
<p>Предположим, что у нас есть функция, которая суммирует числа:</p>
<div style="margin-bottom:var(--mantine-spacing-lg)" class="m_e597c321 mantine-CodeHighlight-codeHighlight" dir="ltr"><div class="m_be7e9c9c mantine-CodeHighlight-controls"><button style="--ai-bg:transparent;--ai-hover:transparent;--ai-color:inherit;--ai-bd:none" class="mantine-focus-auto mantine-active m_d498bab7 mantine-CodeHighlight-control m_8d3f4000 mantine-ActionIcon-root m_87cf2631 mantine-UnstyledButton-root" data-variant="none" type="button" aria-label="Copy code"><span class="m_8d3afb97 mantine-ActionIcon-icon"><svg xmlns="http://www.w3.org/2000/svg" viewBox="0 0 24 24" stroke-width="2" stroke="currentColor" fill="none" stroke-linecap="round" stroke-linejoin="round"><path stroke="none" d="M0 0h24v24H0z" fill="none"></path><path d="M8 8m0 2a2 2 0 0 1 2 -2h8a2 2 0 0 1 2 2v8a2 2 0 0 1 -2 2h-8a2 2 0 0 1 -2 -2z"></path><path d="M16 8v-2a2 2 0 0 0 -2 -2h-8a2 2 0 0 0 -2 2v8a2 2 0 0 0 2 2h2"></path></svg></span></button></div><div style="--scrollarea-scrollbar-size:calc(0.25rem * var(--mantine-scale));--sa-corner-width:0px;--sa-corner-height:0px" class="m_f744fd40 mantine-CodeHighlight-scrollarea m_d57069b5 mantine-ScrollArea-root" dir="ltr"><div style="overflow-x:hidden;overflow-y:hidden;overscroll-behavior-inline:none" class="m_c0783ff9 mantine-ScrollArea-viewport" data-scrollbars="xy"><div class="m_b1336c6 mantine-ScrollArea-content"><pre class="m_2c47c4fd mantine-CodeHighlight-pre" style="padding:0"><code class="m_5caae6d3 mantine-CodeHighlight-code">def sum(*nums):
result = 0
for num in nums:
result += num
return result</code></pre></div></div></div><button class="mantine-focus-auto m_c9378bc2 mantine-CodeHighlight-showCodeButton m_87cf2631 mantine-UnstyledButton-root" data-hidden="true" type="button">Expand code</button></div>
<p>Создадим декоратор, который добавит к этой функции функциональность для отладки:</p>
<div style="margin-bottom:var(--mantine-spacing-lg)" class="m_e597c321 mantine-CodeHighlight-codeHighlight" dir="ltr"><div class="m_be7e9c9c mantine-CodeHighlight-controls"><button style="--ai-bg:transparent;--ai-hover:transparent;--ai-color:inherit;--ai-bd:none" class="mantine-focus-auto mantine-active m_d498bab7 mantine-CodeHighlight-control m_8d3f4000 mantine-ActionIcon-root m_87cf2631 mantine-UnstyledButton-root" data-variant="none" type="button" aria-label="Copy code"><span class="m_8d3afb97 mantine-ActionIcon-icon"><svg xmlns="http://www.w3.org/2000/svg" viewBox="0 0 24 24" stroke-width="2" stroke="currentColor" fill="none" stroke-linecap="round" stroke-linejoin="round"><path stroke="none" d="M0 0h24v24H0z" fill="none"></path><path d="M8 8m0 2a2 2 0 0 1 2 -2h8a2 2 0 0 1 2 2v8a2 2 0 0 1 -2 2h-8a2 2 0 0 1 -2 -2z"></path><path d="M16 8v-2a2 2 0 0 0 -2 -2h-8a2 2 0 0 0 -2 2v8a2 2 0 0 0 2 2h2"></path></svg></span></button></div><div style="--scrollarea-scrollbar-size:calc(0.25rem * var(--mantine-scale));--sa-corner-width:0px;--sa-corner-height:0px" class="m_f744fd40 mantine-CodeHighlight-scrollarea m_d57069b5 mantine-ScrollArea-root" dir="ltr"><div style="overflow-x:hidden;overflow-y:hidden;overscroll-behavior-inline:none" class="m_c0783ff9 mantine-ScrollArea-viewport" data-scrollbars="xy"><div class="m_b1336c6 mantine-ScrollArea-content"><pre class="m_2c47c4fd mantine-CodeHighlight-pre" style="padding:0"><code class="m_5caae6d3 mantine-CodeHighlight-code">def debug_decorator(func):
# внутреннюю функцию принято называть wrapper - обертка
def wrapper(*args, **kwargs):
print("Args:", args, kwargs)
result = func(*args, **kwargs)
print("Result:", result)
return result
return wrapper</code></pre></div></div></div><button class="mantine-focus-auto m_c9378bc2 mantine-CodeHighlight-showCodeButton m_87cf2631 mantine-UnstyledButton-root" data-hidden="true" type="button">Expand code</button></div>
<p>Этот декоратор принимает функцию в качестве аргумента и возвращает новую функцию-обертку, которая добавляет отладочные сообщения в процесс выполнения исходной функции. Заметьте, что внутренняя функция принимает сразу все аргументы с помощью <code style="margin-bottom:var(--mantine-spacing-lg)" class="m_dfe9c588 mantine-InlineCodeHighlight-inlineCodeHighlight m_e597c321 mantine-CodeHighlight-codeHighlight m_dfe9c588 mantine-InlineCodeHighlight-inlineCodeHighlight">*args</code> и <code style="margin-bottom:var(--mantine-spacing-lg)" class="m_dfe9c588 mantine-InlineCodeHighlight-inlineCodeHighlight m_e597c321 mantine-CodeHighlight-codeHighlight m_dfe9c588 mantine-InlineCodeHighlight-inlineCodeHighlight">**kwargs</code>. Так мы можем создавать "всеядные" обертки.</p>
<p>Применим этот декоратор к функции <code style="margin-bottom:var(--mantine-spacing-lg)" class="m_dfe9c588 mantine-InlineCodeHighlight-inlineCodeHighlight m_e597c321 mantine-CodeHighlight-codeHighlight m_dfe9c588 mantine-InlineCodeHighlight-inlineCodeHighlight">sum()</code>:</p>
<div style="margin-bottom:var(--mantine-spacing-lg)" class="m_e597c321 mantine-CodeHighlight-codeHighlight" dir="ltr"><div class="m_be7e9c9c mantine-CodeHighlight-controls"><button style="--ai-bg:transparent;--ai-hover:transparent;--ai-color:inherit;--ai-bd:none" class="mantine-focus-auto mantine-active m_d498bab7 mantine-CodeHighlight-control m_8d3f4000 mantine-ActionIcon-root m_87cf2631 mantine-UnstyledButton-root" data-variant="none" type="button" aria-label="Copy code"><span class="m_8d3afb97 mantine-ActionIcon-icon"><svg xmlns="http://www.w3.org/2000/svg" viewBox="0 0 24 24" stroke-width="2" stroke="currentColor" fill="none" stroke-linecap="round" stroke-linejoin="round"><path stroke="none" d="M0 0h24v24H0z" fill="none"></path><path d="M8 8m0 2a2 2 0 0 1 2 -2h8a2 2 0 0 1 2 2v8a2 2 0 0 1 -2 2h-8a2 2 0 0 1 -2 -2z"></path><path d="M16 8v-2a2 2 0 0 0 -2 -2h-8a2 2 0 0 0 -2 2v8a2 2 0 0 0 2 2h2"></path></svg></span></button></div><div style="--scrollarea-scrollbar-size:calc(0.25rem * var(--mantine-scale));--sa-corner-width:0px;--sa-corner-height:0px" class="m_f744fd40 mantine-CodeHighlight-scrollarea m_d57069b5 mantine-ScrollArea-root" dir="ltr"><div style="overflow-x:hidden;overflow-y:hidden;overscroll-behavior-inline:none" class="m_c0783ff9 mantine-ScrollArea-viewport" data-scrollbars="xy"><div class="m_b1336c6 mantine-ScrollArea-content"><pre class="m_2c47c4fd mantine-CodeHighlight-pre" style="padding:0"><code class="m_5caae6d3 mantine-CodeHighlight-code">debugged_sum = debug_decorator(sum)
debugged_sum(1, 2, 3) # 6
# => Args: (1, 2, 3) {}
# => Result: 6</code></pre></div></div></div><button class="mantine-focus-auto m_c9378bc2 mantine-CodeHighlight-showCodeButton m_87cf2631 mantine-UnstyledButton-root" data-hidden="true" type="button">Expand code</button></div>
<p>Проблема в коде выше, что теперь функция называется <code style="margin-bottom:var(--mantine-spacing-lg)" class="m_dfe9c588 mantine-InlineCodeHighlight-inlineCodeHighlight m_e597c321 mantine-CodeHighlight-codeHighlight m_dfe9c588 mantine-InlineCodeHighlight-inlineCodeHighlight">debugged_sum()</code>, и нужно будет изменить весь код, что использовал <code style="margin-bottom:var(--mantine-spacing-lg)" class="m_dfe9c588 mantine-InlineCodeHighlight-inlineCodeHighlight m_e597c321 mantine-CodeHighlight-codeHighlight m_dfe9c588 mantine-InlineCodeHighlight-inlineCodeHighlight">sum()</code>. Но ведь имя функции это всего лишь имя переменной, и мы можем перезаписать новую, декорированную функцию в ту же переменную.</p>
<div style="margin-bottom:var(--mantine-spacing-lg)" class="m_e597c321 mantine-CodeHighlight-codeHighlight" dir="ltr"><div class="m_be7e9c9c mantine-CodeHighlight-controls"><button style="--ai-bg:transparent;--ai-hover:transparent;--ai-color:inherit;--ai-bd:none" class="mantine-focus-auto mantine-active m_d498bab7 mantine-CodeHighlight-control m_8d3f4000 mantine-ActionIcon-root m_87cf2631 mantine-UnstyledButton-root" data-variant="none" type="button" aria-label="Copy code"><span class="m_8d3afb97 mantine-ActionIcon-icon"><svg xmlns="http://www.w3.org/2000/svg" viewBox="0 0 24 24" stroke-width="2" stroke="currentColor" fill="none" stroke-linecap="round" stroke-linejoin="round"><path stroke="none" d="M0 0h24v24H0z" fill="none"></path><path d="M8 8m0 2a2 2 0 0 1 2 -2h8a2 2 0 0 1 2 2v8a2 2 0 0 1 -2 2h-8a2 2 0 0 1 -2 -2z"></path><path d="M16 8v-2a2 2 0 0 0 -2 -2h-8a2 2 0 0 0 -2 2v8a2 2 0 0 0 2 2h2"></path></svg></span></button></div><div style="--scrollarea-scrollbar-size:calc(0.25rem * var(--mantine-scale));--sa-corner-width:0px;--sa-corner-height:0px" class="m_f744fd40 mantine-CodeHighlight-scrollarea m_d57069b5 mantine-ScrollArea-root" dir="ltr"><div style="overflow-x:hidden;overflow-y:hidden;overscroll-behavior-inline:none" class="m_c0783ff9 mantine-ScrollArea-viewport" data-scrollbars="xy"><div class="m_b1336c6 mantine-ScrollArea-content"><pre class="m_2c47c4fd mantine-CodeHighlight-pre" style="padding:0"><code class="m_5caae6d3 mantine-CodeHighlight-code">sum = debug_decorator(sum)
sum(1, 2, 3) # 6
# => Args: (1, 2, 3) {}
# => Result: 6
def sum_odd_numbers(nums):
odd = []
for num in nums:
if num % 2 != 0:
odd.append(num)
return sum(*odd)
numbers = [1, 2, 3, 4, 5]
sum_odd_numbers(numbers) # 9
# => Args: (1, 3, 5) {}
# => Result: 9</code></pre></div></div></div><button class="mantine-focus-auto m_c9378bc2 mantine-CodeHighlight-showCodeButton m_87cf2631 mantine-UnstyledButton-root" data-hidden="true" type="button">Expand code</button></div>
<p>Теперь функция <code style="margin-bottom:var(--mantine-spacing-lg)" class="m_dfe9c588 mantine-InlineCodeHighlight-inlineCodeHighlight m_e597c321 mantine-CodeHighlight-codeHighlight m_dfe9c588 mantine-InlineCodeHighlight-inlineCodeHighlight">sum()</code> будет выполняться с дополнительными отладочными сообщениями. А также весь код, использующий функцию <code style="margin-bottom:var(--mantine-spacing-lg)" class="m_dfe9c588 mantine-InlineCodeHighlight-inlineCodeHighlight m_e597c321 mantine-CodeHighlight-codeHighlight m_dfe9c588 mantine-InlineCodeHighlight-inlineCodeHighlight">sum()</code> будет использовать новую функцию.</p>
<p>Чтобы не добавлять запись вида <code style="margin-bottom:var(--mantine-spacing-lg)" class="m_dfe9c588 mantine-InlineCodeHighlight-inlineCodeHighlight m_e597c321 mantine-CodeHighlight-codeHighlight m_dfe9c588 mantine-InlineCodeHighlight-inlineCodeHighlight">func = decorator(func)</code> каждый раз, в Python добавили синтаксический сахар - <code style="margin-bottom:var(--mantine-spacing-lg)" class="m_dfe9c588 mantine-InlineCodeHighlight-inlineCodeHighlight m_e597c321 mantine-CodeHighlight-codeHighlight m_dfe9c588 mantine-InlineCodeHighlight-inlineCodeHighlight">@decorator</code></p>
<div style="margin-bottom:var(--mantine-spacing-lg)" class="m_e597c321 mantine-CodeHighlight-codeHighlight" dir="ltr"><div class="m_be7e9c9c mantine-CodeHighlight-controls"><button style="--ai-bg:transparent;--ai-hover:transparent;--ai-color:inherit;--ai-bd:none" class="mantine-focus-auto mantine-active m_d498bab7 mantine-CodeHighlight-control m_8d3f4000 mantine-ActionIcon-root m_87cf2631 mantine-UnstyledButton-root" data-variant="none" type="button" aria-label="Copy code"><span class="m_8d3afb97 mantine-ActionIcon-icon"><svg xmlns="http://www.w3.org/2000/svg" viewBox="0 0 24 24" stroke-width="2" stroke="currentColor" fill="none" stroke-linecap="round" stroke-linejoin="round"><path stroke="none" d="M0 0h24v24H0z" fill="none"></path><path d="M8 8m0 2a2 2 0 0 1 2 -2h8a2 2 0 0 1 2 2v8a2 2 0 0 1 -2 2h-8a2 2 0 0 1 -2 -2z"></path><path d="M16 8v-2a2 2 0 0 0 -2 -2h-8a2 2 0 0 0 -2 2v8a2 2 0 0 0 2 2h2"></path></svg></span></button></div><div style="--scrollarea-scrollbar-size:calc(0.25rem * var(--mantine-scale));--sa-corner-width:0px;--sa-corner-height:0px" class="m_f744fd40 mantine-CodeHighlight-scrollarea m_d57069b5 mantine-ScrollArea-root" dir="ltr"><div style="overflow-x:hidden;overflow-y:hidden;overscroll-behavior-inline:none" class="m_c0783ff9 mantine-ScrollArea-viewport" data-scrollbars="xy"><div class="m_b1336c6 mantine-ScrollArea-content"><pre class="m_2c47c4fd mantine-CodeHighlight-pre" style="padding:0"><code class="m_5caae6d3 mantine-CodeHighlight-code"># декоратор нужно добавить над определением функции, которую хотим декорировать
@debug_decorator
def sum(*nums):
result = 0
for num in nums:
result += num
return result
sum(1, 2) # 3
# => Args: (1, 2) {}
# => Result: 3</code></pre></div></div></div><button class="mantine-focus-auto m_c9378bc2 mantine-CodeHighlight-showCodeButton m_87cf2631 mantine-UnstyledButton-root" data-hidden="true" type="button">Expand code</button></div>
<p>Также мы можем создавать несколько декораторов для одной функции, которые будут применяться последовательно:</p>
<div style="margin-bottom:var(--mantine-spacing-lg)" class="m_e597c321 mantine-CodeHighlight-codeHighlight" dir="ltr"><div class="m_be7e9c9c mantine-CodeHighlight-controls"><button style="--ai-bg:transparent;--ai-hover:transparent;--ai-color:inherit;--ai-bd:none" class="mantine-focus-auto mantine-active m_d498bab7 mantine-CodeHighlight-control m_8d3f4000 mantine-ActionIcon-root m_87cf2631 mantine-UnstyledButton-root" data-variant="none" type="button" aria-label="Copy code"><span class="m_8d3afb97 mantine-ActionIcon-icon"><svg xmlns="http://www.w3.org/2000/svg" viewBox="0 0 24 24" stroke-width="2" stroke="currentColor" fill="none" stroke-linecap="round" stroke-linejoin="round"><path stroke="none" d="M0 0h24v24H0z" fill="none"></path><path d="M8 8m0 2a2 2 0 0 1 2 -2h8a2 2 0 0 1 2 2v8a2 2 0 0 1 -2 2h-8a2 2 0 0 1 -2 -2z"></path><path d="M16 8v-2a2 2 0 0 0 -2 -2h-8a2 2 0 0 0 -2 2v8a2 2 0 0 0 2 2h2"></path></svg></span></button></div><div style="--scrollarea-scrollbar-size:calc(0.25rem * var(--mantine-scale));--sa-corner-width:0px;--sa-corner-height:0px" class="m_f744fd40 mantine-CodeHighlight-scrollarea m_d57069b5 mantine-ScrollArea-root" dir="ltr"><div style="overflow-x:hidden;overflow-y:hidden;overscroll-behavior-inline:none" class="m_c0783ff9 mantine-ScrollArea-viewport" data-scrollbars="xy"><div class="m_b1336c6 mantine-ScrollArea-content"><pre class="m_2c47c4fd mantine-CodeHighlight-pre" style="padding:0"><code class="m_5caae6d3 mantine-CodeHighlight-code">@debug_decorator
@time_decorator
def sum(*nums):
result = 0
for num in nums:
result += num
return result</code></pre></div></div></div><button class="mantine-focus-auto m_c9378bc2 mantine-CodeHighlight-showCodeButton m_87cf2631 mantine-UnstyledButton-root" data-hidden="true" type="button">Expand code</button></div>
<p>В этом примере сначала применится декоратор времени выполнения, а затем отладочный декоратор.</p>
<h2 id="heading-2-3">Состояние</h2>
<p>Отличительная черта функций, что они не хранят состояния. Функции, мы говорим о чистых функциях, лишь преобразуют входные данные в выходные. Или как еще говорят, "отображают" входное множество на выходное.</p>
<p>Но замыкания позволяют запоминать значения и использовать их в последующих вычислениях.</p>
<div style="margin-bottom:var(--mantine-spacing-lg)" class="m_e597c321 mantine-CodeHighlight-codeHighlight" dir="ltr"><div class="m_be7e9c9c mantine-CodeHighlight-controls"><button style="--ai-bg:transparent;--ai-hover:transparent;--ai-color:inherit;--ai-bd:none" class="mantine-focus-auto mantine-active m_d498bab7 mantine-CodeHighlight-control m_8d3f4000 mantine-ActionIcon-root m_87cf2631 mantine-UnstyledButton-root" data-variant="none" type="button" aria-label="Copy code"><span class="m_8d3afb97 mantine-ActionIcon-icon"><svg xmlns="http://www.w3.org/2000/svg" viewBox="0 0 24 24" stroke-width="2" stroke="currentColor" fill="none" stroke-linecap="round" stroke-linejoin="round"><path stroke="none" d="M0 0h24v24H0z" fill="none"></path><path d="M8 8m0 2a2 2 0 0 1 2 -2h8a2 2 0 0 1 2 2v8a2 2 0 0 1 -2 2h-8a2 2 0 0 1 -2 -2z"></path><path d="M16 8v-2a2 2 0 0 0 -2 -2h-8a2 2 0 0 0 -2 2v8a2 2 0 0 0 2 2h2"></path></svg></span></button></div><div style="--scrollarea-scrollbar-size:calc(0.25rem * var(--mantine-scale));--sa-corner-width:0px;--sa-corner-height:0px" class="m_f744fd40 mantine-CodeHighlight-scrollarea m_d57069b5 mantine-ScrollArea-root" dir="ltr"><div style="overflow-x:hidden;overflow-y:hidden;overscroll-behavior-inline:none" class="m_c0783ff9 mantine-ScrollArea-viewport" data-scrollbars="xy"><div class="m_b1336c6 mantine-ScrollArea-content"><pre class="m_2c47c4fd mantine-CodeHighlight-pre" style="padding:0"><code class="m_5caae6d3 mantine-CodeHighlight-code"># Чистая функция - просто сортирует задачи по приоритету
def sort_tasks(tasks):
return sorted(tasks, key=lambda x: x["priority"], reverse=True)
# Замыкание - хранит задачи и позволяет управлять ими
def create_task_manager():
tasks = [] # хранилище задач в замыкании
def manage(action, task=None):
if action == "add" and task:
tasks.append(task)
elif action == "remove" and task:
tasks.remove(task)
elif action == "clear":
tasks.clear()
elif action == "get_high_priority":
return [t for t in tasks if t["priority"] >= 8]
return sorted(tasks, key=lambda x: x["priority"], reverse=True)
return manage
# Чистая функция - каждый раз новый результат
tasks_list = [{"name": "Task 1", "priority": 5}, {"name": "Task 2", "priority": 9}]
print(sort_tasks(tasks_list)) # всегда один и тот же вывод для одних и тех же данных
# Замыкание - хранит состояние и позволяет редактировать список задач
manager = create_task_manager()
print(
manager("add", {"name": "Task 1", "priority": 5})
) # [{'name': 'Task 1', 'priority': 5}]
print(
manager("add", {"name": "Task 2", "priority": 9})
) # [{'name': 'Task 2', 'priority': 9}, {'name': 'Task 1', 'priority': 5}]
print(manager("get_high_priority")) # [{'name': 'Task 2', 'priority': 9}]
print(manager("clear")) # []</code></pre></div></div></div><button class="mantine-focus-auto m_c9378bc2 mantine-CodeHighlight-showCodeButton m_87cf2631 mantine-UnstyledButton-root" data-hidden="true" type="button">Expand code</button></div>
<p>Замыкания, как и декораторы, позволяют нам создавать на базе функций собственные объекты и систему управления ими.</p>
<h2 id="heading-2-4">Декораторы с параметрами</h2>
<p>Что если мы хотели бы вызывать обернутую функцию с разными настройками? Для этого нам понадобятся декораторы с параметрами.</p>
<p>Общий синтаксис декоратора с параметрами выглядит так:</p>
<div style="margin-bottom:var(--mantine-spacing-lg)" class="m_e597c321 mantine-CodeHighlight-codeHighlight" dir="ltr"><div class="m_be7e9c9c mantine-CodeHighlight-controls"><button style="--ai-bg:transparent;--ai-hover:transparent;--ai-color:inherit;--ai-bd:none" class="mantine-focus-auto mantine-active m_d498bab7 mantine-CodeHighlight-control m_8d3f4000 mantine-ActionIcon-root m_87cf2631 mantine-UnstyledButton-root" data-variant="none" type="button" aria-label="Copy code"><span class="m_8d3afb97 mantine-ActionIcon-icon"><svg xmlns="http://www.w3.org/2000/svg" viewBox="0 0 24 24" stroke-width="2" stroke="currentColor" fill="none" stroke-linecap="round" stroke-linejoin="round"><path stroke="none" d="M0 0h24v24H0z" fill="none"></path><path d="M8 8m0 2a2 2 0 0 1 2 -2h8a2 2 0 0 1 2 2v8a2 2 0 0 1 -2 2h-8a2 2 0 0 1 -2 -2z"></path><path d="M16 8v-2a2 2 0 0 0 -2 -2h-8a2 2 0 0 0 -2 2v8a2 2 0 0 0 2 2h2"></path></svg></span></button></div><div style="--scrollarea-scrollbar-size:calc(0.25rem * var(--mantine-scale));--sa-corner-width:0px;--sa-corner-height:0px" class="m_f744fd40 mantine-CodeHighlight-scrollarea m_d57069b5 mantine-ScrollArea-root" dir="ltr"><div style="overflow-x:hidden;overflow-y:hidden;overscroll-behavior-inline:none" class="m_c0783ff9 mantine-ScrollArea-viewport" data-scrollbars="xy"><div class="m_b1336c6 mantine-ScrollArea-content"><pre class="m_2c47c4fd mantine-CodeHighlight-pre" style="padding:0"><code class="m_5caae6d3 mantine-CodeHighlight-code">def decorator_with_params(param1, param2, ...):
def actual_decorator(func):
def wrapper(*args, **kwargs):
# Здесь можем использовать param1, param2, ...
result = func(*args, **kwargs)
return result
return wrapper
return actual_decorator</code></pre></div></div></div><button class="mantine-focus-auto m_c9378bc2 mantine-CodeHighlight-showCodeButton m_87cf2631 mantine-UnstyledButton-root" data-hidden="true" type="button">Expand code</button></div>
<p>К обычному декоратору мы добавляем еще один слой с параметрами. И, конечно, в качестве параметров мы можем передавать другие функции:</p>
<div style="margin-bottom:var(--mantine-spacing-lg)" class="m_e597c321 mantine-CodeHighlight-codeHighlight" dir="ltr"><div class="m_be7e9c9c mantine-CodeHighlight-controls"><button style="--ai-bg:transparent;--ai-hover:transparent;--ai-color:inherit;--ai-bd:none" class="mantine-focus-auto mantine-active m_d498bab7 mantine-CodeHighlight-control m_8d3f4000 mantine-ActionIcon-root m_87cf2631 mantine-UnstyledButton-root" data-variant="none" type="button" aria-label="Copy code"><span class="m_8d3afb97 mantine-ActionIcon-icon"><svg xmlns="http://www.w3.org/2000/svg" viewBox="0 0 24 24" stroke-width="2" stroke="currentColor" fill="none" stroke-linecap="round" stroke-linejoin="round"><path stroke="none" d="M0 0h24v24H0z" fill="none"></path><path d="M8 8m0 2a2 2 0 0 1 2 -2h8a2 2 0 0 1 2 2v8a2 2 0 0 1 -2 2h-8a2 2 0 0 1 -2 -2z"></path><path d="M16 8v-2a2 2 0 0 0 -2 -2h-8a2 2 0 0 0 -2 2v8a2 2 0 0 0 2 2h2"></path></svg></span></button></div><div style="--scrollarea-scrollbar-size:calc(0.25rem * var(--mantine-scale));--sa-corner-width:0px;--sa-corner-height:0px" class="m_f744fd40 mantine-CodeHighlight-scrollarea m_d57069b5 mantine-ScrollArea-root" dir="ltr"><div style="overflow-x:hidden;overflow-y:hidden;overscroll-behavior-inline:none" class="m_c0783ff9 mantine-ScrollArea-viewport" data-scrollbars="xy"><div class="m_b1336c6 mantine-ScrollArea-content"><pre class="m_2c47c4fd mantine-CodeHighlight-pre" style="padding:0"><code class="m_5caae6d3 mantine-CodeHighlight-code">def filter_by(key):
def inner(func):
def wrapper(*args):
args = key(args)
print("filtered args = ", args)
return func(*args)
return wrapper
return inner
def odd(numbers):
result = []
for num in numbers:
if num % 2 == 1:
result.append(num)
return result
@filter_by(odd)
def sum(*nums):
result = 0
for num in nums:
result += num
return result
sum(3, 4, 5, 6) # 8
# => filtered args = [3, 5]</code></pre></div></div></div><button class="mantine-focus-auto m_c9378bc2 mantine-CodeHighlight-showCodeButton m_87cf2631 mantine-UnstyledButton-root" data-hidden="true" type="button">Expand code</button></div>
<p>И даже функции с собственными параметрами, используя замыкания:</p>
<div style="margin-bottom:var(--mantine-spacing-lg)" class="m_e597c321 mantine-CodeHighlight-codeHighlight" dir="ltr"><div class="m_be7e9c9c mantine-CodeHighlight-controls"><button style="--ai-bg:transparent;--ai-hover:transparent;--ai-color:inherit;--ai-bd:none" class="mantine-focus-auto mantine-active m_d498bab7 mantine-CodeHighlight-control m_8d3f4000 mantine-ActionIcon-root m_87cf2631 mantine-UnstyledButton-root" data-variant="none" type="button" aria-label="Copy code"><span class="m_8d3afb97 mantine-ActionIcon-icon"><svg xmlns="http://www.w3.org/2000/svg" viewBox="0 0 24 24" stroke-width="2" stroke="currentColor" fill="none" stroke-linecap="round" stroke-linejoin="round"><path stroke="none" d="M0 0h24v24H0z" fill="none"></path><path d="M8 8m0 2a2 2 0 0 1 2 -2h8a2 2 0 0 1 2 2v8a2 2 0 0 1 -2 2h-8a2 2 0 0 1 -2 -2z"></path><path d="M16 8v-2a2 2 0 0 0 -2 -2h-8a2 2 0 0 0 -2 2v8a2 2 0 0 0 2 2h2"></path></svg></span></button></div><div style="--scrollarea-scrollbar-size:calc(0.25rem * var(--mantine-scale));--sa-corner-width:0px;--sa-corner-height:0px" class="m_f744fd40 mantine-CodeHighlight-scrollarea m_d57069b5 mantine-ScrollArea-root" dir="ltr"><div style="overflow-x:hidden;overflow-y:hidden;overscroll-behavior-inline:none" class="m_c0783ff9 mantine-ScrollArea-viewport" data-scrollbars="xy"><div class="m_b1336c6 mantine-ScrollArea-content"><pre class="m_2c47c4fd mantine-CodeHighlight-pre" style="padding:0"><code class="m_5caae6d3 mantine-CodeHighlight-code">def less_than(max):
def inner(numbers):
result = []
for num in numbers:
if num < max:
result.append(num)
return result
return inner
@filter_by(less_than(5))
def sum(*nums):
result = 0
for num in nums:
result += num
return result
sum(3, 4, 5, 6) # 7
# => filtered args = [3, 4]
@filter_by(less_than(3))
def sum(*nums):
result = 0
for num in nums:
result += num
return result
sum(3, 4, 5, 6) # 0
# => filtered args = []</code></pre></div></div></div><button class="mantine-focus-auto m_c9378bc2 mantine-CodeHighlight-showCodeButton m_87cf2631 mantine-UnstyledButton-root" data-hidden="true" type="button">Expand code</button></div>
<p>Декоратор <code style="margin-bottom:var(--mantine-spacing-lg)" class="m_dfe9c588 mantine-InlineCodeHighlight-inlineCodeHighlight m_e597c321 mantine-CodeHighlight-codeHighlight m_dfe9c588 mantine-InlineCodeHighlight-inlineCodeHighlight">filter_by()</code> принимает функцию фильтрации <code style="margin-bottom:var(--mantine-spacing-lg)" class="m_dfe9c588 mantine-InlineCodeHighlight-inlineCodeHighlight m_e597c321 mantine-CodeHighlight-codeHighlight m_dfe9c588 mantine-InlineCodeHighlight-inlineCodeHighlight">less_than()</code>, затем применяет ее к аргументам обернутой функции, и вызывает обернутую функцию с новыми аргументами.</p>
<h2 id="heading-2-5">Выводы</h2>
<p>Мы изучили декораторы, мощный механизм для расширения функций. Существует большое количество готовых декораторов, доступных в стандартной библиотеке Python и других библиотеках. Некоторые из них позволяют кэшировать результаты функций, обеспечивать авторизацию и безопасность, профилировать код, проверять типы данных и многое другое.</p>
<p>Декораторы могут повлиять на производительность кода, поэтому их следует использовать с умом и осторожностью. Также нужно учитывать, что декораторы могут усложнить отладку кода и сделать его менее понятным для других разработчиков.</p>
<p>При этом декораторы являются сильным инструментом, который позволяет легко добавлять дополнительную функциональность к существующим функциям и классам и не изменять их исходный код.</p></div><div style="margin-block:var(--mantine-spacing-xl)" class=""><h2 style="--title-fw:var(--mantine-h2-font-weight);--title-lh:var(--mantine-h2-line-height);--title-fz:var(--mantine-h2-font-size);margin-bottom:var(--mantine-spacing-md)" class="m_8a5d1357 mantine-Title-root" data-order="2">Рекомендуемые программы</h2><style data-mantine-styles="inline">.__m__-_R_2mremqrdub_{--carousel-slide-gap:var(--mantine-spacing-xs);--carousel-slide-size:70%;}@media(min-width: 36em){.__m__-_R_2mremqrdub_{--carousel-slide-gap:var(--mantine-spacing-xl);--carousel-slide-size:50%;}}</style><div style="--carousel-control-size:calc(2.5rem * var(--mantine-scale));--carousel-controls-offset:var(--mantine-spacing-sm);margin-bottom:var(--mantine-spacing-lg);padding-block:var(--mantine-spacing-sm);background:var(--app-color-surface)" class="m_17884d0f mantine-Carousel-root responsiveClassName" data-orientation="horizontal" data-include-gap-in-size="true"><div class="m_39bc3463 mantine-Carousel-controls" data-orientation="horizontal"><button class="mantine-focus-auto m_64f58e10 mantine-Carousel-control m_87cf2631 mantine-UnstyledButton-root" type="button" data-inactive="true" data-type="previous" tabindex="-1"><svg viewBox="0 0 15 15" fill="none" xmlns="http://www.w3.org/2000/svg" style="transform:rotate(90deg);width:calc(1rem * var(--mantine-scale));height:calc(1rem * var(--mantine-scale));display:block"><path d="M3.13523 6.15803C3.3241 5.95657 3.64052 5.94637 3.84197 6.13523L7.5 9.56464L11.158 6.13523C11.3595 5.94637 11.6759 5.95657 11.8648 6.15803C12.0536 6.35949 12.0434 6.67591 11.842 6.86477L7.84197 10.6148C7.64964 10.7951 7.35036 10.7951 7.15803 10.6148L3.15803 6.86477C2.95657 6.67591 2.94637 6.35949 3.13523 6.15803Z" fill="currentColor" fill-rule="evenodd" clip-rule="evenodd"></path></svg></button><button class="mantine-focus-auto m_64f58e10 mantine-Carousel-control m_87cf2631 mantine-UnstyledButton-root" type="button" data-inactive="true" data-type="next" tabindex="-1"><svg viewBox="0 0 15 15" fill="none" xmlns="http://www.w3.org/2000/svg" style="transform:rotate(-90deg);width:calc(1rem * var(--mantine-scale));height:calc(1rem * var(--mantine-scale));display:block"><path d="M3.13523 6.15803C3.3241 5.95657 3.64052 5.94637 3.84197 6.13523L7.5 9.56464L11.158 6.13523C11.3595 5.94637 11.6759 5.95657 11.8648 6.15803C12.0536 6.35949 12.0434 6.67591 11.842 6.86477L7.84197 10.6148C7.64964 10.7951 7.35036 10.7951 7.15803 10.6148L3.15803 6.86477C2.95657 6.67591 2.94637 6.35949 3.13523 6.15803Z" fill="currentColor" fill-rule="evenodd" clip-rule="evenodd"></path></svg></button></div><div class="m_a2dae653 mantine-Carousel-viewport" data-type="media"><div class="m_fcd81474 mantine-Carousel-container __m__-_R_2mremqrdub_" data-orientation="horizontal"><div class="m_d98df724 mantine-Carousel-slide" data-orientation="horizontal"><div tabindex="0" style="cursor:pointer;height:100%"><a style="text-decoration:none" class="mantine-focus-auto m_849cf0da m_b6d8b162 mantine-Text-root mantine-Anchor-root" data-underline="hover" href="/programs/python?promo_name=programs_list&promo_position=course&promo_creative=catalog_card&promo_type=card" target="_blank"><div style="height:100%" class="m_e615b15f mantine-Card-root m_1b7284a3 mantine-Paper-root" data-with-border="true"><div style="--group-gap:calc(0.25rem * var(--mantine-scale));--group-align:center;--group-justify:flex-start;--group-wrap:nowrap" class="m_4081bf90 mantine-Group-root"><span style="font-size:var(--mantine-font-size-sm)" class="mantine-focus-auto m_b6d8b162 mantine-Text-root">10 месяцев</span><span class="mantine-focus-auto m_b6d8b162 mantine-Text-root">·</span><span style="font-size:var(--mantine-font-size-sm)" class="mantine-focus-auto m_b6d8b162 mantine-Text-root">С нуля</span></div><p style="margin-bottom:var(--mantine-spacing-sm);font-size:var(--mantine-font-size-h5);font-weight:bold" class="mantine-focus-auto m_b6d8b162 mantine-Text-root">Python-разработчик </p><p class="mantine-focus-auto m_b6d8b162 mantine-Text-root">Изучите Python, Django, REST и Fast API для создания веб-приложений</p><div style="margin-top:auto" class=""><div class="m_4451eb3a mantine-Center-root"><img style="opacity:0.8;width:70%" class="m_9e117634 mantine-Image-root mantine-visible-from-xs" src="https://hexlet.io/rails/active_storage/representations/proxy/eyJfcmFpbHMiOnsiZGF0YSI6MzczMSwicHVyIjoiYmxvYl9pZCJ9fQ==--f5df4883f3f678321cb4fa96e9ce657bd5ee1adf/eyJfcmFpbHMiOnsiZGF0YSI6eyJmb3JtYXQiOiJ3ZWJwIiwicmVzaXplX3RvX2xpbWl0IjpbNDAwLDQwMF0sInNhdmVyIjp7InF1YWxpdHkiOjg1fX0sInB1ciI6InZhcmlhdGlvbiJ9fQ==--5b6f46dacd1af664f27558553a58076185091823/Static%20website-cuate.png" alt="Python-разработчик " loading="eager"/></div><div style="--group-gap:var(--mantine-spacing-md);--group-align:end;--group-justify:space-between;--group-wrap:wrap;margin-top:var(--mantine-spacing-xs)" class="m_4081bf90 mantine-Group-root"><p style="font-size:var(--mantine-font-size-xl)" class="mantine-focus-auto m_b6d8b162 mantine-Text-root">от 6 792 ₽</p><p style="font-size:var(--mantine-font-size-sm)" class="mantine-focus-auto m_b6d8b162 mantine-Text-root">Посмотреть →</p></div></div></div></a></div></div><div class="m_d98df724 mantine-Carousel-slide" data-orientation="horizontal"><div tabindex="0" style="cursor:pointer;height:100%"><a style="text-decoration:none" class="mantine-focus-auto m_849cf0da m_b6d8b162 mantine-Text-root mantine-Anchor-root" data-underline="hover" href="/programs/qa-auto-engineer-python?promo_name=programs_list&promo_position=course&promo_creative=catalog_card&promo_type=card" target="_blank"><div style="height:100%" class="m_e615b15f mantine-Card-root m_1b7284a3 mantine-Paper-root" data-with-border="true"><div style="--group-gap:calc(0.25rem * var(--mantine-scale));--group-align:center;--group-justify:flex-start;--group-wrap:nowrap" class="m_4081bf90 mantine-Group-root"><span style="font-size:var(--mantine-font-size-sm)" class="mantine-focus-auto m_b6d8b162 mantine-Text-root">6 месяцев</span><span class="mantine-focus-auto m_b6d8b162 mantine-Text-root">·</span><span style="font-size:var(--mantine-font-size-sm)" class="mantine-focus-auto m_b6d8b162 mantine-Text-root">Для продвинутых</span></div><p style="margin-bottom:var(--mantine-spacing-sm);font-size:var(--mantine-font-size-h5);font-weight:bold" class="mantine-focus-auto m_b6d8b162 mantine-Text-root">Автоматизатор тестирования на Python</p><p class="mantine-focus-auto m_b6d8b162 mantine-Text-root">Изучите Python, фреймворки для тестирования, автоматизация UI и API</p><div style="margin-top:auto" class=""><div class="m_4451eb3a mantine-Center-root"><img style="opacity:0.8;width:70%" class="m_9e117634 mantine-Image-root mantine-visible-from-xs" src="https://hexlet.io/rails/active_storage/representations/proxy/eyJfcmFpbHMiOnsiZGF0YSI6MzkyMCwicHVyIjoiYmxvYl9pZCJ9fQ==--71cd9d863b21d7bfbd927cf623a7a2baaf4530ca/eyJfcmFpbHMiOnsiZGF0YSI6eyJmb3JtYXQiOiJ3ZWJwIiwicmVzaXplX3RvX2xpbWl0IjpbNDAwLDQwMF0sInNhdmVyIjp7InF1YWxpdHkiOjg1fX0sInB1ciI6InZhcmlhdGlvbiJ9fQ==--5b6f46dacd1af664f27558553a58076185091823/Bug%20fixing-cuate.png" alt="Автоматизатор тестирования на Python" loading="eager"/></div><div style="--group-gap:var(--mantine-spacing-md);--group-align:end;--group-justify:space-between;--group-wrap:wrap;margin-top:var(--mantine-spacing-xs)" class="m_4081bf90 mantine-Group-root"><p style="font-size:var(--mantine-font-size-xl)" class="mantine-focus-auto m_b6d8b162 mantine-Text-root">от 4 281 ₽</p><p style="font-size:var(--mantine-font-size-sm)" class="mantine-focus-auto m_b6d8b162 mantine-Text-root">Посмотреть →</p></div></div></div></a></div></div><div class="m_d98df724 mantine-Carousel-slide" data-orientation="horizontal"><div tabindex="0" style="cursor:pointer;height:100%"><a style="text-decoration:none" class="mantine-focus-auto m_849cf0da m_b6d8b162 mantine-Text-root mantine-Anchor-root" data-underline="hover" href="/programs/devops-engineer-from-scratch?promo_name=programs_list&promo_position=course&promo_creative=catalog_card&promo_type=card" target="_blank"><div style="height:100%" class="m_e615b15f mantine-Card-root m_1b7284a3 mantine-Paper-root" data-with-border="true"><div style="--group-gap:calc(0.25rem * var(--mantine-scale));--group-align:center;--group-justify:flex-start;--group-wrap:nowrap" class="m_4081bf90 mantine-Group-root"><span style="font-size:var(--mantine-font-size-sm)" class="mantine-focus-auto m_b6d8b162 mantine-Text-root">14 месяцев</span><span class="mantine-focus-auto m_b6d8b162 mantine-Text-root">·</span><span style="font-size:var(--mantine-font-size-sm)" class="mantine-focus-auto m_b6d8b162 mantine-Text-root">С нуля</span></div><p style="margin-bottom:var(--mantine-spacing-sm);font-size:var(--mantine-font-size-h5);font-weight:bold" class="mantine-focus-auto m_b6d8b162 mantine-Text-root">DevOps-инженер с нуля</p><p class="mantine-focus-auto m_b6d8b162 mantine-Text-root">Полное погружение в DevOps: весь стек от Linux до Kubernetes</p><div style="margin-top:auto" class=""><div class="m_4451eb3a mantine-Center-root"><img style="opacity:0.8;width:70%" class="m_9e117634 mantine-Image-root mantine-visible-from-xs" src="https://hexlet.io/rails/active_storage/representations/proxy/eyJfcmFpbHMiOnsiZGF0YSI6Mzk2NSwicHVyIjoiYmxvYl9pZCJ9fQ==--84278a1852c9c6fb13b80a69f395bac6e47a422e/eyJfcmFpbHMiOnsiZGF0YSI6eyJmb3JtYXQiOiJ3ZWJwIiwicmVzaXplX3RvX2xpbWl0IjpbNDAwLDQwMF0sInNhdmVyIjp7InF1YWxpdHkiOjg1fX0sInB1ciI6InZhcmlhdGlvbiJ9fQ==--5b6f46dacd1af664f27558553a58076185091823/Cloud%20sync-bro.png" alt="DevOps-инженер с нуля" loading="eager"/></div><div style="--group-gap:var(--mantine-spacing-md);--group-align:end;--group-justify:space-between;--group-wrap:wrap;margin-top:var(--mantine-spacing-xs)" class="m_4081bf90 mantine-Group-root"><p style="font-size:var(--mantine-font-size-xl)" class="mantine-focus-auto m_b6d8b162 mantine-Text-root">от 6 792 ₽</p><p style="font-size:var(--mantine-font-size-sm)" class="mantine-focus-auto m_b6d8b162 mantine-Text-root">Посмотреть →</p></div></div></div></a></div></div><div class="m_d98df724 mantine-Carousel-slide" data-orientation="horizontal"><div tabindex="0" style="cursor:pointer;height:100%"><a style="text-decoration:none" class="mantine-focus-auto m_849cf0da m_b6d8b162 mantine-Text-root mantine-Anchor-root" data-underline="hover" href="/courses?promo_name=programs_list&promo_position=course&promo_creative=catalog_card&promo_type=card"><div style="height:100%" class="m_e615b15f mantine-Card-root m_1b7284a3 mantine-Paper-root" data-with-border="true"><h2 style="--title-fw:var(--mantine-h2-font-weight);--title-lh:var(--mantine-h2-line-height);--title-fz:var(--mantine-h2-font-size);margin-bottom:var(--mantine-spacing-md);font-size:var(--mantine-font-size-h3)" class="m_8a5d1357 mantine-Title-root" data-order="2" data-responsive="true">Каталог</h2><p style="margin-bottom:auto" class="mantine-focus-auto m_b6d8b162 mantine-Text-root">Полный список доступных курсов по разным направлениям</p><div style="margin-top:auto" class=""><div class="m_4451eb3a mantine-Center-root"><img style="opacity:0.8;width:70%" class="m_9e117634 mantine-Image-root mantine-visible-from-xs" src="/vite/assets/development-BVihs_d5.png" alt="Orientation"/></div></div></div></a></div></div></div></div></div></div></div></div></div><style data-mantine-styles="inline">.__m__-_R_1bdub_{--col-flex-grow:auto;--col-flex-basis:8.333333333333334%;--col-max-width:8.333333333333334%;}@media(min-width: 48em){.__m__-_R_1bdub_{--col-flex-grow:auto;--col-flex-basis:16.666666666666668%;--col-max-width:16.666666666666668%;}}</style><div style="min-width:0rem;height:100%;min-height:0rem" class="m_96bdd299 mantine-Grid-col __m__-_R_1bdub_"><div style="margin-inline:var(--mantine-spacing-xs)" class="mantine-visible-from-sm"><a style="--button-color:var(--mantine-color-white);margin-bottom:var(--mantine-spacing-lg);text-decoration:none" class="mantine-focus-auto m_849cf0da mantine-focus-auto m_77c9d27d mantine-Button-root m_87cf2631 mantine-UnstyledButton-root m_b6d8b162 mantine-Text-root mantine-Anchor-root" data-underline="hover" href="/courses/python-functions/lessons/decorators/finish_unit?unit=theory" data-disabled="true" data-block="true" disabled=""><span class="m_80f1301b mantine-Button-inner"><span class="m_811560b9 mantine-Button-label"><span style="margin-inline-end:var(--mantine-spacing-xs)" class="mantine-focus-auto m_b6d8b162 mantine-Text-root">Дальше</span>→</span></span></a><a style="padding-inline:0rem" class="mantine-focus-auto m_f0824112 mantine-NavLink-root m_87cf2631 mantine-UnstyledButton-root"><span class="m_690090b5 mantine-NavLink-section" data-position="left"><div style="--ti-size:var(--ti-size-sm);--ti-bg:transparent;--ti-color:var(--mantine-color-indigo-light-color);--ti-bd:calc(0.0625rem * var(--mantine-scale)) solid transparent;color:inherit" class="m_7341320d mantine-ThemeIcon-root" data-variant="transparent" data-size="sm"><svg xmlns="http://www.w3.org/2000/svg" width="24" height="24" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="1.2" stroke-linecap="round" stroke-linejoin="round" class="tabler-icon tabler-icon-list-numbers "><path d="M11 6h9"></path><path d="M11 12h9"></path><path d="M12 18h8"></path><path d="M4 16a2 2 0 1 1 4 0c0 .591 -.5 1 -1 1.5l-3 2.5h4"></path><path d="M6 10v-6l-2 2"></path></svg></div></span><div class="m_f07af9d2 mantine-NavLink-body"><span class="m_1f6ac4c4 mantine-NavLink-label">Навигация по теме</span><span class="m_57492dcc mantine-NavLink-description">Теория</span></div><span class="m_690090b5 mantine-NavLink-section" data-position="right"></span></a><div style="margin-block:var(--mantine-spacing-lg)" class="m_3eebeb36 mantine-Divider-root" data-orientation="horizontal" role="separator"></div><div style="margin-block:var(--mantine-spacing-lg)" class=""><div style="justify-content:space-between;margin-bottom:calc(0.1875rem * var(--mantine-scale));color:var(--mantine-color-dimmed);font-size:var(--mantine-font-size-xs)" class="m_8bffd616 mantine-Flex-root __m__-_R_qimrbdub_"><p style="font-size:var(--mantine-font-size-xs)" class="mantine-focus-auto m_b6d8b162 mantine-Text-root">Завершено</p><p style="font-size:var(--mantine-font-size-xs)" class="mantine-focus-auto m_b6d8b162 mantine-Text-root">0 / 15</p></div><div style="--progress-size:var(--progress-size-sm)" class="m_db6d6462 mantine-Progress-root" data-size="sm"><div style="--progress-section-size:0%;--progress-section-color:var(--mantine-color-gray-filled)" class="m_2242eb65 mantine-Progress-section" role="progressbar" aria-valuemax="100" aria-valuemin="0" aria-valuenow="0" aria-valuetext="0%"></div></div></div><button style="padding-inline:0rem" class="mantine-focus-auto m_f0824112 mantine-NavLink-root m_87cf2631 mantine-UnstyledButton-root" type="button"><span class="m_690090b5 mantine-NavLink-section" data-position="left"><div style="--ti-size:var(--ti-size-sm);--ti-bg:transparent;--ti-color:var(--mantine-color-indigo-light-color);--ti-bd:calc(0.0625rem * var(--mantine-scale)) solid transparent;color:inherit" class="m_7341320d mantine-ThemeIcon-root" data-variant="transparent" data-size="sm"><svg xmlns="http://www.w3.org/2000/svg" width="24" height="24" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="1.2" stroke-linecap="round" stroke-linejoin="round" class="tabler-icon tabler-icon-message "><path d="M8 9h8"></path><path d="M8 13h6"></path><path d="M18 4a3 3 0 0 1 3 3v8a3 3 0 0 1 -3 3h-5l-5 3v-3h-2a3 3 0 0 1 -3 -3v-8a3 3 0 0 1 3 -3h12"></path></svg></div></span><div class="m_f07af9d2 mantine-NavLink-body"><span class="m_1f6ac4c4 mantine-NavLink-label">Обсуждения (архив)</span><span class="m_57492dcc mantine-NavLink-description"></span></div></button><div style="--toc-bg:var(--mantine-color-blue-light);--toc-color:var(--mantine-color-blue-light-color);--toc-size:var(--mantine-font-size-sm);--toc-radius:var(--mantine-radius-sm);margin-top:var(--mantine-spacing-xl)" class="m_bcaa9990 mantine-TableOfContents-root" data-variant="light" data-size="sm"></div></div><div class="mantine-hidden-from-sm"><div style="--stack-gap:0rem;--stack-align:stretch;--stack-justify:flex-start" class="m_6d731127 mantine-Stack-root"><a style="--button-color:var(--mantine-color-white);margin-bottom:var(--mantine-spacing-xs);padding:0rem;text-decoration:none" class="mantine-focus-auto m_849cf0da mantine-focus-auto m_77c9d27d mantine-Button-root m_87cf2631 mantine-UnstyledButton-root m_b6d8b162 mantine-Text-root mantine-Anchor-root" data-underline="hover" href="/courses/python-functions/lessons/decorators/finish_unit?unit=theory" data-disabled="true" data-block="true" disabled=""><span class="m_80f1301b mantine-Button-inner"><span class="m_811560b9 mantine-Button-label">→</span></span></a><button style="--ai-size:var(--ai-size-sm);--ai-bg:transparent;--ai-hover:var(--mantine-color-indigo-light-hover);--ai-color:var(--mantine-color-indigo-light-color);--ai-bd:calc(0.0625rem * var(--mantine-scale)) solid transparent;padding-block:var(--mantine-spacing-lg);color:inherit;width:100%" class="mantine-focus-auto m_8d3f4000 mantine-ActionIcon-root m_87cf2631 mantine-UnstyledButton-root" data-variant="subtle" data-size="sm" data-disabled="true" type="button" disabled=""><span class="m_8d3afb97 mantine-ActionIcon-icon"><svg xmlns="http://www.w3.org/2000/svg" width="24" height="24" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="1.2" stroke-linecap="round" stroke-linejoin="round" class="tabler-icon tabler-icon-list-numbers "><path d="M11 6h9"></path><path d="M11 12h9"></path><path d="M12 18h8"></path><path d="M4 16a2 2 0 1 1 4 0c0 .591 -.5 1 -1 1.5l-3 2.5h4"></path><path d="M6 10v-6l-2 2"></path></svg></span></button><button style="--ai-size:var(--ai-size-sm);--ai-bg:transparent;--ai-hover:var(--mantine-color-indigo-light-hover);--ai-color:var(--mantine-color-indigo-light-color);--ai-bd:calc(0.0625rem * var(--mantine-scale)) solid transparent;padding-block:var(--mantine-spacing-lg);color:inherit;width:100%" class="mantine-focus-auto mantine-active m_8d3f4000 mantine-ActionIcon-root m_87cf2631 mantine-UnstyledButton-root" data-variant="subtle" data-size="sm" type="button"><span class="m_8d3afb97 mantine-ActionIcon-icon"><svg xmlns="http://www.w3.org/2000/svg" width="24" height="24" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="1.2" stroke-linecap="round" stroke-linejoin="round" class="tabler-icon tabler-icon-message "><path d="M8 9h8"></path><path d="M8 13h6"></path><path d="M18 4a3 3 0 0 1 3 3v8a3 3 0 0 1 -3 3h-5l-5 3v-3h-2a3 3 0 0 1 -3 -3v-8a3 3 0 0 1 3 -3h12"></path></svg></span></button></div></div></div></div></div></div></div>
</main>
<footer class="bg-dark fw-light text-light px-3 py-5">
<div class="row small">
<div class="col-12 col-sm-6 col-md-3">
<div class="h5 mb-3">Хекслет</div>
<ul class="list-unstyled">
<li>
<a class="nav-link link-light py-1 ps-0" href="/pages/about">О нас</a>
</li>
<li>
<a class="nav-link link-light py-1 ps-0" href="/testimonials">Отзывы</a>
</li>
<li>
<span class="nav-link link-light py-1 ps-0 external-link" data-href="https://b2b.hexlet.io" role="button">Корпоративное обучение</span>
</li>
<li>
<a class="nav-link link-light py-1 ps-0" href="/blog">Блог</a>
</li>
<li>
<a class="nav-link link-light py-1 ps-0" href="/qna">Вопросы и ответы</a>
</li>
<li>
<a class="nav-link link-light py-1 ps-0" href="/glossary">Глоссарий</a>
</li>
<li>
<span class="nav-link link-light py-1 ps-0 external-link" data-href="https://help.hexlet.io" data-target="_blank" role="button">Справка</span>
</li>
<li>
<a class="nav-link link-light py-1 ps-0" target="_blank" rel="noopener noreferrer" href="/map">Карта сайта</a>
</li>
</ul>
</div>
<div class="col-12 col-sm-6 col-md-3">
<div class="h5 fw-normal mb-3">Направления</div>
<ul class="list-unstyled">
<li>
<a class="nav-link link-light py-1 ps-0" href="/courses_devops">DevOps
</a></li>
<li>
<a class="nav-link link-light py-1 ps-0" href="/courses_data_analytics">Аналитика
</a></li>
<li>
<a class="nav-link link-light py-1 ps-0" href="/courses_backend_development">Бэкенд
</a></li>
<li>
<a class="nav-link link-light py-1 ps-0" href="/courses_programming">Программирование
</a></li>
<li>
<a class="nav-link link-light py-1 ps-0" href="/courses_testing">Тестирование
</a></li>
<li>
<a class="nav-link link-light py-1 ps-0" href="/courses_front_end_dev">Фронтенд
</a></li>
</ul>
</div>
<div class="col-12 col-sm-6 col-md-3">
<div class="h5">Профессии</div>
<ul class="list-unstyled">
<li>
<a class="nav-link link-light py-1 ps-0" href="/programs/devops-engineer-from-scratch">DevOps-инженер с нуля</a>
</li>
<li>
<a class="nav-link link-light py-1 ps-0" href="/programs/go">Go-разработчик</a>
</li>
<li>
<a class="nav-link link-light py-1 ps-0" href="/programs/java">Java-разработчик</a>
</li>
<li>
<a class="nav-link link-light py-1 ps-0" href="/programs/python">Python-разработчик </a>
</li>
<li>
<a class="nav-link link-light py-1 ps-0" href="/programs/data-analytics">Аналитик данных</a>
</li>
<li>
<a class="nav-link link-light py-1 ps-0" href="/programs/qa-engineer">Инженер по ручному тестированию</a>
</li>
<li>
<a class="nav-link link-light py-1 ps-0" href="/programs/php">РНР-разработчик</a>
</li>
<li>
<a class="nav-link link-light py-1 ps-0" href="/programs/frontend">Фронтенд-разработчик</a>
</li>
</ul>
</div>
<div class="col-12 col-sm-6 col-md-3">
<div class="h5">Навыки</div>
<ul class="list-unstyled">
<li>
<a class="nav-link link-light py-1 ps-0" href="/programs/python-django-developer">Django</a>
</li>
<li>
<a class="nav-link link-light py-1 ps-0" href="/programs/docker">Docker</a>
</li>
<li>
<a class="nav-link link-light py-1 ps-0" href="/programs/php-laravel-developer">Laravel</a>
</li>
<li>
<a class="nav-link link-light py-1 ps-0" href="/programs/postman">Postman</a>
</li>
<li>
<a class="nav-link link-light py-1 ps-0" href="/programs/js-react-developer">React</a>
</li>
<li>
<a class="nav-link link-light py-1 ps-0" href="/programs/js-rest-api">REST API в Node.js</a>
</li>
<li>
<a class="nav-link link-light py-1 ps-0" href="/programs/spring-boot">Spring Boot</a>
</li>
<li>
<a class="nav-link link-light py-1 ps-0" href="/programs/typescript">Typescript</a>
</li>
</ul>
</div>
</div>
<hr>
<div class="row">
<div class="col-12 col-sm-4 col-md-2">
<div class="fs-4">
<ul class="list-unstyled d-flex">
<li class="me-3">
<a aria-label="Telegram" target="_blank" class="link-light" rel="noopener noreferrer nofollow" href="https://t.me/hexlet_ru"><span class="bi bi-telegram"></span>
</a></li>
<li>
<a aria-label="Youtube" target="_blank" class="link-light" rel="noopener noreferrer nofollow" href="https://www.youtube.com/user/HexletUniversity"><span class="bi bi-youtube"></span>
</a></li>
</ul>
</div>
<div class="mb-2 d-flex flex-column">
<a class="link-light text-decoration-none" rel="nofollow" href="mailto:support@hexlet.io">support@hexlet.io</a>
<a class="link-light text-decoration-none py-2" target="_blank" href="https://t.me/hexlet_help_bot">t.me/hexlet_help_bot</a>
</div>
<ul class="list-unstyled d-flex">
<li class="me-3">
<span class="link-light text-decoration-none opacity-50 x-font-size-18 external-link" rel="nofollow" data-href="https://hexlet.io/locale/switch?new_locale=en" data-target="_self" role="button"><span class="my-auto">EN</span>
</span></li>
<li class="me-3">
<span class="link-light text-decoration-none opacity-50 x-font-size-18 opacity-100 external-link" rel="nofollow" data-href="https://ru.hexlet.io/locale/switch?new_locale=ru" data-target="_self" role="button"><span class="my-auto">RU</span>
</span></li>
<li class="me-3">
<span class="link-light text-decoration-none opacity-50 x-font-size-18 external-link" rel="nofollow" data-href="https://kz.hexlet.io/locale/switch?new_locale=kz" data-target="_self" role="button"><span class="my-auto">KZ</span>
</span></li>
</ul>
</div>
<div class="col-12 col-sm-4 col-md-3">
<ul class="list-unstyled fs-4">
<li class="mb-3">
<a class="link-light text-decoration-none" href="tel:8%20800%20100%2022%2047">8 800 100 22 47</a>
<span class="d-block opacity-50 small">бесплатно по РФ</span>
</li>
<li>
<a class="link-light text-decoration-none" href="tel:%2B7%20495%20085%2021%2062">+7 495 085 21 62</a>
<span class="d-block opacity-50 small">бесплатно по Москве</span>
</li>
</ul>
</div>
<div class="col-12 col-sm-4 col-md-3">
<div class="small mb-3">Образовательные услуги оказываются на основании Л035-01298-77/01989008 от 14.03.2025</div>
<ul class="list-unstyled small">
<li>
<a class="nav-link link-light py-1 ps-0" href="/pages/legal">Правовая информация</a>
</li>
<li>
<a class="nav-link link-light py-1 ps-0" href="/pages/offer">Оферта</a>
</li>
<li>
<a class="nav-link link-light py-1 ps-0" href="/pages/license">Лицензия</a>
</li>
<li>
<a class="nav-link link-light py-1 ps-0" href="/pages/contacts">Контакты</a>
</li>
</ul>
</div>
<div class="col-12 col-sm-12 col-md-4 small">
<div class="mb-2">
<div>ООО «<a href="/" class="text-decoration-none link-light">Хекслет Рус</a>»</div>
<div>108813 г. Москва, вн.тер.г. поселение Московский,</div>
<div>г. Московский, ул. Солнечная, д. 3А, стр. 1, помещ. 20Б/3</div>
<div>ОГРН 1217300010476</div>
<div>ИНН 7325174845</div>
</div>
<hr>
<div>АНО ДПО «<a href="/" class="text-decoration-none link-light">Учебный центр «Хекслет</a>»</div>
<div>119331 г. Москва, вн. тер. г. муниципальный округ</div>
<div>Ломоносовский, пр-кт Вернадского, д. 29</div>
<div>ОГРН 1247700712390</div>
<div>ИНН 7736364948</div>
</div>
</div>
</footer>
<div id="root-assistant-offcanvas"></div>
<script src="/vite/assets/assistant-Bukl1lYy.js" crossorigin="anonymous" type="module"></script><link rel="modulepreload" href="/vite/assets/chunk-DsPFFUou.js" as="script" crossorigin="anonymous">
<link rel="modulepreload" href="/vite/assets/init-BrRXra1y.js" as="script" crossorigin="anonymous">
<link rel="modulepreload" href="/vite/assets/ErrorFallbackBlock-naDSYSy9.js" as="script" crossorigin="anonymous">
<link rel="modulepreload" href="/vite/assets/MarkdownBlock-DbyKWoR_.js" as="script" crossorigin="anonymous">
<link rel="modulepreload" href="/vite/assets/gon-D3e4yh1x.js" as="script" crossorigin="anonymous">
<link rel="modulepreload" href="/vite/assets/mantine-CGMYrt2Y.js" as="script" crossorigin="anonymous">
<link rel="modulepreload" href="/vite/assets/shiki-V011pkdv.js" as="script" crossorigin="anonymous">
<link rel="modulepreload" href="/vite/assets/utils-DRqSHbQE.js" as="script" crossorigin="anonymous">
<link rel="modulepreload" href="/vite/assets/routes-CCH8ilKF.js" as="script" crossorigin="anonymous">
<link rel="modulepreload" href="/vite/assets/lib-XR8Qr8kR.js" as="script" crossorigin="anonymous">
<link rel="modulepreload" href="/vite/assets/dist-GCHh59xr.js" as="script" crossorigin="anonymous">
<link rel="modulepreload" href="/vite/assets/Box-B5-OOzBf.js" as="script" crossorigin="anonymous">
<link rel="modulepreload" href="/vite/assets/notifications.store-C-3AFSMn.js" as="script" crossorigin="anonymous">
<link rel="modulepreload" href="/vite/assets/useIsomorphicEffect-HJ6VK0D3.js" as="script" crossorigin="anonymous">
<link rel="modulepreload" href="/vite/assets/lib-KSp6QbZ0.js" as="script" crossorigin="anonymous">
<link rel="modulepreload" href="/vite/assets/axios-BEvgo0ym.js" as="script" crossorigin="anonymous">
<link rel="modulepreload" href="/vite/assets/classnames-l6ipYlLR.js" as="script" crossorigin="anonymous">
<link rel="modulepreload" href="/vite/assets/dayjs.min-BkKovM-s.js" as="script" crossorigin="anonymous">
<link rel="modulepreload" href="/vite/assets/debounce-jMQ_Cf4f.js" as="script" crossorigin="anonymous">
<link rel="modulepreload" href="/vite/assets/i18next-BlSq9s7B.js" as="script" crossorigin="anonymous">
<link rel="modulepreload" href="/vite/assets/client-U9M77rxp.js" as="script" crossorigin="anonymous">
<link rel="modulepreload" href="/vite/assets/react-dom-DaLxUz_h.js" as="script" crossorigin="anonymous">
<link rel="modulepreload" href="/vite/assets/useTranslation-Bx1Cdrkz.js" as="script" crossorigin="anonymous">
<link rel="modulepreload" href="/vite/assets/compiler-runtime-6XxiPFnt.js" as="script" crossorigin="anonymous">
<link rel="modulepreload" href="/vite/assets/jsx-runtime-CwjcCKJi.js" as="script" crossorigin="anonymous">
<link rel="modulepreload" href="/vite/assets/react-CkL4ZRHB.js" as="script" crossorigin="anonymous">
<script defer src="https://static.cloudflareinsights.com/beacon.min.js/v67327c56f0bb4ef8b305cae61679db8f1769101564043" integrity="sha512-rdcWY47ByXd76cbCFzznIcEaCN71jqkWBBqlwhF1SY7KubdLKZiEGeP7AyieKZlGP9hbY/MhGrwXzJC/HulNyg==" data-cf-beacon='{"version":"2024.11.0","token":"d11015b65d11429ea6b4a2ef37dd7e0b","server_timing":{"name":{"cfCacheStatus":true,"cfEdge":true,"cfExtPri":true,"cfL4":true,"cfOrigin":true,"cfSpeedBrain":true},"location_startswith":null}}' crossorigin="anonymous"></script>
</body>
</html>