闭包:

  如果在一个函数的内部定义了另一个函数,外部的函数我们叫它外函数,内部的我们叫它内函数。

  在一个外函数中定义了一个内函数,内函数里运用了外函数的临时变量,并且外函数的返回值是内函数的引用。这样就构成了一个闭包。

  下面我们来看一段代码:

 #闭包函数的实例
  # outer是外部函数 a是外函数的临时变量
  def outer( a ):
      # inner是内函数
      def inner( b ):
          #在内函数中用到了外函数的临时变量
          print(a+b)
      # 外函数的返回值是内函数的引用
     return inner

     # 在这里我们调用外函数传入参数5,此时a为5
     # 外函数结束的时候发现内部函数将会用到自己的临时变量,这个临时变量 a就不会释放,会绑定给这个内部函数
     demo1 = outer(5)

     # 我们调用内部函数,看一看内部函数是不是能使用外部函数的临时变量
     # demo存了外函数的返回值,也就是inner函数的引用,这里相当于执行inner函数
     demo1(1)

     demo2 = outer(10)
     demo2()
View Code

注意点:

1、 内函数绑定外函数临时变量:

  一般情况下,如果一个函数结束,函数的内部所有东西都会释放掉,还给内存,局部变量都会消失。但是闭包是一种特殊情况,如果外函数在结束的时候发现有自己的临时变量将来会在内部函数中用到,就把这个临时变量绑定给了内部函数,然后自己再结束。所以外函数已经结束了,调用内函数的时候仍然能够使用外函数的临时变量。

  我们调用外部函数outer,分别传入的值是5和7。虽然内函数我们只定义了一次,但是外函数在运行的时候,它都创建一个内函数,并且把每次传入的临时变量数值绑定给内函数,再返回不同内函数引用。

  所以多次使用相同外函数返回的内函数引用,虽然每次调用内函数,但是闭包变量实际上只有一份,每次开启内函数都在使用同一份闭包变量

2、内函数修改外函数临时变量:

  在闭包内函数中,我们可以随意使用外函数绑定来的临时变量,但是如果我们想修改外函数临时变量数值的时候发现出问题了。

  在python当中,一个函数可以随意读取全局数据,但是要修改全局数据的时候有两种方法(在闭包内函数也是类似的情况):

    1)  global 声明的全局变量

    2)  全局变量是可变类型数据的时候可以修改

  在python3中,可以用nonlocal 关键字声明 一个变量, 表示这个变量不是局部变量空间的变量,需要向上一层变量空间找这个变量; 在python2中,没有nonlocal这个关键字,我们可以把闭包变量改成可变类型数据进行修改,比如列表。

 

装饰器:

  先来看一个场景:客户提出新需求,对已有函数,加入函数执行时间消耗功能。

import time
def session():
    time.sleep(1)

def session ():
        begin  =  time.time()
        func()
        end =  time.time()
        print(end - begin)
View Code

  当有成千上万个函数,怎么做?分别改写每个函数,造成大量雷同代码、人力财力物力消耗?为了增加代码复用性,我们重新定义一个函数,

import time

def show_time(func):
        begin  =  time.time()
        func()
        end =  time.time()
        print(end - begin)

def session():
    time.sleep(1)
 
show_time(session)
View Code

       但是这样的话,我们每次都要将一个函数作为参数传递给show_time函数。而且这种方式已经破坏了原有的代码逻辑结构,之前执行业务逻辑时,执行运行session (),但是现在不得不改成show_time(session)。

  那么有没有更好的方式的呢?当然有,答案就是装饰器。

import time

def session():
    time.sleep(1)

def show_time(func):
    def inner():
        begin  =  time.time()
        func()
        end =  time.time()
        print(end - begin)
    return inner
session = show_time(session)

session()
View Code

  函数show_time就是装饰器,它把真正的业务方法session包裹在里面,看起来像session被上下时间函数装饰了。

  在python中提供了更简单的写法,在方法session前面加上@show_time @show_time帮我们做的事情就是当我们执行业务逻辑session 时,自动去找到show_time装饰器去装饰session,相当于bar=show_time(bar)

import time

def show_time(func):
    def inner():
        begin  =  time.time()
        func()
        end =  time.time()
        print(end - begin)
    return inner

@show_time
def session():
    time.sleep(1)

session()
View Code

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

1、带参数的被装饰函数

import time

def show_time(func):
    def inner(a,b):
        begin  =  time.time()
        func(a,b)
        end =  time.time()
    return inner
 
@show_time
def session(a,b):#可变参数怎么写?
    print(a+b)

session(2,3)
View Code

2、带参数的装饰器

import time

 
def log_showtime(flag):
    def show_time(func):
        def inner():
            func()
            if flag == True:
                print("i am time")
            else:
                print("i am log")
        return inner
    return show_time

@log_showtime(True)
def session():
    print(True)

session()
View Code

  上面的log_showtim是允许带参数的装饰器。它实际上是对原有装饰器的一个函数封装,并返回一个装饰器。我们可以将它理解为一个含有参数的闭包。当我们使用@ log_showtim (True)调用的时候,Python能够发现这一层的封装,并把参数传递到装饰器的环境中。

 

posted on 2018-03-05 21:32  LiubeibeiLiu  阅读(127)  评论(0)    收藏  举报