5 装饰器

python 装饰器

一、函数、类的数据类型

二、装饰器

一、函数、类的数据类型

下面的例子,函数、类也是一种数据类型

from types import MethodType,FunctionType

class A:
    def func(self):
        pass


def fun_test(x):
    return x


if __name__ == '__main__':
    a = A()
    print(type(a.func))  # <class 'method'>  方法类型
    print(type(fun_test))  # <class 'function'> 函数类型
    print(isinstance(a.func, MethodType))  # True
    print(isinstance(fun_test, FunctionType))  # True

二、装饰器

1.什么是装饰器?

装饰器本身也是一个函数或者类,可以新增一些被装饰函数/类的功能,简化代码。

2.装饰器的分类:函数装饰器 、类装饰器---------(被装饰的对象可以是函数,也可以是类)

注意:装饰器来装饰函数或者类,被装饰的函数或类的原功能是不变的,会增加一些新的功能;

           其次,被装饰的函数或者类的调用方式是不会变的

3.装饰器的语法糖

    被装饰的函数/类名 = 装饰器名(被装饰的函数/类名)

4.为什么要写装饰器?

首先来看一个例子:输出结果上添加(装饰)了<>符号

def add(a,b):
    return a + b

if __name__ == '__main__':
    print(f"<{add(3,4)}>")   # <7>

那么,如何能直接调用函数就加上<>符号,不需要手动添加呢?就是使用装饰器,对原来的函数进行装饰。

原始装饰器的实现原理:原函数被新函数调用

def add(a,b):
    return a + b

# 原始的装饰器
def new_add(func,*args,**kwargs):
    return f"<{func(*args,**kwargs)}>"

if __name__ == '__main__':
    print(f"<{add(3,4)}>")   # <7>
    print(new_add(add,3,4))  # <7>

原始装饰器的缺点:不够简单,需要手动传入参数

下面写个更加简单优雅的装饰器实现原理:

from types import FunctionType
# 原函数
def text():
    return "Hello Python!"


def add_fun(func:FunctionType):
    def wrapper():
        return f"<i>{func()}</i>"

    return wrapper

if __name__ == '__main__':
    print(add_fun(text)()) # <i>Hello Python!</i>

加注解符号@,直接调用原函数,就可以实现对原函数的装饰。

from types import FunctionType


def add_fun(func:FunctionType):
    def wrapper():
        return f"<i>{func()}</i>"

    return wrapper

@add_fun
def text():
    return "Hello Python!"



if __name__ == '__main__':
#直接调用原函数
print(text()) # <i>Hello Python!</i>

装饰器的意义:团队合作开发中,一段代码被多人调用,又要增加不同的新需求,每个人都直接在原函数上作改动,是不现实的。用装饰器,既不影响原函数的调用(不修改原函数),又可以在原函数的基础增加新的功能,更加合适。

5.一个函数被多个装饰器 装饰

原理:从下往上

执行:从上往下

from types import FunctionType


def add_fun(func: FunctionType):
    def wrapper():
        return f"<i>{func()}</i>"

    return wrapper


def add_bold(func):
    def wrapper2():
        return f"<b>{func}</b>"

    return wrapper2


@add_fun    # wrapper2() = add_fun() = wrapper()
@add_bold   # text() = add_bold(text)()=wrapper2()
def text():
    return "Hello Python!"


if __name__ == '__main__':
    print(text())  # <i><b><function text at 0x10e0cfb00></b></i>

6.装饰器 的应用

1)函数装饰器---用来装饰函数

①实战1

# 需求:写一个装饰器,能对任意函数进行装饰,功能:记录日志


def log(func):
    def wrapper(*args, **kwargs):
        print(f"{func.__name__}函数正在运行...")
        return func(*args, **kwargs)
    return wrapper

@log
def add(a, b):
    return a + b

if __name__ == '__main__':
    print(add(1,2))
"""
add函数正在运行...
3
"""

②计算函数的运行时间

# 计算函数运行的时间

import time


def caculate_time(func):
    def wrapper(*args, **kwargs):
        start_time = time.time()
        result = func(*args, **kwargs)
        time.sleep(0.5)
        end_time = time.time()
        return end_time - start_time

    return wrapper


@caculate_time
def add(a, b):
    return a + b


if __name__ == '__main__':
    print(add(3, 4))  # 0.5043981075286865

2)类的装饰器---用来装饰类

# 类的装饰器
"""
格式
def 装饰器名(cls):
    装饰代码
    return cls

"""
name = "python"

def add_name(cls):
    cls.name = name
    return cls

@add_name
class A(object):
    pass

if __name__ == '__main__':
    a = A()
    print(a.name)

3)装饰器类

_ _call_ _方法:函数的调用,实际就是调用函数里的_ _call_ _方法

# 这个装饰器是一个类(不是函数)
"""
格式
class 装饰器名(object):
    def __init__(self,func):
        pass
    def __call__(self,*args,**kwargs):   # 调用类的对象时,call会被自动调用执行
        装饰代码段
        return self.func(*args,**kwargs)

"""

class A(object):
    def __init__(self,func):
        self.func = func

    def __call__(self, *args, **kwargs):
        print(f"{self.func.__name__}函数正在被调用...")
        result = self.func(*args, **kwargs)
        return result

@A
def add(a, b):
    return a + b

if __name__ == '__main__':
    print(add(1,2))

4)带参数的装饰器

# 带参数的装饰器-三层函数(最外层用来传参)---装饰器里面可以传参数
# @log(filename = "123.txt")

import logging

def add_log(filename):       # 最外层:接收装饰参数
    def inner(func):         # 中间层:接收被装饰函数
        def wrapper(*args, **kwargs):  # 内层装饰函数:接收被装饰的函数的参数
            logging.warning(f"{func.__name__}函数正在被调用...")
            result = func(*args, **kwargs)  # 调用被装饰函数
            return result
        return wrapper
    return inner


@add_log(filename="123.txt")
def add(a, b):
    return a + b

if __name__ == '__main__':
    print(add(3,4))
"""
WARNING:root:add函数正在被调用...
7
"""
原文地址:https://www.cnblogs.com/ananmy/p/14062262.html