闭包函数与装饰器

闭包函数与装饰器

一、什么是闭包

闭包:闭是封闭(函数内部函数),包是包含(函数内部函数对外部作用域而非全局作用域的变量的引用)。闭包指的是:函数内部函数对外部作用域的引用。

x = 111  # 全局作用域的变量
def outer():
    y = 222  # 局部作用域的变量
    def inner():
        print(x,y)  # 引用了全局和局部作用域的变量
    return inner

二、给函数体传值的的两种方式

1、传参

def save_file(filename,content):  # 通过参数给函数体代码传值
    with open(filename,'a',encoding='utf-8') as fw:
        fw.write(content)
    return 

2、闭包

def deco():
    x = 1   # x的值写死了,无法修改 
    y = 10  # y的值写死了,无法修改 
    def my_max():
        if x > y:  # 函数内部函数x对外部作用域x=1的引用,就是闭包
            return x
        return y
    return my_max

res = deco()
print(res())

# 闭包函数进阶
def deco(x,y):  # x,y的值在调用时可以改变。
    def my_max():
        if x > y:
            return x
        return y
    return my_max
res = deco(4,9)  # 优点一次传参,多次调用。
print(res())
print(res())

三、闭包函数的应用

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

闭包函数,可以是一次传参,多次调用。

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

# 使用函数传参实现
import requests
url = 'https://www.baidu.com'
url2 = '....'
def my_get(url):
    response = requests.get(url)
    print(response.text)

my_get(url)  # 需要时,每次都要定义网址。如果需要爬取的网址比较多,会特别麻烦
my_get(url) 
my_get(url)
# 使用闭包函数实现
def deco(url):
    def my_get():
        response = requests.get(url)
        print(response.text)
    return my_get

my_jd = deco('http://www.jd.com')  # 一次定义,多次调用调用
my_jd()  # 多次调用闭包函数
my_jd()
my_jd()
my_baidu = deco('https://www.baidu.com')
my_baidu()
my_baidu()
my_baidu()
my_baidu()

装饰器

什么是装饰器?

器就是一个工具,而程序中的函数就是具备某一功能的工具,所以装饰器就是给被装饰对象添加新的功能。因此定义装饰器就是定义一个函数,只不过该函数的功能是用来为其他函数添加新的功能。

需要注意的是:

  • 装饰器本身其实是任意可调用的对象
  • 被装饰的对象也是任意可调用的对象

为什么要用装饰器?

如果我们上线了一个项目,我们需要修改某一个方法,但是我们不想修改方法的使用方法,这个时候可以使用装饰器。因为软件的维护必须遵循开封封闭原则,即软件一旦上线运行后,软件的维护对修改源代码是封闭的,对扩展功能值的是开放的。

装饰器的实现必须遵循两大原则:

​ 1.不修改被装饰对象的源代码

​ 2.不修改被装饰对象的调用方式

怎么用装饰器?

# 提前了解知识
# 导入时间模块
import time
print(time.time())  # 时间戳 当前时间距离1970-1-1 00:00:00相差的秒数
# 1970-1-1 00:00:00是Unix诞生元年
time.sleep(1)  # 睡眠3秒,程序暂停3秒
print('FBI warning!')

要实现统计index函数执行的时间

# 源代码
import time
def index():
    time.sleep(3)
    print('澳门最大赌场上线了 性感MM在线发牌')
# 通过修改源代码实现,实现统计index函数执行的时间
import time
def index():
    time.sleep(3)
    print('澳门最大赌场上线了 性感MM在线发牌')
start = time.time()
index()
end = time.time()
print(f'index run time {end-start}')
# 通过改变源函数的调用方式实现
import time
def index():
    time.sleep(3)
    print('澳门最大赌场上线了 性感MM在线发牌')
    
def get_time(func):  # func = index
    start = time.time()
    func()  # index()
    end = time.time()
    print(f'index run time {end-start}')

get_time(index)  # 通过函数传参的方法实现了,但是改变了源函数的调用方式
# 通过闭包函数实现
import time
def index():
    time.sleep(3)
    print('澳门最大赌场上线了 性感MM在线发牌')

def outter(func):  # func = 原始的index
    def get_time():  # 无参装饰器
        start = time.time()
        func()
        end = time.time()
        print(f'index run time {end-start}')
    return get_time

index = outter(index)  # get_time
index()  # func()= index()

完善装饰器

上述的装饰器,最后调用index()的时候,其实是在调用get_time(),因此如果原始的index()有返回值的时候,get_time()函数的返回值应该和index()的返回值相同,也就是说,我们需要同步原始的index()和get_time()方法的返回值。

import time
def index():
    time.sleep(3)
    print('澳门最大赌场上线了 性感MM在线发牌')
    return 123

def outter(func):
    def get_time():
        start = time.time()
        res = func()
        end = time.time()
        print(f'index run time {end-start}')
        return res
    return get_time

index = outter(index)  # get_time
res = index  # <function outter.<locals>.get_time at 0x079D2DB0>
print(res)
# index()
res2 = index()
print(res2)  # 123

澳门最大赌场上线了 性感MM在线发牌
index run time 3.000171661376953

如果我们想要该装饰器取装饰一个有参函数

def login(name):
    time.sleep(1)
    print(f'{name} is sb')
    return 'login'

函数参数的问题:

无参函数和有参函数都可以直接使用,函数可以以接收任意数量的参数。

如果原始的login()方法需要传参,那么我们之前的装饰器是无法实现该功能的,由于login = outer(login),实际上outer(login)是等于get_time的,所以,要想login()传参,只需要在get_time()中传参即可。get_time(*args,**kwargs),实际上就是将原始的login(name)中的位置实参用元组接收,关键字实参用字典接收,并赋值给args,kwargs,fun(*args,**kwargs)将赋值的args,kwargs即(元组和字典)打散成位置形参传给login的形参。

def index():
    time.sleep(3)
    print('澳门最大赌场上线了 性感MM在线发牌')
    return 'index'

# res1 = index()

def login(name):
    time.sleep(1)
    print(f'{name} is sb')
    return 'login'

# res = login('egon')

def outter(func):
    def get_time(*args,**kwargs):
        start = time.time()
        res = func(*args,**kwargs)
        end = time.time()
        print(f'func run time {end-start}')
        return res
    return get_time

login = outter(login)  # login = get_time
res = login('egon')  # login('egon') = get_time('egon')
print(res)  # res = 'login'

egon is sb
func run time 1.0000572204589844
login

语法糖

import time

def outter(func):
    def get_time(*args,**kwargs):
        start = time.time()
        res = func(*args,**kwargs)
        end = time.time()
        print(f'func run time {end-start}')
        return res
    return get_time
@outter  # 等同于 index = outter(index)  # index = get_time
def index():
    time.sleep(3)
    print('澳门最大赌场上线了 性感MM在线发牌')
    return 'index'

# index = outter(index)  # index = get_time
# print(index)
index()  # get_time() -> 执行index()


@outter
def login(name):
    time.sleep(1)
    print(f'{name} is sb')
    return 'login'

login = outter(login)  # login = get_time
print(login)
login('egon')   #  get_time('egon')——>执行真正的login()

@outter
def home(*args,**kwargs):
    time.sleep(1)
    return 'home'

home = outter(home)  # home = get_time()
print(home)
home()  # get_time()——>执行真正的home()

语法糖会将距紧挨着它的可调用对象的名字当做参数自动传入调用outter,自动执行outter函数

@outter
def index()
	pass

无参装饰器模板

def outter(func):
    def inner(*args,**kwargs):
        print('执行被装饰函数之前,你可以做的操作')
        res = func(*args,**kwargs)
        print('执行被装饰函数之后,你可以做的操作')
        return res
    return inner
import time
user_auth = {'is_login':None}

def login_deco(func):
    def wrapper(*args,**kwargs):
        if user_auth['is_login']:  # 已经登录了
            res = func(*args,**kwargs)
            return res
        else:
            username = input('请输入你的用户名>>>>>>:').strip()  # 没有登录,执行登录
            pwd = input('请输入你的密码>>>>>:').strip()
            if username == 'nick'and pwd =='123':
                print('恭喜登录成功')
                user_auth['is_login'] = True
                res = func(*args,**kwargs)
                return res
            else:
                print('账号密码错误')
    return wrapper

@login_deco
def index(name):
    time.sleep(0.5)
    print(f'{name} is dsb')
    return 666


@login_deco
def home():
    time.sleep(0.5)
    print('home')
    return 9999

index('nick')

home()

有参装饰器(最复杂就三层)

def sanceng(data):
    def deco(func):
        def wrapper(*args,**kwargs):
            if data == 'file':
                # 执行被装饰器函数之前你可以做的操作
                res = func(*args,**kwargs)
                # 执行被装饰函数之后你可以做的操作
                return res
        return wrapper
    return deco

装饰器在装饰的时候,顺序从下往上。装饰器在执行的时候,顺序从上往下。

posted @ 2019-07-13 20:57  最后的别离  阅读(220)  评论(0编辑  收藏  举报