0 added
0 removed
Original
2026-01-01
Modified
2026-03-10
1
<p>Эта статья посвящена теме декораторов в Python. Поговорим о том, что это такое, уделим особое внимание свойствам функций в Python, на базе которых реализована данная идея, а также рассмотрим декораторы, которые принимают аргументы и возвращают значение из функции.</p>
1
<p>Эта статья посвящена теме декораторов в Python. Поговорим о том, что это такое, уделим особое внимание свойствам функций в Python, на базе которых реализована данная идея, а также рассмотрим декораторы, которые принимают аргументы и возвращают значение из функции.</p>
2
<h2>Что надо знать о методах и функциях в Python?</h2>
2
<h2>Что надо знать о методах и функциях в Python?</h2>
3
<p>Говоря о функциях в Python, нужно упомянуть два аспекта: 1) функция в Python - есть объект специального вида, который можно передавать в виде аргумента другим функциям; 2) внутри функций в Python вы можете создавать другие функции, а также вызывать их, возвращая результат посредством return.</p>
3
<p>Говоря о функциях в Python, нужно упомянуть два аспекта: 1) функция в Python - есть объект специального вида, который можно передавать в виде аргумента другим функциям; 2) внутри функций в Python вы можете создавать другие функции, а также вызывать их, возвращая результат посредством return.</p>
4
<p>Теперь давайте поговорим об этом подробнее.</p>
4
<p>Теперь давайте поговорим об этом подробнее.</p>
5
<h2>Функция как объект в Python</h2>
5
<h2>Функция как объект в Python</h2>
6
<p>В языке программирования Python часто практикуется передача одной функции в виде аргумента другой функции. Представьте, что есть список целых чисел, и вы желаете на его базе получить другой список с элементами, которые будут квадратами первого списка. Вот, как это можно реализовать в Python:</p>
6
<p>В языке программирования Python часто практикуется передача одной функции в виде аргумента другой функции. Представьте, что есть список целых чисел, и вы желаете на его базе получить другой список с элементами, которые будут квадратами первого списка. Вот, как это можно реализовать в Python:</p>
7
>>> # исходный список >>> a = [1, 2, 3, 4, 5] >>> # функция, которая возводит в квадрат переданное ей число >>> sq = lambda x: x**2 >>> # проверка работы функции в Python >>> print(sq(5)) 25 >>> # получение списка квадратов >>> b = list(map(sq, a)) >>> print(b) [1, 4, 9, 16, 25]<p>В нашем примере мы передали функции<strong>map</strong>в виде первого аргумента функцию<strong>sq</strong>. Последняя будет по очереди применяться ко всем элементам нашего списка<strong>a</strong>.</p>
7
>>> # исходный список >>> a = [1, 2, 3, 4, 5] >>> # функция, которая возводит в квадрат переданное ей число >>> sq = lambda x: x**2 >>> # проверка работы функции в Python >>> print(sq(5)) 25 >>> # получение списка квадратов >>> b = list(map(sq, a)) >>> print(b) [1, 4, 9, 16, 25]<p>В нашем примере мы передали функции<strong>map</strong>в виде первого аргумента функцию<strong>sq</strong>. Последняя будет по очереди применяться ко всем элементам нашего списка<strong>a</strong>.</p>
8
<p>Кроме того, в Python функция является специальным объектом, имеющим метод __call__(). Представьте, что мы создали следующий класс:</p>
8
<p>Кроме того, в Python функция является специальным объектом, имеющим метод __call__(). Представьте, что мы создали следующий класс:</p>
9
class DemoCall(): def __call__(self): return "Hello!"<p>Объект такого класса в Python мы сможем вызывать как функцию:</p>
9
class DemoCall(): def __call__(self): return "Hello!"<p>Объект такого класса в Python мы сможем вызывать как функцию:</p>
10
>>> hello = DemoCall() >>> hello() 'Hello!'<h2>Функция внутри функции в Python</h2>
10
>>> hello = DemoCall() >>> hello() 'Hello!'<h2>Функция внутри функции в Python</h2>
11
<p>Функции в Python мы можем создавать, вызывать и возвращать из других функций. Кстати, на этом основана идея замыкания (closures) в Python.</p>
11
<p>Функции в Python мы можем создавать, вызывать и возвращать из других функций. Кстати, на этом основана идея замыкания (closures) в Python.</p>
12
<p>Давайте создадим функцию, умножающую 2 числа:</p>
12
<p>Давайте создадим функцию, умножающую 2 числа:</p>
13
def mul(a): def helper(b): return a * b return helper<p>В этой функции в Python реализованы два важных свойства: 1) внутри функции mul() мы создаём ещё одну функцию helper(); 2) функция mul() возвращает нам функцию helper() в качестве результата работы.</p>
13
def mul(a): def helper(b): return a * b return helper<p>В этой функции в Python реализованы два важных свойства: 1) внутри функции mul() мы создаём ещё одну функцию helper(); 2) функция mul() возвращает нам функцию helper() в качестве результата работы.</p>
14
<p>Вызов этой функции в Python:</p>
14
<p>Вызов этой функции в Python:</p>
15
<p>Особенность заключается в том, что мы можем создавать на базе функции mul() собственные кастомизированные функции. Давайте создадим функцию в Python, умножающую на 3:</p>
15
<p>Особенность заключается в том, что мы можем создавать на базе функции mul() собственные кастомизированные функции. Давайте создадим функцию в Python, умножающую на 3:</p>
16
>>>three_mul = mul(3) >>>three_mul(5) 15<p>В результате была построена функция three_mul(), умножающая на 3 любое переданное ей число.</p>
16
>>>three_mul = mul(3) >>>three_mul(5) 15<p>В результате была построена функция three_mul(), умножающая на 3 любое переданное ей число.</p>
17
<h2>Декоратор функции в Python</h2>
17
<h2>Декоратор функции в Python</h2>
18
<p>Конструктивно речь идёт о некоторой функции, в качестве аргумента которого выступает другая функция. Декоратор в Python добавляет дополнительный функционал к функции, не меняя её содержимое.</p>
18
<p>Конструктивно речь идёт о некоторой функции, в качестве аргумента которого выступает другая функция. Декоратор в Python добавляет дополнительный функционал к функции, не меняя её содержимое.</p>
19
<h3>Создание</h3>
19
<h3>Создание</h3>
20
<p>Представьте, что мы имеем пару простых функций в Python:</p>
20
<p>Представьте, что мы имеем пару простых функций в Python:</p>
21
def first_test(): print("Test function 1") def second_test(): print("Test function 2")<p>При этом мы желаем их дополнить таким образом, чтобы перед вызовом основного кода нашей функции печаталась строчка “Start function”, а в конце - строка “Stop function”.</p>
21
def first_test(): print("Test function 1") def second_test(): print("Test function 2")<p>При этом мы желаем их дополнить таким образом, чтобы перед вызовом основного кода нашей функции печаталась строчка “Start function”, а в конце - строка “Stop function”.</p>
22
<p>Реализовать поставленную задачу можно несколькими методами. Во-первых, мы можем добавить необходимые строки в конец и начало каждой функции. Но вряд ли это удобно, ведь если мы пожелаем их убрать, придётся модифицировать тело функции.</p>
22
<p>Реализовать поставленную задачу можно несколькими методами. Во-первых, мы можем добавить необходимые строки в конец и начало каждой функции. Но вряд ли это удобно, ведь если мы пожелаем их убрать, придётся модифицировать тело функции.</p>
23
<p>Теперь поговорим о втором пути. Для начала создадим функцию:</p>
23
<p>Теперь поговорим о втором пути. Для начала создадим функцию:</p>
24
def simple_decore(fn): def wrapper(): print("Start function") fn() print("Stop function") return wrapper<p>Теперь нужно обернуть функции в оболочку:</p>
24
def simple_decore(fn): def wrapper(): print("Start function") fn() print("Stop function") return wrapper<p>Теперь нужно обернуть функции в оболочку:</p>
25
first_test_wrapped = simple_decore(first_test) second_test_wrapped = simple_decore(second_test)<p>Обратите внимание, что функции<strong>first_test</strong>и<strong>second_test</strong>не поменялись.</p>
25
first_test_wrapped = simple_decore(first_test) second_test_wrapped = simple_decore(second_test)<p>Обратите внимание, что функции<strong>first_test</strong>и<strong>second_test</strong>не поменялись.</p>
26
>>> first_test() Test function 1 >>> second_test() Test function 2<p>Наши функции<strong>second_test_wrapped</strong>и<strong>first_test_wrapped</strong>обладают функционалом, который нам и нужен.</p>
26
>>> first_test() Test function 1 >>> second_test() Test function 2<p>Наши функции<strong>second_test_wrapped</strong>и<strong>first_test_wrapped</strong>обладают функционалом, который нам и нужен.</p>
27
>>> first_test_wrapped() Start function Test function 1 Stop function >>> first_test_wrapped() Start function Test function 1 Stop function<p>Теперь, если надо, чтобы так работали функции с именами<strong>first_test</strong>и<strong>second_test</strong>, делаем следующее:</p>
27
>>> first_test_wrapped() Start function Test function 1 Stop function >>> first_test_wrapped() Start function Test function 1 Stop function<p>Теперь, если надо, чтобы так работали функции с именами<strong>first_test</strong>и<strong>second_test</strong>, делаем следующее:</p>
28
first_test = first_test_wrapped second_test = second_test_wrapped<p>Проверяем:</p>
28
first_test = first_test_wrapped second_test = second_test_wrapped<p>Проверяем:</p>
29
>>> first_test() Start function Test function 1 Stop function >>> second_test() Start function Test function 2 Stop function<p>Выполненные нами действия и являются реализацией идеи декоратора.</p>
29
>>> first_test() Start function Test function 1 Stop function >>> second_test() Start function Test function 2 Stop function<p>Выполненные нами действия и являются реализацией идеи декоратора.</p>
30
<p>Правда, вместо строк:</p>
30
<p>Правда, вместо строк:</p>
31
def first_test(): print("Test function 1") first_test_wrapped = simple_decore(first_test) first_test = first_test_wrapped<p>мы можем написать иначе:</p>
31
def first_test(): print("Test function 1") first_test_wrapped = simple_decore(first_test) first_test = first_test_wrapped<p>мы можем написать иначе:</p>
32
@simple_decore def first_test(): print("Test function 1")<p>В нашем случае @simple_decore - это не что иное, как декоратор функции.</p>
32
@simple_decore def first_test(): print("Test function 1")<p>В нашем случае @simple_decore - это не что иное, как декоратор функции.</p>
33
<h3>Передаём аргументы в функцию с помощью декоратора</h3>
33
<h3>Передаём аргументы в функцию с помощью декоратора</h3>
34
<p>Бывает, что функция требует наличие аргумента, поэтому мы можем передать его через декоратор. Давайте создадим декоратор, принимающий аргумент и выводящий информацию о декорируемой нами функции и её аргументе.</p>
34
<p>Бывает, что функция требует наличие аргумента, поэтому мы можем передать его через декоратор. Давайте создадим декоратор, принимающий аргумент и выводящий информацию о декорируемой нами функции и её аргументе.</p>
35
def param_transfer(fn): def wrapper(arg): print("Start function: " + str(fn.__name__) + "(), with param: " + str(arg)) fn(arg) return wrapper<p>Чтобы продемонстрировать работу, создадим функцию, выводящую квадратный корень переданного ей числа, а в качестве декоратора, укажем созданный param_transfer:</p>
35
def param_transfer(fn): def wrapper(arg): print("Start function: " + str(fn.__name__) + "(), with param: " + str(arg)) fn(arg) return wrapper<p>Чтобы продемонстрировать работу, создадим функцию, выводящую квадратный корень переданного ей числа, а в качестве декоратора, укажем созданный param_transfer:</p>
36
@param_transfer def print_sqrt(num): print(num**0.5)<p>Теперь давайте выполним данную функцию с аргументом 4:</p>
36
@param_transfer def print_sqrt(num): print(num**0.5)<p>Теперь давайте выполним данную функцию с аргументом 4:</p>
37
>>> print_sqrt(4) Start function: print_sqrt(), with param: 4 2.0<h3>Декораторы для методов класса в Python</h3>
37
>>> print_sqrt(4) Start function: print_sqrt(), with param: 4 2.0<h3>Декораторы для методов класса в Python</h3>
38
<p>С декоратором можно объявлять и методы классов. Давайте выполним модификацию декоратора param_transfer:</p>
38
<p>С декоратором можно объявлять и методы классов. Давайте выполним модификацию декоратора param_transfer:</p>
39
def method_decor(fn): def wrapper(self): print("Start method: " + str(fn.__name__)) fn(self) return wrapper<p>Теперь приступим к созданию класса для представления 2-мерного вектора (математического). В классе определим метод norm(), выводящий модуль вектора.</p>
39
def method_decor(fn): def wrapper(self): print("Start method: " + str(fn.__name__)) fn(self) return wrapper<p>Теперь приступим к созданию класса для представления 2-мерного вектора (математического). В классе определим метод norm(), выводящий модуль вектора.</p>
40
class Vector(): def __init__(self, px = 0, py = 0): self.px = px self.py = py @method_decor def norm(self): print((self.px**2 + self.py**2)**0.5)<p>Что же, осталось продемонстрировать работу нашего метода:</p>
40
class Vector(): def __init__(self, px = 0, py = 0): self.px = px self.py = py @method_decor def norm(self): print((self.px**2 + self.py**2)**0.5)<p>Что же, осталось продемонстрировать работу нашего метода:</p>
41
>>> vc = Vector(px=10, py=5) >>> vc.norm() Start method: norm 11.180339887498949<h3>Возвращаем результат работы функции через декоратор</h3>
41
>>> vc = Vector(px=10, py=5) >>> vc.norm() Start method: norm 11.180339887498949<h3>Возвращаем результат работы функции через декоратор</h3>
42
<p>Зачастую создаваемые функции выполняют возвращение какого-либо значения. Чтобы это стало возможным осуществить через декоратор, нужно специальным образом построить нашу внутреннюю функцию:</p>
42
<p>Зачастую создаваемые функции выполняют возвращение какого-либо значения. Чтобы это стало возможным осуществить через декоратор, нужно специальным образом построить нашу внутреннюю функцию:</p>
43
def decor_with_return(fn): def wrapper(*args, **kwargs): print("Start method: " + str(fn.__name__)) return fn(*args, **kwargs) return wrapper<p>Возможно применение этого декоратора и для оборачивания функций, принимающих различные аргументы и возвращающие значение:</p>
43
def decor_with_return(fn): def wrapper(*args, **kwargs): print("Start method: " + str(fn.__name__)) return fn(*args, **kwargs) return wrapper<p>Возможно применение этого декоратора и для оборачивания функций, принимающих различные аргументы и возвращающие значение:</p>
44
@decor_with_return def calc_sqrt(val): return val**0.5<p>Осталось выполнить функцию calc_sqrt() с параметром 16:</p>
44
@decor_with_return def calc_sqrt(val): return val**0.5<p>Осталось выполнить функцию calc_sqrt() с параметром 16:</p>
45
>>> tmp = calc_sqrt(16) Start method: calc_sqrt >>> print(tmp)
45
>>> tmp = calc_sqrt(16) Start method: calc_sqrt >>> print(tmp)