Py修饰器笔记

本文涉及的产品
日志服务 SLS,月写入数据量 50GB 1个月
简介: 修饰器的主要功能是,在不改变已有代码的情况下,为某一个类,方法等扩展功能

修饰器

功能

修饰器的主要功能是,在不改变已有代码的情况下,为某一个类,方法等扩展功能

首先看这样一段代码

def foo():
    for i in range(10):
        print(i)
foo()
添加需求 打印日志

现在需要在不改变这段代码前提下,计算出这段代码的运行时间日志

import time


def log(fun):
    print('开始时间:{}'.format(time.time()))
    fun()
    print('结束时间:{}'.format(time.time()))


def foo():
    for i in range(10):
        print(i)


log(foo)
优化调用方式 处理多次调用

我们将foo函数当成一个参数传递给了log(),在def log(fun)函数中,fun()就等同于foo(),好,现在可以显示运行的时间日志了,但是这样就改变了调用方式 需要使用log(foo)来调用,假设代码中使用了100次foo()现在需要打印每个foo()的运行日志,全部这样改是不现实的,我们需要优化一下

import time


def log(fun):
    def run_log():
        print('开始时间:{}'.format(time.time()))
        fun()
        print('结束时间:{}'.format(time.time()))
    
    return run_log

def foo():
    for i in range(10):
        print(i)

foo = log(foo)
foo()

现在我们改造了下代码,foo = log(foo)时,调用log()方法,内部定义了一个run_log()方法,然后返回给了foo,此时foo等于run_log,调用foo相当于调用run_log 这样,即实现了需要打印日志的需求,又可以不用去修改已有代码

传参

现在的foo()方法可以打印0到9数字,但是,由于业务需求变更,现在需要给foo()方法传递一个值,例如foo(100),就需要打印出0-99的数字,同时还要打印日志,所以我们需要再次优化代码

import time


def log(fun):
    def run_log(num):
        print('开始时间:{}'.format(time.time()))
        fun(num)
        print('结束时间:{}'.format(time.time()))

    return run_log


def foo(num):
    for i in range(num):
        print(i)


foo = log(foo)
foo(100)

根据之前的逻辑,我们已经知道新的foo()等于run_log(),所以我们给foo(100)传递100的值时,实际上等同于run_log(100),所以我们直接在def foo(num)中是接受不到的,我们需要在run_log(num)中接受到参数,传给fun(num),这样新的函数就可以接受参数了

返回参数

新的需求又来了,需要在foo()中返回一个所有数字的累加之和,而我们现有的foo函数实际上是run_log,所以我们再来改造一下代码

import time


def log(fun):
    def run_log(num):
        print('开始时间:{}'.format(time.time()))
        info = fun(num)
        print('结束时间:{}'.format(time.time()))
        return info

    return run_log


def foo(num):
    add_num = 0
    for i in range(num):
        add_num += i
        print(i)
    return add_num


foo = log(foo)
x = foo(100)
print(x)

由于现在的foo()等同于run_log(),run_log()中的fun()相当于foo(),所以foo中返回的值传到了info中然后我们把info返回,x就可以接受到从run_log中传递出来的参数

好了,到这里我们就实现了一个阉割版修饰器

通用性

假如需要你再给一个新的函数foo2打印日志, 代码如下

import time


def log(fun):
    def run_log(num):
        print('开始时间:{}'.format(time.time()))
        info = fun(num)
        print('结束时间:{}'.format(time.time()))
        return info

    return run_log


def foo(num):
    add_num = 0
    for i in range(num):
        add_num += i
        print(i)
    return add_num


def foo2(num, num2):
    add_num = 0
    for i in range(num, num2):
        add_num += i
        print(i)
    return add_num


foo = log(foo)
x = foo(100)
print(x)

由于我们的修饰器只能接收一个参数,而foo2需要两个参数,现有代码无法实现,所以我们要继续升级代码

import time


def log(fun):
    def run_log(*args,**kwargs):
        print('开始时间:{}'.format(time.time()))
        info = fun(*args,**kwargs)
        print('结束时间:{}'.format(time.time()))
        return info

    return run_log


def foo(num):
    add_num = 0
    for i in range(num):
        add_num += i
        print(i)
    return add_num


def foo2(num, num2):
    add_num = 0
    for i in range(num, num2):
        add_num += i
        print(i)
    return add_num


foo = log(foo)
foo2 = log(foo2)
x = foo(100)
x2 = foo2(50,100)
print(x)
print(x2)

我们使用了*args,这样就可以接收任意数量的参数,可以满足我们的所有需求

语法糖

但是每次使用前都要写一个 foo = log(foo)这样的赋值操作,代码并不美观 不符合python的代码风格,所以python给提供了一种语法糖,可以用@log来替代,修改后代码如下

import time


def log(fun):
    def run_log(*args,**kwargs):
        print('开始时间:{}'.format(time.time()))
        info = fun(*args,**kwargs)
        print('结束时间:{}'.format(time.time()))
        return info

    return run_log

@log
def foo(num):
    add_num = 0
    for i in range(num):
        add_num += i
        print(i)
    return add_num

@log
def foo2(num, num2):
    add_num = 0
    for i in range(num, num2):
        add_num += i
        print(i)
    return add_num

x = foo(100)
x2 = foo2(50, 100)
print(x)
print(x2)

我们只需要在定义函数时,在上面添加一句@修饰器名就相当于完成了函数名 = 修饰器名(函数名)这样的操作

现在就已经是一个标准的修饰器了

扩展

同理,我们用js也可以写一个修饰器

function log(fun) {    function run_log(...ags) {        console.log('==============')        let info = fun(...ags)        console.log('==============')        return info    }    return run_log}foo = log(foo)function foo(num, num1) {    let x = 0    for (let i = num; i < num1; i++) {        console.log(i)        x += i    }    return x}foo2 = log(foo2)function foo2(num) {    let x = 0    for (let i = 0; i < num; i++) {        console.log(i)        x += i    }    return x}foo(10, 100)foo2(10)
相关实践学习
日志服务之使用Nginx模式采集日志
本文介绍如何通过日志服务控制台创建Nginx模式的Logtail配置快速采集Nginx日志并进行多维度分析。
相关文章
|
存储 监控 API
Python笔记2(函数参数、面向对象、装饰器、高级函数、捕获异常、dir)
Python笔记2(函数参数、面向对象、装饰器、高级函数、捕获异常、dir)
66 0
|
6月前
|
Python
Python 的其他主题:Python 中的 `__init__.py` 文件有什么作用?
Python 的其他主题:Python 中的 `__init__.py` 文件有什么作用?
73 1
|
6月前
|
程序员 测试技术 Python
Python中的装饰器(Decorators) :深入解析与实战应用
Python中的装饰器(Decorators) :深入解析与实战应用
36 0
|
6月前
|
监控 Python
深入解析Python中的装饰器(Decorators)及其实用场景
装饰器是Python中一个强大且灵活的功能,它允许在不修改原有函数或类代码的情况下,动态地为其添加新的功能。本文不仅介绍了装饰器的基本概念、语法和工作原理,还通过具体实例展示了装饰器在日志记录、权限校验、性能分析等实用场景中的应用,旨在帮助读者深入理解装饰器的强大之处,并激发其在实际项目中的创新应用。
|
6月前
|
监控 开发者 Python
Python装饰器与上下文管理器:面试详解
【4月更文挑战第14天】本文介绍了Python中的装饰器和上下文管理器,它们是增强函数和代码块功能的工具,常用于日志、性能监控和资源管理。文章讲解了装饰器和上下文管理器的基础知识,包括语法和使用示例,并列举了面试中常见的问题和易错点,如装饰器作用理解、多层装饰器顺序、上下文管理器与`with`语句的使用。通过理解这些概念和策略,开发者能在面试中更好地展示其编程水平和经验。
49 0
|
6月前
|
测试技术 开发者 Python
Python中的装饰器(Decorators)原理与应用解析
在Python编程中,装饰器(Decorators)是一种强大的工具,能够灵活地扩展函数或类的功能,提高代码的可复用性和可维护性。本文将深入探讨装饰器的原理、使用方法以及常见应用场景,帮助读者更好地理解和运用这一重要的Python特性。
|
6月前
|
存储 缓存 编译器
【conan 入门教程】介绍 conanfile.py中的默认方法的作用
【conan 入门教程】介绍 conanfile.py中的默认方法的作用
444 0
|
6月前
|
测试技术 Python
Python中的装饰器:原理、用法与实例
【2月更文挑战第20天】 本文将深入探讨Python中装饰器的工作原理,使用方法以及实际应用场景。装饰器是Python的一项重要特性,它允许我们在不改变函数源代码的情况下,增加函数的功能。我们将通过实例来详细解析装饰器的使用,帮助读者更好地理解和应用这一强大的工具。
|
存储 Python
Python装饰器2-__call__方法与类装饰器
__call__方法、创建类装饰器、装饰器的应用场景
Python装饰器2-__call__方法与类装饰器
|
测试技术 数据库连接 Python
conftest.py是什么?该怎么用?
conftest.py是什么?该怎么用?
212 0