• Python 基础4 闭包与装饰器


    ==闭包 closure:

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

      闭包是指调用了此函数外部嵌套函数作用域变量的函数;

      闭包必须满足三个条件:(注意闭包不释放变量,有内存消耗)
        1.必须有内嵌函数;
        2.内嵌函数必须引用外部函数中的变量;
        3. 外部函数返回值必须是内嵌函数

    示例closure.py
    #问题:
    #如何写一个函数,让此函数能通过调用参数y就能生成一个x的y次方的函数?
    #如下:
    def make_power(y):
        def fn(x):    #注意此处为内嵌函数
            return x ** y    #内嵌函数引用外部嵌套函数的变量y
        return fn    #外部函数返回值 为内嵌函数
    
    pow2 = make_power(2)  #此处返回的是fn函数对象
    print('5的平方是:', pow2(5))    #25  
    pow3 = make_power(3)
    print('6的3次方是:', pow3(6))   #216
    #一元二次方程式,已知x,求y值?
    #a * x ** 2 + b * x + c = y
    
    def get_fx(a, b, c):
        def fx(x):
            return a * x ** 2 + b * x ** 1 + c
        return fx
    
    f123 = get_fx(1, 2, 3)
    print('x=20, y=', f123(20))
    print('x=50, y=',f123(50))
    
    f654 = get_fx(6, 5, 4)
    print('x=20, y=', f654(20))

    ==装饰器decorators (专业提高篇)
      问题:
        函数名是变量,它绑定一个函数;
        函数名 与 函数() 区别: 不加括号表示函数名变量绑定的函数;
        加括号表达调用函数做事;
      什么是装饰器:
        装饰器是一个函数,主要作用是用来包装另一个函数或类(后面讲)

        装饰器本质上是一个函数,该函数用来处理其他函数,它可以让其他函数在不需要修改代码的前提下增加额外的功能,装饰器的返回值也是一个函数对象。它经常用于有切面需求的场景,比如:插入日志、性能测试、事务处理、缓存、权限校验等

        应用场景。装饰器是解决这类问题的绝佳设计,有了装饰器,我们就可以抽离出大量与函数功能本身无关的雷同代码并继续重用。概括的讲,装饰器的作用就是为已经存在的对象添加额外的功能

      装饰器作用:
        是在不改变原函数名(或类名)的情况下改变被包装对象的行为;
        在不改变原函数和调用者行为的情况下,来改变原有函数功能;

     函数装饰器:
        函数装饰器是指装饰器是一个函数,传入的是一个函数,返回的也是一个函数;

      语法:
        def 装饰器函数名(参数):
          语句块
          return 函数对象

        @装饰器函数名<换行>
        def 函数名(形参列表):
          语句块

    import time
    
    #遵守开放封闭原则
    #需求计算功能函数foo、bar运行时间
    def show_time(f):  #装饰器函数
        def inner():
            start = time.time()
            f()
            end = time.time()
            print('spend %s' % (end - start))
        return inner    #返回inner函数的内存地址
    
    @show_time  #等价于 foo = show_time(foo)
    def foo():  #功能函数
        print('foo...')
        time.sleep(2)
    
    @show_time  #等价于 bar = show_time(bar)
    def bar():
        print('bar....')
        time.sleep(3)
    
    
    foo()
    bar()

     

    # 示例见: mydeco1.py
    # ==================================
    def mydeco(fn):
        def fx():
            print('fx函数被调用')
        return fx
    
    def myfunc():
        print('函数myfunc被调用')
    
    #这样的写法可以用装饰器来代替
    #等同于
    #    @mydeco
    #    def myfunc()......
    myfunc = mydeco(myfunc)  
    myfunc()
    # ==================================
    # 修改后如下
    # ==================================
    def mydeco(fn):
        def fx():
            print('fx函数被调用')
        return fx
    
    #myfunc加了mydeco装饰器,等同于在myfunc创建之后调用赋值myfunc = mydeco(myfunc)语句 
    @mydeco  #等价于 myfunc = mydeco(myfunc)
    def myfunc():
        print('函数myfunc被调用')
    
    myfunc()
    # =======================================
    
    #装饰器应用示例
    def mydeco(fn):
        def fx():
            print('++++++++++++')
            #要想在此处调用被装饰的函数myfunc怎么办?
            fn()    #调用被装饰函数
            print('------------------------')
        return fx
    
    ##添加装饰器,myfunc函数被装饰(@mydeco 等介于 myfunc = mydeco(myfunc) 代码)
    @mydeco  #等价于 myfunc = mydeco(myfunc)
    def myfunc():    
        print('函数myfunc被调用')
    
    myfunc()

    #看懂下面代码的调用关系及打印结果:
    def mydeco5(fn):
        print('装饰器函数被调用了....')    #在第一次赋值时myfunction = mydeco5(myfunction)调用
        def fx():
            print('fx函数被调用')
        return fx
    
    @ mydeco5  #这里加装饰器和不加装饰器结果一样吗?为什么?
    def myfunction():
        print('函数myfunction被调用')
    
    myfunction()
    myfunction()    #调用第二次
    myfunction()    #调用第三次
    ========================
    #mydeco5.py   示例
    #此示例示意装饰器在不改变原函数和调用者行为的情况下,来改变原有函数功能;
    #再加一个装饰器用来添加余额变动提醒功能
    #写一个装饰器函数用来发送短信
    def send_message(fn):
        def fy(name, x):
            fn(name, x)    #先办业务
            print('发短信给', name, '办理了', x, '')
        return fy
    
    #写一个装饰器函数
    def privillage_check(fn):
        def fx(name, x):
            print('正在检查权限...')
            fn(name, x)    #如果权限通过调用相应函数
        return fx
    
    #写一个操作数据的函数(此函数用来示意存钱操作)
    #
    #注意此位置两层装饰器,层极关系是上到下层,层层嵌套调用
    #(关系为:send_message在外层->privillage_check->savemoney在里层)
    @send_message
    @privillage_check
    def savemoney(name, x):
        print(name, '存钱', x, '')
    #
    @privillage_check
    def withdraw(name, x):
        print(name, '取钱', x, '')
    #
    savemoney('小张', 200)
    savemoney('小赵', 200)
    
    withdraw('小李', 500)
    =========================

    带参数的装饰器

        装饰器还有更大的灵活性,例如带参数的装饰器:在上面的装饰器调用中,比如@show_time,该装饰器唯一的参数就是执行业务的函数。装饰器的语法允许我们在调用时,提供其它参数,比如@decorator(a)。这样,就为装饰器的编写和使用提供了更大的灵活性。

     1 import time
     2  
     3 def time_logger(flag=0):
     4  
     5     def show_time(func):
     6  
     7             def wrapper(*args,**kwargs):
     8                 start_time=time.time()
     9                 func(*args,**kwargs)
    10                 end_time=time.time()
    11                 print('spend %s'%(end_time-start_time))
    12  
    13                 if flag:
    14                     print('将这个操作的时间记录到日志中')
    15  
    16             return wrapper
    17  
    18     return show_time
    19  
    20  
    21 @time_logger(3)
    22 def add(*args,**kwargs):
    23     time.sleep(1)
    24     sum=0
    25     for i in args:
    26         sum+=i
    27     print(sum)
    28  
    29 add(2,7,5)

      @time_logger(3) 做了两件事:

          (1)time_logger(3):得到闭包函数show_time,里面保存环境变量flag

          (2)@show_time   :add=show_time(add)

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

     1 实例:
     2 import os
     3 import sys
     4 
     5 # 京东网站有以下home 、finance、book几个版块
     6 # home()
     7 # finance()
     8 # book()
     9 # 打开会选展示home首页
    10 # 当在选择finance页面时,需要检测有没有登录,没有登录就调用登录验证接口,要求用weixin方式验证
    11 # 当选择book页面时,需要检测有没有登录,没有登录就调用登录验证接口,要求用jidong方式验证
    12 
    13 #文件读取用户名与密码函数
    14 def read_info(filename):
    15     local_path = sys.path[0]
    16     # 拼接localpath与filename
    17     localxmlpath = os.path.join(local_path, filename)
    18     # open文件,按行读取,返回content1列表
    19     with open(localxmlpath, 'r') as f1:
    20         content1 = f1.readlines()
    21     return content1
    22 
    23 user_status = False #定义变量记录用户登录状态,用户登录后把这个修改为True
    24 
    25 def login(auth_type):   #把要选择的认证方式通过auth_type传入
    26     def auth(func):
    27         def inner(*args):   #再定义一层函数
    28             print('+++++ %s +++++++' % (func))
    29             pass_list = read_info(auth_type+'.txt') #根据auth_type拼接需要读取的文件名
    30             _username = pass_list[0].rstrip('
    ')   #读取的用户名,去除字符串最后的'
    '字符
    31             _password = pass_list[1]                #读取密码
    32 
    33             global user_status      #声明记录用户状态的变量为全局变量
    34 
    35             if not user_status:
    36                 username = input('username:')
    37                 password = input('password:')
    38 
    39                 if username == _username and password == _password:
    40                     print('成功登录,welcome!!!')
    41                     user_status = True  #登录后改变登录状态标识为true
    42                     func(*args)         #验证通过后,执行功能函数
    43                 else:
    44                     print('woring username or passwd!')     #验证未通过提示错误信息
    45             else:
    46                 print('用户已经登录,无需验证')
    47                 func(*args)
    48         return inner    #返回内部函数的内存引用id
    49     return auth
    50 
    51 
    52 def home():
    53     print('welcom to home page')
    54 
    55 @login('weixin') #1、带入weixin参数给auth_type变量,2、finance = auth(finance)
    56 def finance():
    57     print('welcom to finance page')
    58 
    59 @login('jidong')    #1、带入jidong参数给auth_type变量,2、book = auth(book)
    60 def book(style):
    61     print('welcom to book page')
    62 
    63 
    64 
    65 home()
    66 
    67 
    68 book('3x')
    69 
    70 finance()

    多层装饰器:

    #定义函数:完成包裹数据
    def makeBold(fn):
        def wrapped():
            print('----1----')
            return '<b>' + fn() + '</b>'
        return wrapped
    
    #定义函数:完成包裹数据
    def makeItalic(fn):
        def wrapped():
            print('----2----')
            return '<i>' + fn() + '</i>'
        return wrapped
    
    @makeBold
    @makeItalic
    def test3():
        print('------3-----')
        return 'hello world.3'
    
    ret = test3()
    print(ret)
    
    #执行结果如下:
    ----1----
    ----2----
    ------3-----
    <b><i>hello world.3</i></b>

    ==函数的文档字符串:
      函数内第一次末赋值给任何变量的字符串是此函数的文档字符串
        语法:
        def 函数名(形参列表):
        '''函数的文档字符串'''
          函数语句块

      示例:
        def hello():
          '''此函数来打招呼
          这是函数的文档字符串
          '''
          pass
      说明:
        1. 文档字符串通常用来说明本函数的功能和使用方法;
        2. 在交互模式下,输入help(函数名),可以查看函数的文档字符串;
        3. 文档字符串需要被解析执行器解析并绑定变量的,注释是被解析执行器忽略的。

      函数的__doc__属性:
        __doc__属性用于记录文档字符串;
      函数的__name__属性:
        __name__属性用于记录函数的名称;

    ==函数的定义语法:
        @装饰器1
        @装饰器2
        ......
        def 函数名([[[[位置形参],*元组形参(或*)], 命名关键字形参], **字符形参])
          '''文档字符串'''
          语句块

    # 面试题,思考?
    L = [1, 2, 3]
    def f(n=0, lst=[]):    #注意此处不是每次函数调用赋值,而lst列表是存在于函数内部,生命周期与函数一致
        lst.append(n)
        print(lst)
    
    f(4, L)        #打印结果是什么?[1, 2, 3, 4]
    f(5, L)        #打印结果是什么?[1, 2, 3, 4, 5]
    f(100)        #[100]
    f(200)        #打印结果是什么?为什么?[100, 200]
    
    #如下代码的打印结果是什么?
    L = [1, 2, 3]
    def f(n = 0, lst = None):
        if lst is None:
            lst = []    #此处是调用时赋值
        lst.append(n)
        print(lst)
    
    f(4, L)        #打印结果是什么?[1, 2, 3, 4]
    f(5, L)        #打印结果是什么?[1, 2, 3, 4,5]
    f(100)         #[100]
    f(200)         #打印结果是什么?为什么?[200];因为调用函数时,未带入列表;函数lst默认参数为None,if判断会对lst赋值为[];
  • 相关阅读:
    按字母分类的产品组件
    container中的内容 垂直-水平居中
    安卓手机--键盘谈起后 fixed背景图片被键盘顶起的问题
    清除样式的css
    vue 路由(二级)配置及详细步骤
    vue 路由 URL传参
    路由表的组件群
    vue 路由传参
    vue 路由入门(vue-router)
    jQuery对文档的操作
  • 原文地址:https://www.cnblogs.com/pineliao/p/12097175.html
Copyright © 2020-2023  润新知