Декораторы и методы
Декоратор – это функция, расширяющая возможности другой функции или метода класса.
Декоратор – это функция, которая принимает функцию и возвращает функцию. И ничего более.
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 – декоратор, который позволяет работать с методом класса как с атрибутом.
class Planet:
@staticmethod
def is_big_planet(diameter):
if diameter > 10000:
return True
else:
return False
Planet.is_big_planet(100)
False
Статические методы имеют доступ только к атрибутам классов, к ним нельзя обратиться через self. По сути, статические методы ничего не знают ни о классе, ни об экземпляре, на который вызываются.
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 используется, когда вам нужно получить методы, не относящиеся к какому-либо конкретному экземпляру, но тем не менее, каким-то образом привязанные к классу.
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 позволяет работать с методом некоторого класса как с атрибутом.
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
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
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 позволяет работать с методом некоторого класса как с атрибутом. Используем, чтобы отделить свойства класса от методов, позволив обращаться к "вычисляемым" свойствам не как к функциям, а как к атрибутам.