IT数码 购物 网址 头条 软件 日历 阅读 图书馆
TxT小说阅读器
↓语音阅读,小说下载,古典文学↓
图片批量下载器
↓批量下载图片,美女图库↓
图片自动播放器
↓图片自动播放器↓
一键清除垃圾
↓轻轻一点,清除系统垃圾↓
开发: C++知识库 Java知识库 JavaScript Python PHP知识库 人工智能 区块链 大数据 移动开发 嵌入式 开发工具 数据结构与算法 开发测试 游戏开发 网络协议 系统运维
教程: HTML教程 CSS教程 JavaScript教程 Go语言教程 JQuery教程 VUE教程 VUE3教程 Bootstrap教程 SQL数据库教程 C语言教程 C++教程 Java教程 Python教程 Python3教程 C#教程
数码: 电脑 笔记本 显卡 显示器 固态硬盘 硬盘 耳机 手机 iphone vivo oppo 小米 华为 单反 装机 图拉丁
 
   -> Python知识库 -> python 装饰器@ -> 正文阅读

[Python知识库]python 装饰器@

装饰器概述

装饰器就是在特定条件下为某些函数在不改动函数体的时候为函数新添加一些功能,可以对原函数进行功能扩展,而且还不需要修改原函数的内容,也不需要修改原函数的调用,装饰器的使用符合了面向对象编程的开放封闭原则,即对扩展开放、对修改封闭。

装饰器基于@语法和函数闭包,将原函数封装在闭包中,然后将函数赋值为一个新的函数(内置函数),执行函数时再在内层函数中执行闭包中的原函数。

装饰器使用

定义一个函数add,实现了两个数的相加。

def add():
    x = 1
    y = 2
    z = x + y
    print(z)

如果需要计算这些代码的执行时间,应该怎么办呢?一个可行的方法就是在函数体内部加上计算执行时间的代码。

import time


def add():
    start = time.time()
    x = 1
    y = 2
    z = x + y
    print(z)
    end = time.time()
    print("total time:" + str(end - start))

add()

输出结果为:

3
total time:0.0

这样已经违背了开闭原则,如果不想在修改add函数的基础上,来计算该函数的运行时间,又该怎么办呢?我们知道,函数的参数传递的值可以为一个函数,基于这个特性,将源代码修改如下:

import time


def add():
    x = 1
    y = 2
    z = x + y
    print(z)


def count_time(function):
    def wrap():
        start = time.time()
        function()
        end = time.time()
        print("total time:" + str(end - start))

    return wrap


add = count_time(add)
add()

输出结果为:

3
total time:0.0

首先向count_time函数传递一个参数,这个参数的值是一个add函数;然后该count_time函数的返回值是一个wrap函数,这个wrap函数的函数体内包含了add函数,并在此基础上增加了计算时间的代码。这样,我们就在不修改add函数的基础上,实现了计算函数add的运行时间的功能。

但是这样又出现了一个问题,我们每次在调用add函数前,都需要调用一次count_time函数。这时候应该如何解决呢?

如果看过其他python项目里面的代码里,难免会看到@符号,这个@符号就是装饰器的语法糖。因此上面简单的装饰器还是可以通过语法糖来实现的,这样就可以省去add = count_time(add)这一行代码。

import time


def count_time(function):
    def wrap():
        start = time.time()
        function()
        end = time.time()
        print("total time:" + str(end - start))

    return wrap


@count_time
def add():
    x = 1
    y = 2
    z = x + y
    print(z)


add()

输出结果为:

3
total time:0.0

上面我们用函数来实现了一个装饰器,如果用类来实现一个同样功能的装饰器应该怎么做呢?可以通过类的_ _ init _ _ 方法来传入一个add函数,然后通过 _ _ call _ _ 函数来添加相应的功能。

import time


class CountTime:
    def __init__(self, function):
        self.function = function

    def __call__(self, *args, **kwargs):
        start = time.time()
        self.function(*args, **kwargs)
        end = time.time()
        print("total time:" + str(end - start))


@CountTime
def add():
    x = 1
    y = 2
    z = x + y
    print(z)


add()

输出结果为:

3
total time:0.0

装饰器传参

上面实现了一个简单的装饰器,这个装饰器其实就是一个函数,既然装饰器是一个函数,那么肯定就可以有参数。如何实现一个带有参数的装饰器呢?为了实现传参,我们可以在原函数的基础上,再加入一层函数。

def count_time_args(arg1):
    def count_time(function):
        def wrap():
            start = time.time()
            function()
            end = time.time()
            print("total time:" + str(end - start))
            print(arg1)
        return wrap
    return count_time


@count_time_args(arg1="end")
def add():
    x = 1
    y = 2
    z = x + y


add()

输出结果为:

3
total time:0.0
end

既然函数形式的装饰器能传参,那么类装饰器肯定也能传参,如何向类装饰器传入参数呢?如果带有参数的化,我们不能像函数那么在函数的外面再包装一层,而是将装饰器的参数传入_ _ init _ _ (),而将函数传入_ _ call _ _()。

import time


class CountTime:
    def __init__(self, arg1):
        self.arg1 = arg1

    def __call__(self, function, *args, **kwargs):
        def wrap():
            start = time.time()
            function(*args, **kwargs)
            end = time.time()
            print("total time:" + str(end - start))
            print(self.arg1)
        return wrap


@CountTime(arg1="end")
def add():
    x = 1
    y = 2
    z = x + y
    print(z)


add()

输出结果为:

3
total time:0.0
end

装饰器顺序

如果一个函数同时被多个装饰器所修饰时,这些装饰器的执行顺序是什么样的呢?

def decorator_1(func):
    def wrapper(*args, **kwargs):
        func(*args, **kwargs)
        print('我是装饰器1')

    return wrapper


def decorator_2(func):
    def wrapper(*args, **kwargs):
        func(*args, **kwargs)
        print('我是装饰器2')

    return wrapper


def decorator_3(func):
    def wrapper(*args, **kwargs):
        func(*args, **kwargs)
        print('我是装饰器3')

    return wrapper


@decorator_1
@decorator_2
@decorator_3
def main():
    print("this is a text")


main()

结果为

this is a text
我是装饰器3
我是装饰器2
我是装饰器1

从结果中可以看出,如果一个函数被多个装饰器修饰,那么多个装饰器会按照由内向外的顺序生效。

  Python知识库 最新文章
Python中String模块
【Python】 14-CVS文件操作
python的panda库读写文件
使用Nordic的nrf52840实现蓝牙DFU过程
【Python学习记录】numpy数组用法整理
Python学习笔记
python字符串和列表
python如何从txt文件中解析出有效的数据
Python编程从入门到实践自学/3.1-3.2
python变量
上一篇文章      下一篇文章      查看所有文章
加:2022-07-21 21:30:58  更:2022-07-21 21:32:07 
 
开发: C++知识库 Java知识库 JavaScript Python PHP知识库 人工智能 区块链 大数据 移动开发 嵌入式 开发工具 数据结构与算法 开发测试 游戏开发 网络协议 系统运维
教程: HTML教程 CSS教程 JavaScript教程 Go语言教程 JQuery教程 VUE教程 VUE3教程 Bootstrap教程 SQL数据库教程 C语言教程 C++教程 Java教程 Python教程 Python3教程 C#教程
数码: 电脑 笔记本 显卡 显示器 固态硬盘 硬盘 耳机 手机 iphone vivo oppo 小米 华为 单反 装机 图拉丁

360图书馆 购物 三丰科技 阅读网 日历 万年历 2024年11日历 -2024/11/15 12:13:23-

图片自动播放器
↓图片自动播放器↓
TxT小说阅读器
↓语音阅读,小说下载,古典文学↓
一键清除垃圾
↓轻轻一点,清除系统垃圾↓
图片批量下载器
↓批量下载图片,美女图库↓
  网站联系: qq:121756557 email:121756557@qq.com  IT数码