1.函数装饰器的工作原理

函数装饰器的工作原理是怎样的呢?假设用 funA() 函数装饰器去装饰 funB() 函数,如下所示:

#funA 作为装饰器函数
def funA(fn):
#...
fn() # 执行传入的fn参数
#...
return '...'
@funA
def funB():
#...

实际上,上面程序完全等价于下面的程序:

def funA(fn):
#...
fn() # 执行传入的fn参数
#...
return '...'
def funB():
#... funB = funA(funB)

通过比对以上 2 段程序不难发现,使用函数装饰器 A() 去装饰另一个函数 B(),其底层执行了如下 2 步操作:

  1. 将 B 作为参数传给 A() 函数;
  2. 将 A() 函数执行完成的返回值反馈回 B。

举个实例:

#funA 作为装饰器函数
def funA(fn):
print("C语言中文网")
fn() # 执行传入的fn参数
print("http://c.biancheng.net")
return "装饰器函数的返回值"
@funA
def funB():
print("学习 Python")
程序执行流程为:
C语言中文网
学习 Python
http://c.biancheng.net

在此基础上,如果在程序末尾添加如下语句:

print(funB)

其输出结果为:

装饰器函数的返回值

显然,被“@函数”修饰的函数不再是原来的函数,而是被替换成一个新的东西(取决于装饰器的返回值),即如果装饰器函数的返回值为普通变量,那么被修饰的函数名就变成了变量名;同样,如果装饰器返回的是一个函数的名称,怎么被修饰的函数名依然表示一个函数。

实际上,所谓函数装饰器,就是通过装饰器函数,在不修改原函数的前提下,来对函数的功能进行合理的扩充。

2.带参数的函数装饰器

在分析 funA() 函数装饰器和 funB() 函数的关系时,细心的读者可能会发现一个问题,即当 funB() 函数无参数时,可以直接将 funB 作为 funA() 的参数传入。但是,如果被修饰的函数本身带有参数,那应该如何传值呢?

比较简单的解决方法就是在函数装饰器中嵌套一个函数,该函数带有的参数个数和被装饰器修饰的函数相同。例如:

def funA(fn):
# 定义一个嵌套函数
def say(arc):
print("Python教程:",arc)
return say
@funA
def funB(arc):
print("funB():", a)
funB("http://c.biancheng.net/python") 程序执行结果为:
Python教程: http://c.biancheng.net/python

这里有必要给读者分析一下这个程序,其实,它和如下程序是等价的:

def funA(fn):
# 定义一个嵌套函数
def say(arc):
print("Python教程:",arc)
return say
def funB(arc):
print("funB():", a) funB = funA(funB)
funB("http://c.biancheng.net/python")

如果运行此程序会发现,它的输出结果和上面程序相同。

显然,通过 funB() 函数被装饰器 funA() 修饰,funB 就被赋值为 say。这意味着,虽然我们在程序显式调用的是 funB() 函数,但其实执行的是装饰器嵌套的 say() 函数。

但还有一个问题需要解决,即如果当前程序中,有多个(≥ 2)函数被同一个装饰器函数修饰,这些函数带有的参数个数并不相等,怎么办呢?

最简单的解决方式是用 args 和 **kwargs 作为装饰器内部嵌套函数的参数,args 和 **kwargs 表示接受任意数量和类型的参数。举个例子:

def funA(fn):
# 定义一个嵌套函数
def say(*args,**kwargs):
fn(*args,**kwargs)
return say
@funA
def funB(arc):
print("C语言中文网:",arc)
@funA
def other_funB(name,arc):
print(name,arc)
funB("http://c.biancheng.net")
other_funB("Python教程:","http://c.biancheng.net/python") 运行结果为:
C语言中文网: http://c.biancheng.net
Python教程: http://c.biancheng.net/python

3. 函数装饰器可以嵌套

上面示例中,都是使用一个装饰器的情况,但实际上,Python 也支持多个装饰器,比如:

@funA
@funB
@funC
def fun():
#...

上面程序的执行顺序是里到外,所以它等效于下面这行代码:

fun = funA( funB ( funC (fun) ) )

装饰器应用场景:

  1. 比如网站很多页面需要登录后才能操作,这里就可以把这个登录判断写成一个装饰器函数;
  2. 需要登录才能操作的页面直接调用上面写的函数 @装饰器函数,把当前页作为一个参数传过去;
  3. 装饰器函数接收到页面后,判断当前用户是否登录,如果登录则返回2步真正的页面,否则跳转到登录页面;

    实例参考:《Django(十六)基于模板的登录案例》
def login_required(view_func):
'''登录判断装饰器'''
def wrapper(request, *view_args, **view_kwargs):
# 判断用户是否登录
if request.session.has_key('islogin'):
# 用户已登录,调用对应的视图
return view_func(request, *view_args, **view_kwargs)
else:
# 用户未登录,跳转到登录页
return redirect('/login')
return wrapper

调用:

# /change_pwd
@login_required #作用:把此页面作为一个参数传到login_required里
def change_pwd(request):
'''显示修改密码页面'''
return render(request, 'booktest/change_pwd.html')

Python @函数装饰器及用法的更多相关文章

  1. Python函数装饰器高级用法

    在了解了Python函数装饰器基础知识和闭包之后,开始正式学习函数装饰器. 典型的函数装饰器 以下示例定义了一个装饰器,输出函数的运行时间: 函数装饰器和闭包紧密结合,入参func代表被装饰函数,通过 ...

  2. Python @函数装饰器及用法(超级详细)

    函数装饰器的工作原理是怎样的呢?假设用 funA() 函数装饰器去装饰 funB() 函数,如下所示: #funA 作为装饰器函数 def funA(fn): #... fn() # 执行传入的fn参 ...

  3. Python函数装饰器原理与用法详解《摘》

    本文实例讲述了Python函数装饰器原理与用法.分享给大家供大家参考,具体如下: 装饰器本质上是一个函数,该函数用来处理其他函数,它可以让其他函数在不需要修改代码的前提下增加额外的功能,装饰器的返回值 ...

  4. python函数-装饰器

    python函数-装饰器 1.装饰器的原则--开放封闭原则 开放:对于添加新功能是开放的 封闭:对于修改原功能是封闭的 2.装饰器的作用 在不更改原函数调用方式的前提下对原函数添加新功能 3.装饰器的 ...

  5. Python的装饰器实例用法小结

    这篇文章主要介绍了Python装饰器用法,结合实例形式总结分析了Python常用装饰器的概念.功能.使用方法及相关注意事项 一.装饰器是什么 python的装饰器本质上是一个Python函数,它可以让 ...

  6. Python 函数装饰器

    首次接触到装饰器的概念,太菜啦! Python 装饰器可以大大节省代码的编写量,提升代码的重复使用率.函数装饰器其本质也是一个函数,我们可以把它理解为函数中定义了一个子函数. 例如我们有这么一个需求, ...

  7. Python高手之路【四】python函数装饰器

    def outer(func): def inner(): print('hello') print('hello') print('hello') r = func() print('end') p ...

  8. python 函数 装饰器 内置函数

    函数 装饰器 内置函数 一.命名空间和作用域 二.装饰器 1.无参数 2.函数有参数 3.函数动态参数 4.装饰器参数 三.内置函数 salaries={ 'egon':3000, 'alex':10 ...

  9. Python 函数装饰器简明教程

    定义类的静态方法时,就使用了装饰器.其实面向对象中的静态方法都是使用了装饰器. @staticmethod def jump(): print(" 3 meters high") ...

随机推荐

  1. SpringMvc 项目配置

    spring-mvc.xml <?xml version="1.0" encoding="UTF-8"?> <beans xmlns=&quo ...

  2. 面试问题之——给你图片的url,你能知道它所占的字节空间吗?

    从一个需求说起 这标题起得有点标题党,实际情况是我最近在做一个公司内部工具时,遇到了这么一个需求,给定一个静态资源站点上某张图片的url(比如https://a.xxxcdn.com/demo.jpg ...

  3. Plastic Sprayer Manufacturer - How Does The Sprayer Work?

    The Plastic Sprayers Manufacturer   stated that the sprayer is a very useful type of machine and a g ...

  4. Python 正则表达式之 sub 和 subn函数的使用

    re.sub() 函数的功能 re是reguler expressioin的缩写,表示正则表达式 sub 是 substitute 的缩写,表示替换: re.sub是个正则表达式方面的函数,用来实现通 ...

  5. Hibernate框架:org.hibernate.exception.SQLGrammarException: Cannot open connection at org.hibernate.exception.SQLStateConverter.convert(SQLStateConverter.java92)

    报错信息 org.hibernate.exception.SQLGrammarException: Cannot open connection at org.hibernate.exception. ...

  6. loss训练技巧

    一,train loss与test loss结果分析4666train loss 不断下降,test loss不断下降,说明网络仍在学习; train loss 不断下降,test loss趋于不变, ...

  7. Atcoder Beginner Contest 140E(多重集,思维)

    #define HAVE_STRUCT_TIMESPEC#include<bits/stdc++.h>using namespace std;multiset<long long&g ...

  8. Eth合约攻击

    前言 Ethernaut是一个类似于CTF的智能合约平台,集成了不少的智能合约相关的安全问题,这对于安全审计人员来说是一个很不错的学习平台,本篇文章将通过该平台来学习智能合约相关的各种安全问题,由于关 ...

  9. C++的注册和回调

    注册回调的作用 在设计模式中注册回调的方式叫做回调模式.在SDK开发中,为增强开发者的SDK通用性,排序或者一些算法逻辑需要使用者进行编写.这时候就需要向SDK传递回调函数.注册回调能使下层主动与上层 ...

  10. Groovy轻松入门——通过与Java的比较,迅速掌握Groovy

    转自 :Groovy轻松入门——通过与Java的比较,迅速掌握Groovy (更新于2008.10.18) 在前几篇文章中,我已经向大家介绍了Groovy是什么,学习Groovy的重要性等内容,还不了 ...