1、什么是装饰器?

Python的装饰器的英文名叫Decorator,当你看到这个英文名的时候,你可能会把其跟Design Pattern里的Decorator搞混了,其实这是完全不同的两个东西。虽然好像,他们要干的事都很相似——都是想要对一个已有的模块做一些“修饰工作”,所谓修饰工作就是想给现有的模块加上一些小装饰(一些小功能,这些小功能可能好多模块都会用到),但又不让这个小装饰(小功能)侵入到原有的模块中的代码里去。

实例:文件名hello.py

 #!/usr/bin/env python
 # _*_ coding:utf-8 _*_
 # Author: enzhi.wang

 def hello(fn):
     def wrapper():
         print("hello,%s"% fn.__name__)
         fn()
         print("goodby,%s"% fn.__name__)
     return wrapper

 @hello
 def foo():
     print("i am foo")

 foo()

当你运行代码,你会看到如下输出

C:\Python3.5\python.exe C:/Users/root/PycharmProjects/S14/day4/hello.py
hello,foo
i am foo
goodby,foo

上面代码中你可以看到如下东西

1.函数foo前面有个@hello,hello就是我们前面定义的hello函数

2.hello函数中需要一个fn的参数,fn这个参数就是用来调取foo函数

3.hello函数中返回了一个wrapper函数,wrapper函数中回调了foo函数,并在回调前后加了两条语句。

小结:

@ + 函数名

功能:

  1.自动执行hello函数,并且将hello函数下面的foo函数名当做参数传递给hello

  2.将hello参数的返回值重新赋值给foo

2、Decorator的本质

对于Python的这个@注解语法糖- Syntactic Sugar 来说,当你在用某个@decorator来修饰某个函数func时,如下所示:

1  @decorator
2  def func():
3    pass

其解释器会解释成下面这样的语句

 func = decorator(func)

尼玛,这不就是把一个函数当参数传到另一个函数中,然后再回调吗?是的,但是,我们需要注意,那里还有一个赋值语句,把decorator这个函数的返回值赋值回了原来的func。 根据《函数式编程》中的first class functions中的定义的,你可以把函数当成变量来使用,所以,decorator必需得返回了一个函数出来给func,这就是所谓的higher order function 高阶函数,不然,后面当func()调用的时候就会出错。 就我们上面那个hello.py里的例子来说,

 @hello
 def foo():
     print("i am foo")

被解释成了

foo = hello(foo)

再回到我们hello.py的那个例子,我们可以看到,hello(foo)返回了wrapper()函数,所以,foo其实变成了wrapper的一个变量,而后面的foo()执行其实变成了wrapper()

3.带参数的Decorator

当被调用的函数需要传入参数时如何用装饰器去装饰函数

实例:传入一个参数,文件名decorator_args1.py

 #!/usr/bin/env python
 # _*_ coding:utf-8 _*_
 # Author: enzhi.wang
 def outer(func):
     def inner(args):
         print('before')
         func(args)
         print('after')
     return inner
 @outer
 def func(args):
     print("hello %s"% (args))

 func('enzhi')

当你运行上面代码会看到以下输出:

C:\Python3.5\python.exe C:/Users/root/PycharmProjects/S14/day4/decorator_args1.py
before
hello enzhi
after

上面代码中你可以看到如下东西

1.函数func前面有个@outer,outer就是我们前面定义的outer函数

2.outer函数中需要一个func的参数,func这个参数就是用来调取func函数

3.func函数执行需要传入一个实参

4.outer函数中返回了一个inner函数,并且inner函数也需要接收一个实参,此时的func = inner

5.inner函数中回调了老的func函数,所以需要在inner函数内部调用func函数并传入一个参数

实例:传入多个参数 decorator_args2.py

 #!/usr/bin/env python
 # _*_ coding:utf-8 _*_
 # Author: enzhi.wang
 def outer(func):
     def inner(*args,**kwargs):
         print('before')
         func(*args,**kwargs)
         print('after')
     return inner

 @outer
 def f1(args):
     print("hello %s"% (args))

 @outer
 def f2(*args,**kwargs):
     print("Name %s"% (kwargs['name']))
     print("Age %d"% (kwargs['age']))

 f1('enzhi')
 f2(name="Wangenzhi",age=26)

当你运行上面代码会看到以下输出:

C:\Python3.5\python.exe C:/Users/root/PycharmProjects/S14/day4/decorator_args2.py
before
hello enzhi
after
before
Name Wangenzhi
Age 26
after

上面代码中你可以看到如下东西

1.*args和**kwargs 这个两个参数用于在装饰器装饰不同的函数时可以接收多个参数

2. inner(*args,**kwargs) 接收参数传入给内部回调的函数

3. func(*args,**kwargs)  inner内部回调时处理不同函数传入的多个参数或者没有参数

4、双层装饰器

 #!/usr/bin/env python
 # _*_ coding:utf-8 _*_
 # Author: enzhi.wang
 USER_LOGIN = {}

 def checklogin(func):
     def inner(*args,**kwargs):
         if USER_LOGIN.get('is_login',None):
             ret = func(*args,**kwargs)
             return ret
         else:
             print("请登录")
     return inner

 def checkusertype(func):
     "检查用户类型"
     def inner(*args,**kwargs):
         if USER_LOGIN.get('user_type',None) == 2:
             ret = func(*args,**kwargs)
             return ret
         else:
             print("无权查看")
     return inner

 @checklogin
 @checkusertype
 def index():
     print("Index")

 @checklogin
 def home():
     print("Home")

 def login(user):
     if user == 'admin':
         USER_LOGIN['is_login'] = True
         USER_LOGIN['user_type'] = 2
     else:
         USER_LOGIN['is_login'] = True

 def main():
     while True:
         inp = input("1、登录;2、查看信息;3、管理员\n >>>")
         ':
             username = input("输入用户名:")
             login(username)
         ':
             home()
         ':
             index()

 main()

上面代码会看到如下东西:

1.@checklogin2.@checkusertype双层的装饰器它的解释顺序是右下到上1.先执行@checkusertype,返回checkusertype内部inner函数。此时index = checkusertype(index)2.执行@checklogin,返回checklogin内部inner函数,此时checklogin内部inner函数中的func函数等于checkusertype内部inner函数执行顺序是由上到下1.执行@checklogin,如果if USER_LOGIN.get('is_login',None):满足条件就会执行ret = func()2.执行ret = func(),而func此时是checkusertype内部inner函数,就会执行条件判断if USER_LOGIN.get('user_type',None) == 2:3.如果if USER_LOGIN.get('user_type',None) == 2:条件满足就会执行ret = func(*args,**kwargs),此时func指向的是老的index4.执行func()调用index()函数中的print("Index")

Python基础之装饰器的更多相关文章

  1. python基础—函数装饰器

    python基础-函数装饰器 1.什么是装饰器 装饰器本质上是一个python函数,它可以让其他函数在不需要做任何代码变动的前提下增加额外功能. 装饰器的返回值是也是一个函数对象. 装饰器经常用于有切 ...

  2. 十. Python基础(10)--装饰器

    十. Python基础(10)--装饰器 1 ● 装饰器 A decorator is a function that take a function as an argument and retur ...

  3. [python基础]关于装饰器

    在面试的时候,被问到装饰器,在用的最多的时候就@classmethod ,@staticmethod,开口胡乱回答想这和C#的static public 关键字是不是一样的,等面试回来一看,哇,原来是 ...

  4. Day11 Python基础之装饰器(高级函数)(九)

    在python中,装饰器.生成器和迭代器是特别重要的高级函数   https://www.cnblogs.com/yuanchenqi/articles/5830025.html 装饰器 1.如果说装 ...

  5. 1.16 Python基础知识 - 装饰器初识

    Python中的装饰器就是函数,作用就是包装其他函数,为他们起到修饰作用.在不修改源代码的情况下,为这些函数额外添加一些功能,像日志记录,性能测试等.一个函数可以使用多个装饰器,产生的结果与装饰器的位 ...

  6. python基础-----函数/装饰器

    函数 在Python中,定义一个函数要使用def语句,依次写出函数名.括号.括号中的参数和冒号:,然后,在缩进块中编写函数体,函数的返回值用return语句返回. 函数的优点之一是,可以将代码块与主程 ...

  7. python基础之装饰器(实例)

    1.必备 #### 第一波 #### def foo(): print 'foo' foo #表示是函数 foo() #表示执行foo函数 #### 第二波 #### def foo(): print ...

  8. 【Python基础】装饰器的解释和用法

    装饰器的用法比较简单,但是理解装饰器的原理还是比较复杂的,考虑到接下来的爬虫框架中很多用到装饰器的地方,我们先来讲解一下. 函数 我们定义了一个函数,没有什么具体操作,只是返回一个固定值 请注意一下缩 ...

  9. 学习PYTHON之路, DAY 5 - PYTHON 基础 5 (装饰器,字符格式化,递归,迭代器,生成器)

    ---恢复内容开始--- 一 装饰器 1 单层装饰器 def outer(func): def inner(): print('long') func() print('after') return ...

随机推荐

  1. Java基础复习笔记系列 四 数组

    Java基础复习笔记系列之 数组 1.数组初步介绍? Java中的数组是引用类型,不可以直接分配在栈上.不同于C(在Java中,除了基础数据类型外,所有的类型都是引用类型.) Java中的数组在申明时 ...

  2. Scalaz(14)- Monad:函数组合-Kleisli to Reader

    Monad Reader就是一种函数的组合.在scalaz里函数(function)本身就是Monad,自然也就是Functor和applicative.我们可以用Monadic方法进行函数组合: i ...

  3. js undefine,null 和NaN

    undefined 类型只有一个值,即 undefined. null 类型也只有一个值,即 null. null 指空值(empty value)或指曾赋过值,但是目前没有值 undefined 指 ...

  4. Guava学习笔记:Ordering犀利的比较器

    Ordering是Guava类库提供的一个犀利强大的比较器工具,Guava的Ordering和JDK Comparator相比功能更强.它非常容易扩展,可以轻松构造复杂的comparator,然后用在 ...

  5. 使用 WSO2 API Manager 管理 Rest API

    WSO2 API Manager 简介 随着软件工程的增多,越来越多的软件提供各种不同格式.不同定义的 Rest API 作为资源共享,而由于这些 API 资源的异构性,很难对其进行复用.WSO2 A ...

  6. 今天做项目时,用到了好多关于js的知识点,有的忘记了,然后晚上回来自己整理一番,明天继续整理。哈哈,分享给大家。

    javascript 数据类型 typeof (直接量/变量)-------判断数据类型 简单数据类型(栈,空间小,速度快) string字符串 用引号引起来的文本 number数字 所有数字 NaN ...

  7. 10个最好的 JavaScript 动画库和开发框架

    虽然 CSS3 动画功能能够让我们以简单轻松的方式实现动画效果,但是浏览器兼容性问题让人头疼.不过不用担心,我们还有另外的武器——JavaScript,它同样可以帮助你实现各种各样的动画效果,而且借助 ...

  8. reactjs学习一(环境搭配react+es6+webpack热部署)

    reactjs学习一(环境搭配react+es6+webpack热部署) 本文的源码在这里下载 https://github.com/tianxiangbing/webpack-study   或者使 ...

  9. 每日一博 | 用 Ionic2 创建 App 启动页滑动欢迎界面

    原文  https://my.oschina.net/qinphil/blog/777787 效果如下,图片来自网络 本文例子和上图稍有不同,主要功能如下: 每滑动一下展示一张全屏图片: 滑动到最后一 ...

  10. ArcGis设置到 Oracle 的连接

    设置到 Oracle 的连接 地理数据 » 管理地理数据库 » Oracle 中的地理数据库 要建立从客户端计算机到 Oracle 数据库的连接,必须在客户端计算机上安装 Oracle 客户端应用程序 ...