• 闭包以及装饰器(好东西)


    闭包

    首先要理解函数对象的概念,其实函数名就相当于变量名,只是多了一个括号调用的功能而已.而把函数当作对象就可以让全局空间中使用局部的变量或是函数

    • 闭包就是这样,这个方法可以打破层级关系,把局部变量拿到全局进行使用,并且还可以把外部的变量x封装到f2中,然后下次直接调用f2就能够起到使用局部变量x的作用
    def f1():
        x = 19
        def f2():
            print(x)
            print('from x2')
        return f2
    
    f2 = f1()
    f2()
    
    19
    from x2
    
    • 局部变量在全局使用,但又不污染全局变量,互不干扰
    x = 10
    def f1(x):
        
        def f2():
            print(x)
        return f2
    
    f2 = f1(5)
    f2()
    print(x)
    
    5
    10
    
    • 举一个爬虫的简单示例
    import requests
    def spider(url):
        response = requests.get(url)
        response.encoding = 'gb2312'
        data = response.text
        print(data)
    
    spider('http://www.xiaohuar.com')
    spider('http://www.xiaohuar.com')
    
    输出很长不复制,都是网站信息
    
    • 这样做每次在使用时都需要输入网址,所以用函数对象的概念改进一下
    • 同样的效果,但重复调用时变得很简单
    import requests
    def outter(url):
        def spider():
            response = requests.get(url)
            response.encoding = 'gb2312'
            res = response.text
            return res
        return spider
    
    xiaohuar = outter('http://www.xiaohuar.com')
    print(xiaohuar())
    print(xiaohuar())
    
    输入很长不复制
    

    装饰器

    • 改变代码功能的同时,不改变原有的调用方式,并且不改变原有的函数代码
    # 基础功能代码块
    import time
    
    def time_sleep():
        time.sleep(1)
        print('from sleep')
        
    time_sleep()
    
    from sleep
    
    • 现在要计算这个程序停留了多久,先示例修改源代码的情况
    def time_sleep():
        start = time.time()
        time.sleep(1)
        print('from sleep')
        end = time.time()
        print(end-start)
        
    time_sleep()
    
    from sleep
    1.0004761219024658
    
    • 示例不修改源代码用一个新的函数去添加功能
    def func():
        start = time.time()
        time_sleep()
        end = time.time()
        print(end-start)
    func()
    
    from sleep
    1.0004405975341797
    

    最基础的装饰器

    • 上面的写法有一个缺点,就是已经修改了原有的调用方式,会让用户很不习惯,所以我们在上面加一层装饰
    def deco(func):        # 这里的func只是一个形参,用来放入你想要使用这个装饰器功能的函数
        def wrapper():     # wrapper 就相当于变量名time_sleep,也就是那个假的
            start = time.time()
            func()         # 这里是原有的time_sleep函数
            end = time.time()
            print(end-start)
        return wrapper
    
    time_sleep = deco(time_sleep)       # 这两个time_sleep并不是一个东西,但是给到用户看到的假象就是一个东西
    time_sleep()                        # 调用这个就相当于调用wrapper(),只是伪装成了原函数
    
    from sleep
    1.0004761219024658
    

    完善装饰器

    有返回值的

    def time_sleep():
        
        time.sleep(1)
        print('from sleep')
        
        return 'lalala'
    
    def deco(func):
        
        def wrapper():
            
            start = time.time()
            res = func()         # 调用函数sleep_time并且接收它的返回值
            end = time.time()
            print(end-start)
            
            return res           # 返回sleep_time的返回值
        
        return wrapper
    
    time_sleep = deco(time_sleep)
    res = time_sleep()
    print(res)                    # 调用函数且打印返回值
    
    from sleep
    1.000476598739624
    lalala
    

    有参数的

    def time_sleep(name, age, sex='man'):
        time.sleep(1)
        print('from sleep')
        print(name, age, sex)
        return 'lalala'
    
    def deco(func):
        
        def wrapper(*args, **kwargs):         # *args,**kwargs用来接收参数,打包成元祖或字典
            
            start = time.time()
            res = func(*args, **kwargs)       # *args,**kwargs用来解包成一个个的参数
            end = time.time()
            print(end-start)
            
            return res
        
        return wrapper
    
    time_sleep = deco(time_sleep)
    res = time_sleep('leijun', 19)
    print(res)
    
    from sleep
    leijun 19 man
    1.0004239082336426
    lalala
    

    装饰器模版

    def deco(func):
        
        def wrapper(*args, **kwargs):
            
            res = func(*args, **kwargs)
            
            return res
        
        return wrapper
    

    语法糖

    • 写好装饰器,然后在函数上方使用"@装饰器名"来调用装饰器,让代码看上去舒服一点
    • 效果就相当于写了,伪装函数名 = 装饰器名(函数名)

    登录装饰器

    userinfo_dict = {'leijun': '123'}     # 用户信息可以用文件操作
    
    def deco(func):
        
        def wrapper(*args, **kwargs):
            
            username = input('请输入用户名:').strip()
            userpwd = input('请输入密码:').strip()
            
            if userinfo_dict.get(username) == userpwd:
                print('登录成功')
                res = func(*args, **kwargs)
                return res
            else:
                print('登录失败')
                return None
            
        return wrapper
    
    @deco
    def shopping():
        print('shopping')
                
    shopping()
    
    请输入用户名:lei
    请输入密码:123
    登录失败
    
    # 避免二次登录
    
    userinfo_dict = {'leijun': '123'}     # 用户信息可以用文件操作
    is_login = False                    # 用这个来控制用户是否需要登录
    
    def deco(func):
        
        def wrapper(*args, **kwargs):
            global is_login          # 声明全局变量,否则局部的无法影响到全局的
            
            if not is_login:         # 没有登录过的
                username = input('请输入用户名:').strip()
                userpwd = input('请输入密码:').strip()
    
                if userinfo_dict.get(username) == userpwd:
                    print('登录成功')
                    res = func(*args, **kwargs)
                    is_login = True         # 标记为已登录
                    return res
                else:
                    print('登录失败')
                    return None
            else:
                res = func(*args, **kwargs)
                
                return res
            
        return wrapper
    
    @deco
    def shopping():
        print('shopping')
    
    @deco
    def withopen():
        print('with open')
                
    shopping()
    withopen()
    
    请输入用户名:leijun
    请输入密码:123
    登录成功
    shopping
    with open
    

    可变类型的局部变量可以修改全局变量

    l = [1, 2]
    def func():
        l.append(3)
    
    func()
    print(l)
    
    [1, 2, 3]
    

    三层装饰器

    • 用来给装饰器中添加一些参数
    def sanceng(name, age):
        def deco(func):
            print(name)
            def wrapper(*args, **kwargs):
                print(age)
                res = func(*args, **kwargs)
                
                return res
            return wrapper
        return deco
    
    
    def haha():
        print('haha')
    
    # 调用方法
    deco = sanceng('leijun', 19)
    haha = deco(haha)
    haha()
    
    print('-' * 30)
    # 调用方法二
    
    @sanceng('leijun', 19)
    def lala():
        print('lala')
        
    lala()
    
    leijun
    19
    haha
    ------------------------------
    leijun
    19
    lala
    
    • 能够写下面这段代码就对闭包和装饰器有初步的了解了
    is_login_dict = {'leijun': '123'}    # 用户信息,可以用文件操作进行扩展
    is_login = False   # 控制用户是否已登录
    
    def auth(origin):
        '''第三层装饰器'''
        
        def deco(func):
            '''第二层装饰器'''
            
            def wrapper(*args, **kwargs):
                '''第一层装饰器'''
                
                global is_login         # 升级全局变量,便于更改
                
                # 用户已登录的话,直接调用函数就可以了
                if is_login:      
                    res = func(*args, **kwargs)
                    return res
                
                # 用户没登录,但是口令不是T,不给与登录
                if origin != 'T':
                    print('非法入侵')
                    return '非法入侵'
                
                # 用户没登录,但是口令是T,进行登录
                username = input('请输入你的帐号>>>').strip()
                userpwd = input('请输入你的密码>>>').strip()
                
                if is_login_dict.get(username) == userpwd:
                    print('登录成功')
                    is_login = True              # 修改用户为已登录
                    res = func(*args, **kwargs)
                    return res
                else: 
                    print('帐号密码错误')
                    return None           # 养成写None的习惯
            
            return wrapper     # 进入第一层
        
        return deco    # 进入第二层
    
    
    @auth('T')
    def shopping():
        print('shopping')
    
    @auth('f')
    def withopen():
        print('withopen')
        
    shopping()
    withopen()
    
    请输入你的帐号>>>leijun
    请输入你的密码>>>123
    登录成功
    shopping
    withopen
    
    • 下面不用看
    is_login_dict = {'username': None}
    
    
    def auth(origin):
            def login_deco(func):
                '装饰器外层'
    
                def wrapper(*args, **kwargs):
    
                    if not is_login_dict['username']:
                        username = input('请输入帐号:').strip()
    
                        if username != 'leijun':
                            print('非法登录')
                            return None
    
                        is_login_dict['username'] = True
    
                        res = func(*args, **kwargs)
    
                        return res
                    else:
                        res = func(*args, **kwargs)
                        return res
                if origin == '1':
                    return wrapper
                else:
                    print('非法入侵')
                    return None
    
            return login_deco
    
    
    @auth('1')
    def shopping():
        print('from shopping')
    
    @auth('0')
    def register():
        print('from register')
    
    shopping()
    # register()
    
    非法入侵
    请输入帐号:213
    非法登录
    
    PS:这个非法入侵,是怎么出来的
  • 相关阅读:
    django LDAP
    Python egg
    皮皮书屋
    Linux运维
    bash shell 快捷键汇总
    linux ldconfig
    Linux set env export declare unset
    OpenStack
    【LeetCode】258. Add Digits
    一个"Median Maintenance"问题
  • 原文地址:https://www.cnblogs.com/lucky75/p/10957483.html
Copyright © 2020-2023  润新知