Занятие №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