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

闭包

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

  • 闭包就是这样,这个方法可以打破层级关系,把局部变量拿到全局进行使用,并且还可以把外部的变量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:这个非法入侵,是怎么出来的
posted @ 2019-05-31 20:53  abcde_12345  阅读(130)  评论(0编辑  收藏  举报