Занятие №18:
Декораторы и методы
Основные понятия декораторов
Декоратор – это функция, расширяющая возможности другой функции или метода класса.
Определение
Декоратор – это функция, которая принимает функцию и возвращает функцию. И ничего более.
def decorator(func):
return func
# @decorator над данной функцией означает,
# что мы передаем её в декоратор
@decorator
def decorate_example():
print('Привет Вселенная!')
decorate_example()
# Другое объявление декоратора
# Cоздание переменной и присвоение ей декоратора с передаваемой функцией
decorate_example = decorator(decorate_example)
decorate_example()
# Оба объявления правильные,
# но объявление с собакой более распространено и удобно
Пример простейшего декоратора
Привет Вселенная!
Привет Вселенная!
def decorator(func):
def new_func():
# декоратор заменяет передаваемую нами функцию
# на свою и возвращает её
print('Казнить нельзя, помиловать!')
return new_func
@decorator
def decorate_example():
print('Казнить, нельзя помиловать.')
decorate_example()
Более сложный декоратор
Казнить нельзя, помиловать!
# Данная функция имеет тип 'класс', так как в питоне все состоит из объектов
print(decorate_example.__class__)
# Из-за того, что с помощью декоратора мы подменили функцию, её имя поменялось
# Теперь она называется new_func
print(decorate_example.__name__)
<class 'fucntion'>
new_func
Информация о декораторе
def decorator(func):
def wrapper_function(str):
func(str)
return wrapper_function
# Аргумент name передается в wrapper_function декоратора и
# происходит вызов объекта func, который представляет из себя
# функцию greet с аргументом name.
@decorator
def greet(name):
print(f"Привет {name}!")
print("Кому хотите передать привет?")
greet("Анастасия")
Кому хотите передать привет?
Привет Анастасия!
Аргументы
# *args - произвольное число позиционных аргументов.
# При вызове функции, на место этого
# параметра передается список аргументов, заключенных в кортеж
# **kwargs - произвольное число именованных аргументов. При вызове функции,
# на его место передается список именованных аргументов заключенных в словарь,
# кроме тех, имена которых были определены ранее
def decorator(func):
def wrapper_function(*args, **kwargs):
#print(*args, **kwargs)
func(*args, **kwargs)
print(*args, **kwargs)
return wrapper_function
@decorator
def greet(name):
print(f"Привет {name}!")
greet("Анастасия")
Привет Анастасия!
Анастасия
Аргументы
def logger(func):
# def wrapper_function(*args, **kwargs):
def wrapper_function(list_of_num):
# result = func(*args, **kwargs)
result = func(list_of_num)
with open('log.txt', 'w') as f:
f.write(str(result))
return result
return wrapper_function
Пример декоратора
@logger
def summator(list_of_num):
return sum(list_of_num)
summator([1,2,3,4])
10
Пример декоратора
# Можем передать название файла,
# куда будет происходить запись результата суммы
def logger(filename):
def decorator(func):
def wrapped(*args, **kwargs):
result = func(*args, **kwargs)
with open(filename, 'w') as f:
f.write(str(result))
return result
return wrapped
return decorator
Декоратор с параметром
# Декоратор logger должен принимать имя файла и возвращать декоратор,
# который принимает функцию и подменяет её функцией wrapped, как мы делали до этого
@logger('file_log.txt')
def summator(list_of_num):
return sum(list_of_num)
summator([1,2,3,4])
10
Декоратор с параметром
import functools
import math
def debug(func):
@functools.wraps(func)
def wrapper_debug(*args, **kwargs):
args_repr = [str(a) for a in args]
kwargs_repr = [f"{k}={v}" for k, v in kwargs.items()]
signature = ", ".join(args_repr + kwargs_repr)
print(f"Вызываем функцию {func.__name__}({signature})")
value = func(*args, **kwargs)
print(f"Функцию {func.__name__} вернула значение {value}")
return value
return wrapper_debug
Пример декоратора "Дебагер"
Пример декоратора "Дебагер"
debug_factorial = debug(math.factorial)
def show_debug_function(terms=5):
return [debug_factorial(n) for n in range(terms+1)]
show_debug_function(7)
Вызываем функцию factorial(0)
Функцию factorial вернула значение 1
Вызываем функцию factorial(1)
Функцию factorial вернула значение 1
Вызываем функцию factorial(2)
Функцию factorial вернула значение 2
Вызываем функцию factorial(3)
Функцию factorial вернула значение 6
Вызываем функцию factorial(4)
Функцию factorial вернула значение 24
Вызываем функцию factorial(5)
Функцию factorial вернула значение 120
Вызываем функцию factorial(6)
Функцию factorial вернула значение 720
Вызываем функцию factorial(7)
Функцию factorial вернула значение 5040
[1, 1, 2, 6, 24, 120, 720, 5040]
Различные виды методов
@staticmethod – декоратор, который позволяет вызывать метод напрямую через имя класса.
@classmethod – декоратор, который позволяет дополнить метод, который получает класс в качестве первого аргумента.
@property – декоратор, который позволяет работать с методом класса как с атрибутом.
Определение
Декоратор @staticmethod
class Planet:
@staticmethod
def is_big_planet(diameter):
if diameter > 10000:
return True
else:
return False
Planet.is_big_planet(100)
False
Декоратор @staticmethod
Статические методы имеют доступ только к атрибутам классов, к ним нельзя обратиться через self. По сути, статические методы ничего не знают ни о классе, ни об экземпляре, на который вызываются.
Декоратор @staticmethod
class SummatorClass:
@staticmethod
def sum_1(a,b):
print(a+b)
def sum_2(self,a,b):
print(a+b)
def sum_3(self,a,b):
return SummatorClass.sum_1(a,b)
SummatorClass.sum_1(5,10)
sum_num = SummatorClass()
sum_num.sum_2(10, 15)
sum_num.sum_1(20, 25)
sum_num.sum_3(30, 35)
15
25
45
65
Декоратор @classmethod
@classmethod используется, когда вам нужно получить методы, не относящиеся к какому-либо конкретному экземпляру, но тем не менее, каким-то образом привязанные к классу.
Декоратор @classmethod
class MyClass:
counts = 0
def __init__(self):
MyClass.counts = MyClass.counts + 1
@classmethod
def classmethod(cls):
print(cls.counts)
MyClass.classmethod()
mc1 = MyClass()
mc2 = MyClass()
mc3 = MyClass()
MyClass.classmethod()
0
3
Декоратор @property
@property позволяет работать с методом некоторого класса как с атрибутом.
Декоратор @property
class SummatorClass:
def __init__(self,a,b):
self.a = a
self.b = b
@property
def sum_two_num(self):
print(f"Сумма двух чисел равна {self.a + self.b}")
sum_cls = SummatorClass(1,2)
sum_cls.sum_two_num
Сумма двух чисел равна 3
Декоратор @property
class SpaceShip:
def __init__(self,weight):
self._weight = weight
weight = property()
#геттер
@weight.getter
def weight(self):
return self._weight
#сеттер
@weight.setter
def weight(self, value):
if value <= 100:
self._weight = 100
elif value > 5000:
self._weight = 5000
else:
self._weight = value
Декоратор @property
space_ship_1 = SpaceShip(2000)
print(f"Вес космического корабля составляет {space_ship_1.weight} тонн.")
space_ship_1.weight = -20
print(f"Вес космического корабля составляет {space_ship_1.weight} тонн.")
Вес космического корабля составляет 2000 тонн.
Вес космического корабля составляет 100 тонн.
Что, когда использовать?
@staticmethod помогает в достижении инкапсуляции в классе, поскольку он не знает о состоянии текущего экземпляра. Используем, если метод не выполняет какие-либо операции с другими частями класса.
@classmethod используется, когда нужно получить методы, не относящиеся к какому-либо конкретному экземпляру, но тем не менее, каким-то образом привязанные к классу.
@property позволяет работать с методом некоторого класса как с атрибутом. Используем, чтобы отделить свойства класса от методов, позволив обращаться к "вычисляемым" свойствам не как к функциям, а как к атрибутам.
Спасибо за понимание!
Лекция №18. Декораторы и методы
By Alexey Baigashov
Лекция №18. Декораторы и методы
- 114