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装饰器@详解

Python函数修饰符@的作用是为现有的函数增加额外的功能;其作用非常强大,今天我们就来谈谈它是如何在日志记录中起到很好的作用的。
先看一个例子:

import datetime

__DEBUG__ = True

def log(func):
    if __DEBUG__:
        print('函数开始于',datetime.datetime.now())
        
    func()
    
    if __DEBUG__:
        print('函数结束于',datetime.datetime.now())

def test():
    
    print('test')
    test_lst = []
    for i in range(100):
        test_lst.append(i)

log(test)

log() 是一个日志函数,用于记录函数运行的开始时间,和结束时间。
可是这段代码有个弊端,在测试某个函数时,我们需要添加类似于log(test)这样的代码,不需要后把它删除;这样做很麻烦。
还好我们有@函数装饰符。修改前文中的代码,移除 log(text).
在test函数的上方加入@log装饰器。

@log
def test():
    pass

这段代码和前文中的代码功能是一样的。
可是问题又来了;运行上述代码,发现我们没有调用 test 函数,test就已经被执行了,因为 @log 等于 log(test),有什么方法让它只有在我们调用的时候运行呢?修改代码:

import datetime

__DEBUG__ = True

def log(func):
    def wrapper():
        if __DEBUG__:
            print('函数开始于',datetime.datetime.now())
        
        func()
    
        if __DEBUG__:
            print('函数结束于',datetime.datetime.now())
    return wrapper

@log
def test():
    
    print('test')
    test_lst = []
    for i in range(100):
        test_lst.append(i)

在log函数中定义了一个wrapper函数对原功能进行包装,并将这个函数返回就可以了
在不使用装饰器的情况下,你需要log(test)()才能让函数运行;在使用@log装饰器后,直接调用test()即可获取函数运行日志,我们可以为很多需要测试的函数添加@log装饰器,不需要用这个功能直接将__DEBUG__设置成 False。
它的执行顺序是这样的:在为函数添加了@log装饰器后,log函数就被执行,而使用了@log装饰器的 test 函数被作为log函数的传入值使用,在log函数中,对test函数进行了一个简单的包装,并将包装好的函数返回,也就是log函数中的wrapper函数;此时test 函数实际变成了 wrapper函数

如何给函数添加参数

前文中的 test 函数是没有使用参数的,如果你要定义和使用参数,应该这样修改:

def test(a,b):
    
    print('test')
    test_lst = []
    for i in range(a):
        test_lst.append(i*b)
test(1,2)

包装函数:

 def wrapper(*args,**kwargs):
        if __DEBUG__:
            print('函数开始于',datetime.datetime.now())
        
        func(*args,**kwargs)
    
        if __DEBUG__:
            print('函数结束于',datetime.datetime.now())

*args是将参数以元组的形式打包成tuple给函数体调用;
**kwargs是将关键字参数打包成dict给函数体调用;
为@log装饰器添加参数
有这样一种场景:

fp = open('out.log','a')
@log(level=1,file=fp)
@log(1,fp)

怎么能让装饰器拥有参数呢?

import sys

fp = open('out.log','a')

def log(level=1,file=sys.stdout):
    def _log(func):
        def wrapper(*args,**kwargs):
            if __DEBUG__:
                print('函数开始于',datetime.datetime.now(),file=file,flush=True)
        
            func(*args,**kwargs)
    
            if __DEBUG__:
                print('函数结束于',datetime.datetime.now(),file=file,flush=True)
        return wrapper
    return _log
@log(file=fp)

现在你可以指定日志的输出文件了
为@log装饰器添加方法
又有这样的场景:
@log.sendto(‘127.0.0.1:9009’.timeout=30)
你可以将 .sendto 方法理解为将日志发送到指定服务器;
那么log函数就开始有点虚脱了,虽然有方法在log 函数中实现这一目的,但是比较复杂,有一种更简单的方法,就是将log函数转换成类:

class _log:
    def __call__(self,level=1,file=sys.stdout):
        def _log_wrapper(func):
            def wrapper(*args,**kwargs):
                if __DEBUG__:
                    print('函数开始于',datetime.datetime.now(),file=file,flush=True)
        
                func(*args,**kwargs)
    
                if __DEBUG__:
                    print('函数结束于',datetime.datetime.now(),file=file,flush=True)
            return wrapper
        return _log_wrapper

使用前别忘记实例化,log=_log()
现在只需要在 _log类中定义方法,即可向@log装饰器添加方法:
def sendto(self,addr,timeout=20):
pass
完整源码:

 import datetime
import sys
__DEBUG__ = True


class _log:
    def __call__(self,level=1,file=sys.stdout):
        def _log_wrapper(func):
            def wrapper(*args,**kwargs):
                if __DEBUG__:
                    print('函数开始于',datetime.datetime.now(),file=file,flush=True)
        
                func(*args,**kwargs)
    
                if __DEBUG__:
                    print('函数结束于',datetime.datetime.now(),file=file,flush=True)
            return wrapper
        return _log_wrapper
    
    def sendto(self,addr,timeout=20):
        pass
    
log=_log()


fp = open('out.log','a')

@log(file=fp)
def test(a,b):
    
    print('test')
    test_lst = []
    for i in range(a):
        test_lst.append(i*b)

test(1,2)
  Python知识库 最新文章
Python中String模块
【Python】 14-CVS文件操作
python的panda库读写文件
使用Nordic的nrf52840实现蓝牙DFU过程
【Python学习记录】numpy数组用法整理
Python学习笔记
python字符串和列表
python如何从txt文件中解析出有效的数据
Python编程从入门到实践自学/3.1-3.2
python变量
上一篇文章      下一篇文章      查看所有文章
加:2022-05-10 11:52:00  更:2022-05-10 11:53:14 
 
开发: 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 14:58:36-

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