闭包函数和装饰器

闭包函数

闭包函数的概念

  当一个函数的返回值是另外一个函数,而返回的那个函数如果调用了其父函数内部的其它变量,如果返回的这个函数在外部被执行,就产生了闭包
  
  闭包函数成立的条件:
    1 在一个外函数中定义了一个内函数

    2 内函数里运用了外函数的临时变量

    3 并且外函数的返回值是内函数的引用

  闭包函数的作用:函数的内部函数,对外部作用域,而非全局作用域的引用。
  可以打破层级关系,把局部变量拿到全局使用,并且可以把外部的变量封装到内函数中,然后下次直接调用内函数就行了
  
   若一个内部函数引用了其外部函数的变量,而且该内部函数被当成对象返回,此时就产生了闭包效果,该内部函数就成了一个闭包函数。闭包函数无论在哪被引用,都优先使用其自己的私有变量(其外部包裹函数中
的局部变量,此时这些局部变量只能被该闭包函数使用,在其他地方都是无效的,所以自然就成了该闭包函数的私有变量)


    ps:全局中相同名字的变量不会影响局部的变量。
  
    例一:
      x = 10
      def f1(x): # f1()== f2
          # x = 2
          def f2():    
              print(x)
          return f2 # 把f2函数当做一个返回值返回给f1.
      f2 =f1(1) # f1()就相当于把f2的值拿到,然后在赋值给一个变量名为f2。
      f2()

装饰器

装饰器简介

  装饰器:其实装饰器就是一个闭包,装饰器是闭包的一种应用。什么是装饰器呢,简言之,python装饰器就是用于拓展原来函数功能的一种函数,这个函数的特殊
之处在于它的返回值也是一个函数,使用python装饰器的好处就是在不用更改原函数的代码前提下给函数增加新的功能。

  写代码需遵循一个原则,即开放封闭原则:对已实现的功能代码块封闭,不让修改;对扩展开发开放,允许扩展

  装饰器就是这么一种谨遵开放封闭原则的开发工具,装饰器本质上是一个闭包函数

装饰器的原型


  为f3加上一个记录运行时间的功能

  import time # 导入时间模块
  def f1(func): # func = f3
      def f2():
          t1 = time.time()
          func() # 相当于f3()
          t2 = time.time()
          print(t2 - t1)
      return f2

  def f3():
      print('春游去动物园')
      time.sleep(1)
  f3 = f1(f3) # f1(f3) = f2
  f3() # f2()

不带参数的装饰器:(装饰器,被装饰函数都不带参数)

  import time
  def f1(func):
      def f2():
          t1 = time.time()
          func()
          t2 = time.time()
          print(t2 - t1)
      return f2
  @f1
  def f3():
      print('春游去动物园')
      time.sleep(1)
  f3()

带参数的被装饰的函数

  import time
  def f1(func):
      def f2(*args,**kwargs):
          t1 = time.time()
          func(*args,**kwargs)
          t2 = time.time()
          print(t2 - t1)
      return f2
  @f1
  def f3(*args,**kwargs):
      print('春游去动物园')
      time.sleep(1)
  f3(1,2,3)

带参数的装饰器(装饰函数)

  实际是对原有装饰器的一个函数的封装,并返回一个装饰器(一个含有参数的闭包函数)
  当使用@f4(0)调用的时候,Python能发现这一层封装,并将参数传递到装饰器的环境去
  import time
  def f4(flag = 0):
      def f1(func):
          def f2(*args,**kwargs):
              t1 = time.time()
              func(*args,**kwargs)
              t2 = time.time()
              print(t2 - t1)
              print(flag)
          return f2
      return f1
  @f4(0)
  def f3(*args,**kwargs):
      print('春游去动物园')
      time.sleep(1)
  f3(1,2,3)

有返回值的被修饰函数

  import time
def f1(func):
    def f2(*args, **kwargs):
        t1 = time.time()
        res = func(*args, **kwargs) # 用变量res接收f3的返回值
        t2 = time.time()
        print(t2 - t1)
        print(res)
    return f2
@f1
def f3(*args, **kwargs):
    print('春游去动物园')
    time.sleep(1)
    return '返回值'
f3(1, 2, 3)

装饰器模板

  '''编写装饰器其实有一套固定的代码 不需要做任何理解'''
def outer(func_name):  # func_name用于接收被装饰的对象(函数)
    def inner(*args, **kwargs):
        print('执行被装饰函数之前 可以做的额外操作')
        res = func_name(*args, **kwargs)  # 执行真正的被装饰函数
        print('执行被装饰函数之后 可以做的额外操作')
        return res  # 返回真正函数的返回值
    return inner

装饰器语法糖

  import time  
  def f1(func):  
      def f2():
          t1 = time.time()
          func() 
          t2 = time.time()
          print(t2 - t1)
      return f2
  def f3():
      print('春游去动物园')
      time.sleep(1)
  f3 = f1(f3)
  f3()

  有人觉得这样的写法f3 = f1(f3)未免太过麻烦,于是python提供了一种更优雅的写法,被称为语法糖:
  使用了语法糖后:
  import time
  def f1(func):
      def f2():
          t1 = time.time()
          func()
          t2 = time.time()
          print(t2 - t1)
      return f2
  @f1
  def f3():
      print('春游去动物园')
      time.sleep(1)
  f3()

装饰器修复技术

  Python装饰器(decorator)在实现的时候,被装饰后的函数其实已经是另外一个函数了(函数名等函数属性会发生改变)
  为了不影响,Python的functools包中提供了一个叫wraps的decorator来消除这样的副作用。写一个decorator的时候,最好在实现之前加上functools的wrap,
  它能保留原有函数的名称和docstring。

  未加@wraps的时候:
  def wrapper(func):
    def inner(*args, **kwargs):
        print("装饰器工作中...")
        func(*args, **kwargs)
    return inner
  @wrapper
  def f1(arg):
      """
      这是一个测试装饰器修复技术的函数
      :param arg: 随便传
      :return: 没有
      """
      print(arg)
  f1('呵呵')
  print(f1.__name__,f1.__doc__)
  运行结果:
  装饰器工作中...
  呵呵
  inner None # 返回的是装饰器名

  加@wraps的时候:
  from functools import wraps
  def wrapper(func):
      @wraps(func)
      def inner(*args, **kwargs):
          print("装饰器工作中...")
          func(*args, **kwargs)
      return inner
  @wrapper
  def f1(arg):
      """
      这是一个测试装饰器修复技术的函数
      :param arg: 随便传
      :return: 没有
      """
      print(arg)
  f1('呵呵')
  print(f1.__name__,f1.__doc__)
  装饰器工作中...
  呵呵
  f1 # 返回的函数名是原函数名
      这是一个测试装饰器修复技术的函数
      :param arg: 随便传
      :return: 没有


  如果没使用@wraps,当A调用了装饰器B的话,即使A.name,返回的会是装饰器B的函数名称,而不是A的函数名称
  如果使用了@wraps,当A调用了装饰器B的话,A.__ name__返回的会是A函数的名称,而不是饰器B的名称,
  这也即使常说的@wraps是装饰器的修复技术,
  实际就是修复还原了A的__ name__变量,同理__ doc__变量也是一样。

今日作业

  1.编写一个用户认证装饰器
	基本要求
  	执行每个函数的时候必须先校验身份 eg: jason 123
        拔高练习(有点难度)
  	执行被装饰的函数 只要有一次认证成功 那么后续的校验都通过
    	register login transfer withdraw 
    提示:全局变量 记录当前用户是否认证
from functools import wraps

id_login = [0]


def getinfo():
    username = input('请输入你的用户名: ').strip()
    password = input('请输入你的密码: ').strip()
    return username, password


def wrapper(func):
    @wraps(func)
    def inner(*args, **kwargs):
        if id_login[0] == 1:
            func()
        else:
            print('请先进行身份验证')
            username, password = getinfo()
            a = (username, password)
            for i in user_dict.items():
                if a == i:
                    id_login[0] = 1
                    print('验证成功')
                    break
            else:
                print('验证失败')

    return inner


@wrapper
def selectinfo():
    for i in user_dict.items():
        print(f'''
        -------------
        账号:{i[0]}
        密码:{i[1]}
        -------------
        ''')


@wrapper
def delete():
    username = input('请输入你要删除的用户名: ').strip()
    user_dict.pop(username)
    print(f'''
    成功删除用户 {username} 
    ''')


@wrapper
def updata():
    username = input('请输入你要修改的用户名: ').strip()
    password = input('请输入要修改的密码: ').strip()
    user_dict[username] = password
    print('密码修改成功')


@wrapper
def offlogin():
    id_login[0] = 0
    print('已退出登入')


func_dic = {
    '1': selectinfo,
    '2': delete,
    '3': updata,
    '4': offlogin,
}
while True:
    print('''
    1.查看所有用户
    2.删除用户
    3.修改密码
    4.退出登入
    5.退出
    ''')
    chioce = input('请输入指令:').strip()
    if chioce in func_dic.keys():
        func_dic[chioce]()
    elif chioce == '5':
        break
    else:
        print('请输入真确的指令')
posted @ 2022-03-18 18:56  春游去动物园  阅读(36)  评论(0编辑  收藏  举报