深入了解python装饰器

目录
  • 一、装饰器
    • 1.相关知识点
    • 2.语法糖
    • 3.装饰器模板
    • 4.有参装饰器

一、装饰器

1.相关知识点

  • *args:负责将多余的位置实参汇总,赋值给args
  • **kwargs:负责将多余的关键字实参汇总,赋值给kwargs

命名空间与作用域

函数对象:

  • 可以把函数当成参数传入
  • 可以把函数当做返回值返回

函数的嵌套定义:在函数内定义函数

闭包函数:父函数的返回值为一个函数,被返回的函数调用了父函数的局部变量,且该函数可以在父函数外部执行

装饰器:

装饰器:定义一个为其他函数添加功能的函数

为什么要使用装饰器?

  • 开放封闭原则:开放扩展功能但封闭源代码的修改
  • 装饰器就是在不修改装饰对象源代码以及调用方式的前提下,为装饰对象添加新功能

装饰器实现:

需求:不修改源代码,计算代码执行时间

 源代码:

import time
def func0(x):
    time.sleep(1)
    print(x)
func0(0)

# 方案一:实现了计算代码执行时间的功能,但修改了源代码,不符合要求
def func1(x):
    start = time.time()
    time.sleep(1)
    print(x)
    end = time.time()
    print('方案一 运行时间:{}'.format(end - start))
func1(1)

# 方案二:满足要求,但代码不具备重用性
start = time.time()
func0(2)
end = time.time()
print('方案二 运行时间:{}'.format(end - start))

# 方案三:将方案二封装为函数,但修改了函数的调用方式,不符合要求
def wrapper():
    start = time.time()
    func0(3)
    end = time.time()
    print('方案三 运行时间:{}'.format(end - start))
wrapper()

# 方案四:不修改原函数的调用方式
def wrapper(x):
    start = time.time()
    func0(x)
    end = time.time()
    print('方案四 运行时间:{}'.format(end - start))
wrapper(4)

# 方案五:方案四参数被写死,优化方案四
def wrapper(*args, **kwargs):
    start = time.time()
    func0(*args, **kwargs)
    end = time.time()
    print('方案五 运行时间:{}'.format(end - start))
wrapper(5)

# 方案六:方案五函数被写死,优化方案五,但修改了源代码的调用方式
def outter(a):
    def wrapper(*args, **kwargs):
        start = time.time()
        a(*args, **kwargs)
        end = time.time()
        print('方案六 运行时间:{}'.format(end - start))
    return wrapper
f = outter(func0)
f(6)

# 方案七:优化方案六(outter即为装饰器,用于装饰func0)
def outter(a):
    def wrapper(*args, **kwargs):
        start = time.time()
        a(*args, **kwargs)
        end = time.time()
        print('方案七 运行时间:{}'.format(end - start))
    return wrapper
func0 = outter(func0)    # 偷梁换柱,调用者无感知
func0(7)

运行结果:

0
1
方案一 运行时间:1.001857042312622
2
方案二 运行时间:1.0040733814239502
3
方案三 运行时间:1.0017154216766357
4
方案四 运行时间:1.007995367050171
5
方案五 运行时间:1.0145602226257324
6
方案六 运行时间:1.0046615600585938
7
方案七 运行时间:1.0094060897827148

2.语法糖

  • 使用语法糖,需要将装饰器放到被装饰对象前
# 不使用语法糖
import time
def func0(x):
    time.sleep(1)
    print(x)
# func0(0)

def outter(a):
    def wrapper(*args, **kwargs):
        start = time.time()
        a(*args, **kwargs)
        end = time.time()
        print('运行时间:{}'.format(end - start))
    return wrapper
func0 = outter(func0)
func0('hello')

# 使用语法糖
import time

def outter(a):
    def wrapper(*args, **kwargs):
        start = time.time()
        a(*args, **kwargs)
        end = time.time()
        print('运行时间:{}'.format(end - start))
    return wrapper

@outter # 语法糖,等价于该行func0 = outter(func0)
def func0(x):
    time.sleep(1)
    print(x)

func0('hello')
print(func0.__name__) 

运行结果:

hello
运行时间:1.0050427913665771
wrapper

3.装饰器模板

 # 装饰器模板
 def decorator_name(x):
     def wrapper(*args, **kwargs):
         # ...新添加的功能...
         # 下为调用原函数的格式
         x(*args, **kwargs)
     return wrapper

@decorator_name
 def func_name():
     pass

扩展:真正实现偷梁换柱,调用者无感知

  • 不使用装饰器
 # 不使用装饰器
import time

def func0(x):
    time.sleep(1)
    print(x)

func0('hello')
print(func0.__name__) # 查看函数名 
print(help(func0)) # 查看帮助信息(主要为注释内容)

运行结果:

hello
func0
Help on function func0 in module main:

func0(x)
这是函数

None

使用装饰器:

import time

def outter(a):
    def wrapper(*args, **kwargs):
        '''这是装饰器'''
        start = time.time()
        a(*args, **kwargs)
        end = time.time()
        print('运行时间:{}'.format(end - start))
    return wrapper

@outter # func0 = outter(func0)
def func0(x):
    '''这是函数'''
    time.sleep(1)
    print(x)

func0('hello')
print(func0.__name__)
print(help(func0))

运行结果:

hello
运行时间:1.011878490447998
wrapper
Help on function wrapper in module main:

wrapper(*args, **kwargs)
这是装饰器

None

呕吼,露馅了

  • 解决方法,将原函数的属性和方法,赋值给装饰器内的wrapper
import time

def outter(a):
    def wrapper(*args, **kwargs):
        '''这是装饰器'''
        start = time.time()
        a(*args, **kwargs)
        end = time.time()
        print('运行时间:{}'.format(end - start))
    wrapper.__name__ = a.__name__
    wrapper.__doc__ = a.__doc__
    return wrapper

@outter # func0 = outter(func0)
def func0(x):
    '''这是函数'''
    time.sleep(1)
    print(x)

func0('hello')
print(func0.__name__)
print(help(func0))

运行结果:

hello
运行时间:1.0030155181884766
func0
Help on function func0 in module main:

func0(*args, **kwargs)
这是函数

None

但是,函数有很多属性和方法,一个一个手动修改过于麻烦,甚至可能会遗漏,但python也提供了解决方法

import time
from functools import wraps

def outter(a):
    @wraps(a)
    def wrapper(*args, **kwargs):
        '''这是装饰器'''
        start = time.time()
        a(*args, **kwargs)
        end = time.time()
        print('运行时间:{}'.format(end - start))
    # wrapper.__name__ = a.__name__
    # wrapper.__doc__ = a.__doc__
    return wrapper

@outter # func0 = outter(func0)
def func0(x):
    '''这是函数'''
    time.sleep(1)
    print(x)

func0('hello')
print(func0.__name__)
print(help(func0))

运行结果:

hello
运行时间:1.0114128589630127
func0
Help on function func0 in module main:

func0(x)
这是函数

None

4.有参装饰器

  • 装饰器内需要传入参数,但是由于语法糖的限制,装饰器只能有一个参数,并且该参数仅用来接收被装饰对象的内存地址,如何传入其他参数?
  • 解决思路:将装饰器做成闭包函数
def outter(db_type):
    # 装饰器auth
    def auth(x):
        def wrapper(*args, **kwargs):
            if db_type == 'file':
                name = input('请输入姓名:')
                passwd = input('请输入密码:')
                if name == 'zhangsan' and passwd == '123':
                    x(*args, **kwargs)
                    print('基于文件认证')
                else:
                    print('用户名或密码错误,认证失败')
            elif db_type == 'mysql':
                x(*args, **kwargs)
                print('基于数据库认证')
            else:
                print('未知认证方式,不支持')
        return wrapper
    return auth

# 函数
auth = outter(db_type='file')
@auth
def file():
    print('hello')

auth = outter(db_type='mysql')
@auth
def mysql():
    print('world')

msg = input('请选择认证方式(1=file|2=mysql):').strip()
if msg =='1':
    file()
elif msg == '2':
    mysql()
else:
    print('不支持')
  • 将传入的参数写入语法糖
def outter(db_type):
    # 装饰器auth
    def auth(x):
        def wrapper(*args, **kwargs):
            if db_type == 'file':
                name = input('请输入姓名:')
                passwd = input('请输入密码:')
                if name == 'zhangsan' and passwd == '123':
                    x(*args, **kwargs)
                    print('基于文件认证')
                else:
                    print('用户名或密码错误,认证失败')
            elif db_type == 'mysql':
                x(*args, **kwargs)
                print('基于数据库认证')
            else:
                print('未知认证方式,不支持')
        return wrapper
    return auth

# 函数
# auth = outter(db_type='file')
# @auth
@outter(db_type='file')
def file():
    print('hello')

# auth = outter(db_type='mysql')
# @auth
@outter(db_type='mysql')
def mysql():
    print('world')

msg = input('请选择认证方式(1=file|2=mysql):').strip()
if msg =='1':
    file()
elif msg == '2':
    mysql()
else:
    print('不支持')
  • 模板
# 有参装饰器模板
def out_decorator_name(x,y,z):
    def decorator_name(a):
        def wrapper(*args, **kwargs):
            # ...新添加的功能...
            # 下为调用原函数的格式
            a(*args, **kwargs)
        return wrapper
    return decorator_name

@out_decorator_name(x,y,z=1)
def func_name():
     pass

到此这篇关于深入了解python装饰器的文章就介绍到这了,更多相关python装饰器内容请搜索我们以前的文章或继续浏览下面的相关文章希望大家以后多多支持我们!

(0)

相关推荐

  • Python的闭包和装饰器你真的了解吗

    目录 闭包 装饰器 总结 闭包 闭包就是能够读取其他函数内部变量的函数. def test1(k, b): def test1_1(x): print(k*x+b) return test1_1 t1 = test1(1, 2) t1(0) t1(1) t1(2) 闭包中修改数据 x = 300 def test1(): x = 200 def test2(): nonlocal x print("----1----x = {}".format(x)) x = 100 print(&q

  • Python装饰器的练习题

    目录 1.请实现一个装饰器,把函数的返回值+100然后返回 2.请实现一个装饰器,通过一次调用使函数重复执行5次 3.请实现一个装饰器每次调用函数时,将函数名字及调用函数的时间点写入文件中 1.请实现一个装饰器,把函数的返回值+100然后返回 def wapper(func): def innner(*args,**kwargs): ret=func(*args,**kwargs) ret=print(ret+100) return ret return innner @wapper def f

  • python中的迭代器,生成器与装饰器详解

    目录 迭代器 生成器 装饰器 总结 迭代器 每一个可迭代类内部都要实现__iter__()方法,返回一个迭代类对象,迭代类对象则定义了这个可迭代类如何迭代. for循环调用list本质上是是调用了list的迭代器进行迭代. # 对list进行for循环本质上是调用了list的迭代器 list = [1,2,3,4] # for 循环调用 for elem in list: print(elem) # 迭代器调用 list_iter = list.__iter__() while True: tr

  • Python的装饰器详情介绍

    目录 1.定义及使用 2.@classmethod 1.定义及使用 例1:装饰器定义: def 装饰器函数(外部函数):            def 内联函数(*args,**kwargs):                ...前置装饰...                外部函数(*args,**kwargs)                ...后置装饰...            return 内联函数  例2:装饰器两种调用方式 第一种:装饰器函数(外部函数)(参数1,参数2....

  • Python装饰器实现函数运行时间的计算

    目录 个人理解 例子:调用函数的同时对函数进行计时 实现方法1:@语法糖 代码: 实现结果: 实现方法2:闭包 代码: 实现结果: 实现方式1和 2的差异 总结 个人理解 装饰器: 通过闭包和将一个函数作为另一个函数参数的形式,实现已有功能的灵活调用 例如: 首先设置了一个time_master的计时器函数,在运行某个函数的同时,用来统计函数的耗时 那么,如果想知道函数性能, 每次写完新的函数后,都放到time_master函数中运行一次来统计. ——> 这是比较麻烦的.相当于虽然有了一个称,但

  • Python函数装饰器的使用详解

    目录 装饰器 装饰器的定义 装饰器的意义 装饰器的使用 无参装饰器 有参装饰器 实例练习 总结 装饰器 装饰器的定义 关于装饰器的定义,我们先来看一段github上大佬的定义: Function decorators are simply wrappers to existing functions.In the context of design patterns,decorators dynamically alter the functionality of a function, met

  • python装饰器实例大详解

    一.作用域 在python中,作用域分为两种:全局作用域和局部作用域. 全局作用域是定义在文件级别的变量,函数名.而局部作用域,则是定义函数内部. 关于作用域,我们要理解两点: a.在全局不能访问到局部定义的变量 b.在局部能够访问到全局定义的变量,但是不能修改全局定义的变量(当然有方法可以修改) 下面我们来看看下面实例: x = 1 def funx(): x = 10 print(x) # 打印出10 funx() print(x) # 打印出1 如果局部没有定义变量x,那么函数内部会从内往

  • Python 装饰器深入理解

    讲 Python 装饰器前,我想先举个例子,虽有点污,但跟装饰器这个话题很贴切. 每个人都有的内裤主要功能是用来遮羞,但是到了冬天它没法为我们防风御寒,咋办?我们想到的一个办法就是把内裤改造一下,让它变得更厚更长,这样一来,它不仅有遮羞功能,还能提供保暖,不过有个问题,这个内裤被我们改造成了长裤后,虽然还有遮羞功能,但本质上它不再是一条真正的内裤了.于是聪明的人们发明长裤,在不影响内裤的前提下,直接把长裤套在了内裤外面,这样内裤还是内裤,有了长裤后宝宝再也不冷了.装饰器就像我们这里说的长裤,在不

  • 使用Python装饰器在Django框架下去除冗余代码的教程

    Python装饰器是一个消除冗余的强大工具.随着将功能模块化为大小合适的方法,即使是最复杂的工作流,装饰器也能使它变成简洁的功能. 例如让我们看看Django web框架,该框架处理请求的方法接收一个方法对象,返回一个响应对象: def handle_request(request): return HttpResponse("Hello, World") 我最近遇到一个案例,需要编写几个满足下述条件的api方法: 返回json响应 如果是GET请求,那么返回错误码 做为一个注册api

  • 深入理解Python装饰器

    装饰器简介: 装饰器(decorator)是一种高级Python语法.装饰器可以对一个函数.方法或者类进行加工.在Python中,我们有多种方法对函数和类进行加工,比如在Python闭包中,我们见到函数对象作为某一个函数的返回结果.相对于其它方式,装饰器语法简单,代码可读性高.因此,装饰器在Python项目中有广泛的应用. 装饰器最早在Python 2.5中出现,它最初被用于加工函数和方法这样的可调用对象(callable object,这样的对象定义有__call__方法).在Python 2

  • python装饰器初探(推荐)

    一.含有一个装饰器 #encoding: utf-8 ############含有一个装饰器######### def outer(func): def inner(*args, **kwargs):#要装饰f1(),这里用这俩形式参数,可以接受任意个参数,不管f1定义几个参数 print "1" r = func(*args, **kwargs)#这里要用func,不要用f1 print "2" return r return inner @outer #这里ou

  • 深入浅出分析Python装饰器用法

    本文实例讲述了Python装饰器用法.分享给大家供大家参考,具体如下: 用类作为装饰器 示例一 最初代码: class bol(object): def __init__(self, func): self.func = func def __call__(self): return "<b>{}</b>".format(self.func()) class ita(object): def __init__(self, func): self.func = f

  • Python装饰器基础详解

    装饰器(decorator)是一种高级Python语法.装饰器可以对一个函数.方法或者类进行加工.在Python中,我们有多种方法对函数和类进行加工,比如在Python闭包中,我们见到函数对象作为某一个函数的返回结果.相对于其它方式,装饰器语法简单,代码可读性高.因此,装饰器在Python项目中有广泛的应用. 前面快速介绍了装饰器的语法,在这里,我们将深入装饰器内部工作机制,更详细更系统地介绍装饰器的内容,并学习自己编写新的装饰器的更多高级语法. 什么是装饰器 装饰是为函数和类指定管理代码的一种

  • python装饰器与递归算法详解

    1.python装饰器 刚刚接触python的装饰器,简直懵逼了,直接不懂什么意思啊有木有,自己都忘了走了多少遍Debug,查了多少遍资料,猜有点点开始明白了.总结了一下解释得比较好的,通俗易懂的来说明一下: 小P闲来无事,随便翻看自己以前写的一些函数,忽然对一个最最最基础的函数起了兴趣: def sum1(): sum = 1 + 2 print(sum) sum1() 此时小P想看看这个函数执行用了多长时间,所以写了几句代码插进去了: import time def sum1(): star

  • Python装饰器实现几类验证功能做法实例

    最近新需求来了,要给系统增加几个资源权限.尽量减少代码的改动和程序的复杂程度.所以还是使用装饰器比较科学 之前用了一些登录验证的现成装饰器模块.然后仿写一些用户管理部分的权限装饰器. 比如下面这种 def permission_required(permission): def decorator(f): @wraps(f) def decorated_function(*args, **kwargs): if not current_user.can(permission): abort(40

  • Python装饰器入门学习教程(九步学习)

    装饰器(decorator)是一种高级Python语法.装饰器可以对一个函数.方法或者类进行加工.在Python中,我们有多种方法对函数和类进行加工,比如在Python闭包中,我们见到函数对象作为某一个函数的返回结果.相对于其它方式,装饰器语法简单,代码可读性高.因此,装饰器在Python项目中有广泛的应用. 这是在Python学习小组上介绍的内容,现学现卖.多练习是好的学习方式. 第一步:最简单的函数,准备附加额外功能 # -*- coding:gbk -*- '''示例1: 最简单的函数,表

随机推荐