zoukankan      html  css  js  c++  java
  • Python之装饰器

    1、什么是闭包

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

    只要是闭包,就一定会有 .__closure__ 方法,查看闭包元素,且结果不为 None

    .__closure__[0].cell_contents 查看第一个元素

    例1:
    def outer():
        x=10
        def inner():  #条件一:inner 就是内部函数
            print(x)  #条件二:引用外部环境的一个变量
        return inner #结论:内部函数 inner就是一个闭包
    
    f=outer()   # 执行到 return innser 结束,return inner 返回 inner 的内存地址
    print(f)  #  <function outer.<locals>.inner at 0x0000000001060268>
    f()  # f() 装的是 Inner 内存地址指向的对象 print(x) = 10
    
    
    例2:
    def foo():
        print('foo')
        return bar
    
    
    def bar():
        print('bar')
    
    # foo()
    b = foo()  #先返回 foo 内存地址的指向对象 print('foo'),好包含 bar 的内存地址
    b() # foo 已经执行完了,剩下的 bar 内存地址指向的对象,print('bar')
    闭包例子

    闭包的意义:

    返回的函数对象,不仅仅是一个函数对象,在该函数外还包裹了一层作用域,这使得,该函数无论在何处调用,优先使用自己外层包裹的作用域

    #应用领域:延迟计算(原来我们是传参,现在我们是包起来)

     from urllib.request import urlopen
    
    def index(url):
        def get():
            return urlopen(url).read()
        return get
    
    baidu=index('http://www.baidu.com')
    print(baidu().decode('utf-8'))
    View Code

    2、什么是装饰器

    理解:

      装饰器:我在知乎看到这样一个比方(我们的函数好比内裤,作用是遮羞。但在一些特定的环境,内裤明显满足不了我们的需求,冬天它没法为我们防风御寒。所以有了长裤,装饰器就像长裤,在不影响内裤作用的前提下,给我们的身子提供了保暖的功效)

    装饰器是对原函数的一种扩展,本质是一个函数,在原函数不需要做任何代码变动的前提下增加额外功能。装饰器返回的也是一个函数对象。

    场景:插入日志、性能测试、事务处理、权限校验等应用广泛。

    遵守封闭开放原则:对源代码修改封闭,对功能外部增加开放。

     功能函数无参数

    import  time
    def timmer(func):
        def warpper(name):
            start_time=time.time()
            func(name)  # home(name)   不给参数,执行 home(name) 会报 home() 的参数传递错误
            stop_time=time.time()
            print('run time is %s'%(stop_time-start_time))
        return warpper
    
    @timmer   # home=timmer(home),home 拿到一个 warpper 的内存地址,调用 home 的时候就是执行 warpper
    def home(name):
        time.sleep(2)
        print('welcome to %s home page'%name)
    
    home('dragon') # 实际上是运行 timmer 里的 warpper(name),所以 warpper 也要给个参数,不然会报 warpper的参数错误
    简单装饰器例1
    import time
    def timmer(func):
        def wrapper(*args,**kwargs):
            start_time=time.time()
            res=func(*args,**kwargs)  #先执行 my_max(1,2),res=2,my_max 是有一个执行结果的
            stop_time=time.time()
            print('run time is %s' %(stop_time-start_time))
            return res
        return wrapper
    
    
    @timmer
    def my_max(x,y):
        print('my_max function')
        res=x if x > y else y
        return res  # 这里的 res 被 warpper 里的 res 接收了
    
    res=my_max(1,2) #res=wrapper(1,2)
    print('=====>',res)
    简单装饰器例2
    def show_time(f):
        start_time=time.time()
        f()
        end_time=time.time()
        print('spend %s'%(end_time-start_time))
    
    def bar():
        print('bar...')
        time.sleep(3)
    
    show_time(bar)
    #问题:完成需求,但改变了调用方式,原本是  bar(),现在是 show_time(bar)
    
    
    #解决方法:
    def show_time(f):    #这个就是装饰器函数,装饰下面的 bar 函数,作为一个bar函数的扩展
        def inner(): # 当执行 show_time(f) 函数时,把 inner 函数放到内存,拿到 inner 的内存地址
            start_time = time.time()
            f()
            end_time = time.time()
            print('spend %s' % (end_time - start_time))
        return inner # inner 的内存地址指向的是 print('spend %s' % (end_time - start_time))
    
    def bar():
        print('bar...')
        time.sleep(3)
    
    show_time(bar)
    bar=show_time(bar)  # 拿到的只是 print(bar') 和 Inner 的内存地址
    bar() # 执行 inner 函数,调用 inner 内存指向的内容
    
    #结论:解决上面的问题,在不改变的调用的方式,扩展函数的功能
    
    #调用方式的优化:@+函数名会把下面函数名当做参数传给@函数执行
    @show_time   # 等于 bar=show_time(bar)
    def bar():
        print('bar...')
        time.sleep(3)
    
    bar()
    在 bar 函数的基础上增加调用该函数使用了多久时间

    功能函数带参数

    def show_time(f):    # 装饰器函数,装饰下面的 foo 函数,并作为一个函数的扩展
        def inner(*args): #foo函数带了形参,这里也要带上形参
            start_time = time.time()
            f(*args)    #执行 foo 函数,把 foo 函数的传递过来的实参传给形参 args
            end_time = time.time()
            print('spend %s' % (end_time - start_time))
        return inner # inner 的内存地址指向的是 print('spend %s' % (end_time - start_time))
    
    
    @show_time  #给自己加上装饰器函数,装饰器函数下面的 inner 函数
    def foo(*args):
        sums=0
        for i in args:
            sums+=i
        print(sums)
        time.sleep(1)
    
    foo(1,2,5,7,9)
    弄一个加法器

    装饰器带参数

    def logger(flag='true'):
        def show_time(f):    # 装饰器函数,装饰下面的 foo 函数,并作为一个函数的扩展
            def inner(*args): #foo函数带了形参,这里也要带上形参
                start_time = time.time()
                f(*args)    #执行 foo 函数,把 foo 函数的传递过来的实参传给形参 args
                end_time = time.time()
                print('spend %s' % (end_time - start_time))
                if flag=='true':   #inner 拿到 flag 参数后做判断,闭包概念,对外部作用域的变量进行引用
                    print("日志记录")
            return inner # inner 的内存地址指向的是 print('spend %s' % (end_time - start_time))
        return show_time
    
    @logger() 分成两部分:先执行 logger 函数,返回 show_time 内存地址,然后变成 @show_time 装饰器, show_time 装饰器把下面的函数名当做参数传递进 show_time 里面,执行show_time 返回 inner,foo(1,2,5,7,9) = inner(1,2,5,7,9),执行 inner
    def foo(*args):
        sums=0
        for i in args:
            sums+=i
        print(sums)
        time.sleep(1)
    
    foo(1,2,5,7,9)
    例3:把结果写进日志文件里面,时间不写进去,正常输出

    装饰器应用之登陆

       在电商平台中,我们可以看到,在不同的页面,如选择商品、购物车、金融支付等页面都能进行登陆且能记住登陆状态,登陆一次后就不需要在其它页面再次登陆。使用装饰器把登陆抽离出来。
    
        加入文件读写判断用户名密码
        用户选择不同页面登陆时,反回不同结果
    需求
        创建登陆标志位(login_falg),用来判断是否已登陆
        商品commodity()金融finance()购物车shopp_cart()为三个独立函数
        使用带参装饰器,反回不同结果
        用户选择进行测试
    分析
    jingdong.db
    {'user':{'alex':'123','hjc':'123'}}
    
    
    weixin.db
    {'user':{'alex1':'1234','hjc1':'1234'}}
    用户数据库
    login_status=False
    with open('jingdong','r',encoding='utf8') as jd:
        jd=eval(jd.read().strip())
    with open('weixin','r',encoding='utf8') as wx:
        wx=eval(wx.read().strip())
    
    
    def type(auth_type='jingdong'):  # 第三部:判断页面的登录的类型,默认是 jingdong,目的是在login_type 可以引用一个 auth_type 变量
        def page(f): # 第二部:调用主页、书店,金融的功能函数
            global login_status #用于修改全局变量:login_status 登录状态,默认 false,要求用户验证登录,true就不认证
            def login_type():  # 第一步先写好这个函数,再往外套函数
                global login_status # 用于修改 全局变量
                if login_status == False: #默认为未登录状态,要求用户验证
                    username = input('username: ')
                    password = input('password: ')
                    if auth_type=='jingdong': #如果登录页面的是 jingdong,去京东的用户文件里验证
                        if username in jd['user'] and password == jd['user'][username]:
                            print('welcome...')
                            f()  #成功后进入页面,执行页面函数,显示内容
                            login_status=True  #登录成功后,登录状态转为 true
                        else:
                            print('error')
                    elif auth_type=='weixin':
                        if username in wx['user'] and password == wx['user'][username]:
                            print('welcome...')
                            f()
                            login_status = True
                        else:
                            print('error')
                else:
                    print('用户已登录')
            return login_type #login_type 的内存地址,用于指向 login_type 的函数对象
        return page # 返回 page 函数的内存地址,指向 login_type 函数的内存地址,指向 login_type 的函数对象
    
    @type()
    def home():  # 主页
        print('welcome to home page...')
    
    @type('weixin')
    def book():  # 书店
        print('welcome to book page...')
    
    @type()
    def finance():  #金融页
        print('welcome to finance page...')
    
    #start 启动显示如下页面
    
    # 1.home
    # 2.finance
    # 3.book
    #>>:2 检测用户登录状态,没登录就调用登录验证接口
    
    while True:
        user_input=input('请输入:
    1:【主页】
    2:【书店】
    3:【金融】')
        if user_input =='1':
            home()
        elif user_input =='2':
            book()
        else:
            finance()
    代码

    .

  • 相关阅读:
    结对项目:四则运算
    Word Count(C语言)
    自我介绍+软工5问
    如何在博客园中使用markdown
    如何设计一门语言(九)——类型
    如何设计一门语言(八)——异步编程和CPS变换
    如何设计一门语言(七)——闭包、lambda和interface
    时隔多年我又再一次体验了一把跟大神聊天的感觉
    20199107 2019-2020-2 《网络攻防实践》第4周作业
    20199107 2019-2020-2 《网络攻防实践》第3周作业
  • 原文地址:https://www.cnblogs.com/tootooman/p/9007510.html
Copyright © 2011-2022 走看看