二装饰器

Posted mengdie1978

tags:

篇首语:本文由小常识网(cha138.com)小编为大家整理,主要介绍了二装饰器相关的知识,希望对你有一定的参考价值。

二、****装饰器

在python中,装饰器以及前面讲到的迭代器,生成器都是十二分重要的高级函数。要想理解装饰器,首先要掌握以下几点准备知识:

1、准备一:回顾并理解作用域的相关知识

2、准备二:函数即对象

​ 在python的世界里,函数和我们之前的[1,2,3],\'abc\',8等一样都是对象,而且函数是最高级的对象(对象是类的实例化,可以调用相应的方法,函数是包含变量对象的对象)

def foo():
      print(\'i am the foo\')
      bar()
     
  def bar():     
      print(\'i am the bar\')
 
  foo()
  # def bar():      # bar函数定义放到foo()后边会报错
  #     print(\'i am the bar\')

带着上面的问题,我们看看函数在内存的存储情况,如下图:

​ 既然函数是对象,那么自然满足下面两个条件:函数对象的调用仅仅比其他对象多了一个()而已!foo,bar与a,b一样都是个变量名。那么上面示例的报错问题也就解决了,因为只有函数加载到内存才可以被调用。

    1)函数可以被赋给其他变量

def foo():
      print(\'foo\')
  bar=foo
  bar()
  foo()
  print(id(foo),id(bar))  #4321123592 4321123592

2)函数可以被定义在另外一个函数内(作为参数&作为返回值),类似于整型,字符串等对象

#*******函数名作为参数**********
  def foo(func):
      print(\'foo\')
      func()
 
  def bar():
      print(\'bar\')
 
  foo(bar)
 
  #*******函数名作为返回值*********
  def foo():
      print(\'foo\')
      return bar
 
  def bar():
      print(\'bar\')
 
  b=foo()
  b()

注意:这里说的函数都是指函数名,比如foo;而foo()表示已经执行函数了,foo()是什么类型取决于return的内容是什么类型!!!另外,如果你理解不了对象,那么就将函数理解成变量,因为函数对象总会由一个或多个变量引用,比如foo,bar。

3、准备三:函数的嵌套以及闭包

    python允许创建嵌套函数,通过在函数内部def的关键字再声明一个函数即为嵌套。先看下面这个函数嵌套的示例,分析能否正常执行:

def foo():
      print(\'foo\')
      def bar():
          print(\'bar\')
      # bar()    
  bar()   # 报错原因:找不到这个引用变量
  # 分析:没错,bar就是一个变量名,有自己的作用域的,因此bar()不能执行。

再看下面一个函数嵌套的示例,并思考执行inner函数的两种方法有什么区别:

def outer():
    x = 1
    def inner():
      print (x)  
    # inner()  # 方式一
    return inner  # 方式二
 
  # outer()  # 方式一
  in_func=outer()   # 方式二:这里其实就是一个变量赋值,将inner的引用对象赋值给in_func,类似于a=5,b=a一样
  in_func()

分析:两种调用方式有区别吗,不都是在外面调用inner吗?按照方式二调用后,inner()已经加载到内存了!

    回顾闭包的概念:如果在一个内部函数里,对在外部作用域(但不是在全局作用域)的变量进行引用,那么内部函数就被认为是闭包(closure)。

    如上示例,inner就是内部函数,inner里引用了外部作用域的变量x(x在外部作用域outer里面,不是全局作用域),则这个内部函数inner就是一个闭包。

4、装饰器的概念

    说了这么多,终于到装饰器了。装饰器本质上是一个函数,该函数用来处理其他函数,它可以让其他函数在不需要修改代码的前提下增加额外的功能,装饰器的返回值也是一个函数对象。它经常用于有切面需求的场景,比如:插入日志、性能测试、事务处理、缓存、权限校验等应用场景。装饰器是解决这类问题的绝佳设计,有了装饰器,我们就可以抽离出大量与函数功能本身无关的雷同代码并继续重用。概括的讲,装饰器的作用就是为已经存在的对象添加额外的功能。

    比如,业务生产中有如下一个大量调用的函数:

def add():
      for i in range(30000000):
          ret+=i
      print(ret)
  add()

现在有一个新的需求,希望可以记录下函数的执行时间,于是在函数中添加日志代码:

  总结:虽然实现了需求,但是这样做违反了开发的开放封闭原则的封闭原则,即不要轻易去改已经写好方法!而且如果要给其他函数也要加记录时间的代码,再用以上方法会造成代码冗余,所以想一想有没有其他方法。你可能会想到单独定义一个记录时间的函数,如下:

import time
  def show_time(func):
      start_time=time.time()
      func()
      end_time=time.time()
      print(\'spend %s\'%(end_time-start_time))
 
  def add():
      for i in range(30000000):
          ret+=i
      print(ret)
 
  show_time(add)

上面示例从逻辑上不难理解,而且运行正常。但是这样的话,之前执行业务逻辑时,执行运行add(),但是现在不得不改成show_time(add),这种方式就违反了开发的开放封闭原则的开放原则,即不要改动之前已有的调用方式。那么有没有更好的方式的呢?当然有,答案就是装饰器。 

5、简单装饰器

    如果,可以将上例中的add()=show_time(add),那么就可以在不违反开放封闭原则前提下完成需求了,所以,我们需要show_time(add)返回一个函数对象,而这个函数对象内则是核心业务函数:执行func()与装饰函数时间计算,修改如下:

import time
 
  def show_time(func):
        def inner():
            start_time=time.time()
            func()
            end_time=time.time()
            print(\'spend %s\'%(end_time-start_time))
        return inner
 
  def add():
        for i in range(30000000):
            ret+=i
        print(ret)
 
  add=show_time(add)
  add()

分析:函数show_time就是装饰器,它把真正的业务方法func包裹在函数里面,看起来像add被上下时间函数装饰了。在这个例子中,函数进入和退出时 ,被称为一个横切面(Aspect),这种编程方式被称为面向切面的编程(Aspect-Oriented Programming)。

    @符号是装饰器的语法糖,在定义函数的时候使用,避免再一次赋值操作,如下示例:

import time
 
  def show_time(func):
      def inner():
          start_time=time.time()
          func()
          end_time=time.time()
          print(\'spend %s\'%(end_time-start_time))
 
      return inner
   
  @show_time      # add=show_time(add)
  def add():
        for i in range(30000000):
            ret+=i
        print(ret) 
 
  @show_time    # bar=show_time(bar)
  def bar():
      print(\'in the bar\')
      time.sleep(2)
 
  add()
  bar()

如上所示,这样我们就可以省去add = show_time(add)这一句了,直接调用add()即可得到想要的结果。如果我们有其他的类似函数,我们可以继续调用装饰器来修饰函数,而不用重复修改函数或者增加新的封装。这样,我们就提高了程序的可重复利用性,并增加了程序的可读性。

    这里需要注意:add=show_time(add)其实是把inner引用的对象引用给了add,而inner里的变量func之所以可以用,就是因为inner是一个闭包函数。

    @show_time帮我们做的事情就是当我们执行业务逻辑foo()时,执行的代码由粉框部分转到蓝框部分,仅此而已!装饰器在Python使用如此方便都要归因于Python的函数能像普通的对象一样能作为参数传递给其他函数,可以被赋值给其他变量,可以作为返回值,可以被定义在另外一个函数内。

6、带参数的被装饰函数

import time
 
  def show_time(func):
      def inner(a,b):
          start_time=time.time()
          func(a,b)
          end_time=time.time()
          print(\'spend %s\'%(end_time-start_time))
 
      return inner
 
  @show_time   # add=show_time(add)
  def add(a,b):
      time.sleep(1)
      print(a+b)
 
  add(2,4)

7、注意点:当被装饰函数有返回值的情况

import time

  def show_time(func):
      def inner(a,b):
          start_time=time.time()
          ret=func(a,b)
          end_time=time.time()
          print(\'spend %s\'%(end_time-start_time))
          return ret   # 若被装饰函数有返回值,则inner函数也一定要返回func(a,b)的结果

      return inner

  @show_time   # add=show_time(add)
  def add(a,b):
      time.sleep(1)
      return a+b

  print(add(2,5))

8、装饰器在登录认证方面的应用:

Django为我们提供了一个装饰器函数login_required,当我们只想限制少量页面登录才能访问时,就可以使用装饰器函数,而没有必要加中间件。

三、****补充

1、使用django的admin页面录入数据

  1)首先,在admin.py中配置如下信息:

from django.contrib import admin
  # Register your models here.
  from app01.models import *    # 将models模块中的所有类(表)引入

  admin.site.register(UserInfo)    # 括号中是model中的类(表),下面也如此
  admin.site.register(Customer)
  admin.site.register(Campuses)
  admin.site.register(ClassList)

2)创建超级用户

python manage.py createsuperuser

3)以超级用户登录admin后台,开始录入数据

2、安装multiselectfield

方式一:

  pip3 install multiselectfield

  方式二:pycharm软件上按如下操作

    file -> settints -> Project:xxx -> Project Interpreter -> 右侧绿色加号 -> 搜索multiselectfield ->Install Package

  解释:我们之前学过gender=models.CharField(choices=((\'male\',\'男\')(\'female\',\'女\'))),使用modelform会渲染成多个type为radio的input标签,model中定义course = MultiSelectField("咨询课程", choices=((\'Linux\', \'Linux中高级\'),(\'PythonFullStack\', \'Python高级全栈开发\'),), blank=True, null=True),使用modelform会渲染成多个type为checkbox的input标签。

3、blank=True

qq_name = models.CharField(\'QQ昵称\', max_length=64, blank=True, null=True)

解释:null=True表示数据库中不能为空,blank=True表示使用modelform组件中校验该字段输入时不能为空,而只写null=True则不能校验。Django的admin也是基于modelform实现的,所以使用admin提交表单数据时,只写了null=True而没有写blank=True的则必须输入值,不能为空。

4、model中模型类的__str__方法必须返回字符串类型,不能返回其他类型。

5、location的search属性

search 属性是一个可读可写的字符串,可设置或返回当前 URL 的查询部分(即问号 ? 之后的部分)

6、javascript的slice()方法

语法:string.slice(start,end)

  解释:提取字符串的某个部分,并以新的字符串返回被提取的部分,索引从0开始,顾头不顾尾。

第九篇 装饰器

 

一 什么是装饰器

器即函数

装饰即修饰,意指为其他函数添加新功能

装饰器定义:本质就是函数,功能是为其他函数添加新功能

 

二 装饰器需要遵循的原则

1.不修改被装饰函数的源代码(开放封闭原则)

2.为被装饰函数添加新功能后,不修改被修饰函数的调用方式

 

三 实现装饰器知识储备

装饰器=高阶函数+函数嵌套+闭包

 

四高阶函数

高阶函数定义:
1.函数接收的参数是一个函数名

2.函数的返回值是一个函数名

3.满足上述条件任意一个,都可称之为高阶函数

高阶函数总结
1.函数接收的参数是一个函数名
  作用:在不修改函数源代码的前提下,为函数添加新功能,
  不足:会改变函数的调用方式
2.函数的返回值是一个函数名
  作用:不修改函数的调用方式
  不足:不能添加新功能

 

五 函数嵌套

技术分享
 1 def father(name):
 2     print(‘from father %s‘ %name)
 3     def son():
 4         print(‘from son‘)
 5         def grandson():
 6             print(‘from grandson‘)
 7         grandson()
 8     son()
 9 
10 father(‘林海峰‘)
技术分享
 

六 闭包

技术分享
 1 ‘‘‘
 2 闭包:在一个作用域里放入定义变量,相当于打了一个包
 3 ‘‘‘
 4 def father(name):
 5     def son():
 6         # name=‘alex‘
 7         print(‘我爸爸是 [%s]‘ %name)
 8         def grandson():
 9             # name=‘wupeiqi‘
10             print(‘我爷爷是 [%s]‘ %name)
11         grandson()
12     son()
13 
14 father(‘林海峰‘)
技术分享
 

七 无参装饰器

无参装饰器=高级函数+函数嵌套

基本框架

1 #这就是一个实现一个装饰器最基本的架子
2 def timer(func):
3     def wrapper():
4         func()
5     return wrapper

加上参数

1 def timer(func):
2     def wrapper(*args,**kwargs):
3         func(*args,**kwargs)
4     return wrapper

加上功能

技术分享
1 import time
2 def timer(func):
3     def wrapper(*args,**kwargs):
4         start_time=time.time()
5         func(*args,**kwargs)
6         stop_time=time.time()
7         print(‘函数[%s],运行时间是[%s]‘ %(func,stop_time-start_time))
8     return wrapper
技术分享

加上返回值

技术分享
1 import time
2 def timer(func):
3     def wrapper(*args,**kwargs):
4         start_time=time.time()
5         res=func(*args,**kwargs)
6         stop_time=time.time()
7         print(‘函数[%s],运行时间是[%s]‘ %(func,stop_time-start_time))
8         return res
9     return wrapper
技术分享

使用装饰器

技术分享
1 def cal(array):
2     res=0
3     for i in array:
4         res+=i
5     return res
6 
7 cal=timer(cal)
8 cal(range(10))
技术分享

语法糖@

技术分享
1 @timer  #@timer就等同于cal=timer(cal)
2 def cal(array):
3     res=0
4     for i in array:
5         res+=i
6     return res
7 
8 cal(range(10))
技术分享
 

 








以上是关于二装饰器的主要内容,如果未能解决你的问题,请参考以下文章

四 闭包函数装饰器

第九篇 装饰器

第九篇 装饰器

Python小程序练习二之装饰器小例子

遥想大肠包小肠----python装饰器乱弹

TypeScript(21): 装饰器