Python装饰器

文章详细阐述了Python编程中的闭包概念,包括闭包形成的三个条件,并通过示例解释了闭包的工作原理。接着,介绍了装饰器作为闭包的一种应用,展示了如何创建计算函数运行时间和记录日志的装饰器。此外,文章还讨论了带参数的装饰器以及如何用类实现装饰器,最后提到了装饰类的概念。
摘要由CSDN通过智能技术生成

一、闭包

先补充一下变量的生命周期这个知识点,对于局部变量,它会随着函数的调用生成,函数调用完成就会释放,例如:

def func1():
    print("i am func1")

def outer():
    #设置x局部变量
    x = 30
    func1()
    print(f"x is {x}")

outer()
#变量x会随着函数调用的结束而释放
print(x)

由于函数调用的结束,局部变量x被释放,所以打印x会报NameError的错误。

闭包形成的三个条件:

  1. 必须要有内外函数
  2. 外函数必须返回内函数
  3. 内函数必须引用外函数的变量

例如:

def outer(x):
    a = 200
    def inner():
        print(x + a)
    #返回函数本身,不是返回内函数的调用结果,函数名后面不加()
    return inner

#调用外函数outer,调用结束后,没有释放a和x
d = outer(100)
#调用内函数,输出x+a的值
d()

二、装饰器

装饰器是一种程序设计模式,主要用于在不改变函数或类源代码的基础上添加一些额外功能。装饰器本质就是闭包函数,它需要把一个callable对象(函数、类)作为参数传入。装饰器只能装饰callable对象(函数、类)。

例:写一个计算函数运行时间的装饰器

import time

def runtime(func):
    def inner(*args, **kwargs):
        #获取当前时间戳
        start = time.time()
        #*args, **kwargs让装饰器更加通用
        result = func(*args, **kwargs)    #执行原函数
        #获取原函数运行结束后的时间戳
        end = time.time()
        print(f"函数执行花费了{end - start}s")
        #inner函数返回原函数的返回值
        return result
    return inner

使用装饰器,用@修饰符:

@runtime    #相当于func = runtime(func)
def func(a, b):
    #睡眠2秒
    time.sleep(2)
    return a + b

#被装饰过的函数就不是原函数了,是装饰器返回的内函数
func(1, 3)
print(f"func函数运行结果为{result}")

运行结果:

例:编写一个记录日志的装饰器,记录INFO等级并写到文件和屏幕

import logging
from logging import FileHandler, StreamHandler

def log(func):
    def inner_log(*args, **kwargs):
        logger = logging.getLogger()
        file = FileHandler("sc.log")
        stream =  StreamHandler()
        
        #指定格式记录日志
        log_format = "%(asctime)s | %(filename)s | %(levelname)s:%(message)s"
        formatter = logging.Formatter(log_format)
        file.setFormatter(formatter)
        stream.setFormatter(formatter)

        logger.addHandler(file)
        logger.addHandler(stream)
        logger.setLevel(logging.INFO)

        logger.info(f"开始执行{func.__name__}函数!")
        result = func(*args, **kwargs)
        logger.info(f"{func.__name__}函数执行结束!")
        return result
    return inner_log

使用上文编写的两个装饰器--log和runtime装饰器,装饰一个函数:

#被两个装饰器装饰后,MyFunc为log的内函数
@log        #log装饰的是runtime的内函数
@runtime    #runtime装饰的是原来的MyFunc函数
def Myfunc():
    time.sleep(1)
    print("this is MyFunc")

#此时的Myfunc = log(runtime(Myfunc))
Myfunc()

#注意装饰器的先后顺序

运行结果:

三、带参数的装饰器

自身不传入参数的装饰器,使用两层函数定义;自身传入参数的装饰器,使用三层函数定义。

例:使用装饰器带参数来验证用户是否有权限来运行函数。add函数添加权限认证装饰器,判断一:判断用户名和密码是否正确,不正确提示用户名和密码错误,不能执行add函数;判断二、判断用户是否为root,只有root有权限执行函数。其中用户名和密码通过带参数的装饰器传入。

user = {"root": "123456", "admin": "admin123"}
name = input("请输入用户名:")
password = input("请输入密码:")

def check(userdict, username, password):
    def checkfunc(func):
        def inner(*args, **kwargs):
            if userdict.get(username) == password:
                if username == "root":
                    func(*args, **kwargs)
                    print(f"{func.__name__}函数运行成功!")
                else:
                    print(f"{username}用户没有权限")
            else:
                print("用户名或密码错误!")
        return inner
    return checkfunc

@check(userdict=user, username=name, password=password)
def add(a, b):
    print(f"{a} + {b} = {a + b}")

add(1, 2)

add函数执行成功运行结果:

add函数未能成功执行的运行结果:

四、用类实现装饰器

例:用类实现计时装饰器

import time

class runtime():
    def __init__(self, func):
        self.func = func
    
    #把实例化后的对象当做函数调用的时候自动执行
    def __call__(self, *args, **kwargs):
        #在内函数中实现的功能,在类中放在__call__中
        start = time.time()
        result = self.func(*args, **kwargs)
        end = time.time()
        print(f"执行函数花费了{end - start}s")
        return result

@runtime        #func1 = runtime(func1)
def func1():
    time.sleep(1)
    print("i am func1")

func1()

运行结果:

用类实现带参数的装饰器:改写上文写过的权限认证装饰器

class check:
    def __init__(self, userdict, username, password):
        self.userdict = userdict
        self.username = username
        self.password = password
    
    def __call__(self, func):
        def inner(*args, **kwargs):
            if self.userdict.get(self.username) == self.password:
                if self.username == "root":
                    func(*args, **kwargs)
                    print(f"{func.__name}函数执行成功!")
                else:
                    print(f"{self.username}用户权限不够!")
            else:
                print("用户名或密码错误!")
        return inner

user = {"root": "123456", "admin": "admin123"}
name = input("请输入用户名:")
password = input("请输入密码:")

@check(userdict=user, username=name, password=password)
def add(a, b):
    print(f"{a} + {b} = {a + b}")

#a = check(userdict=user, username=name, password=password), add = a(add)
add(1, 2)

五、装饰类

例:

def outer(cls):
    def inner(*args, **kwargs):
        print(f"class name is {cls.__name__}")
        return cls(*args, **kwargs)
    return inner

@outer        #A = outer(A)
class A:
    def __init__(self, name):
        self.name = name

#a1为装饰器outer中内函数返回的一个对象
a1 = A("sc")
print(f"name of a1 is {a1.name}")

运行结果:

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值