python中装饰器之闭包函数(一)

闭包函数

'''
闭包函数:
    1、闭包函数必须在函数内部定义
    2、闭包函数可以引用外层函数的名字
    
    闭包函数是函数对象、函数嵌套、名称空间与作用域的结合体
'''


# 直接传参
def func(x):
    print(x)


# 通过闭包函数传参

def wrapper(x):
    def inner():
        print(x)

    return inner


func = wrapper(100)  # >>返回inner的内存地址>>>>保存在func中
func()
闭包函数定义
# 闭包函数的应用

import requests


# 直接传参
def spider_func(url):
    response = requests.get(url)
    if response.status_code == 200:
        print(len(response.text))


# 闭包函数chuanc
def spider_outer(url):
    def spider_inner():
        response = requests.get(url)
        if response.status_code == 200:
            print(len(response.text))

    return spider_inner


url1 = 'https://www.baidu.com/'
url2 = 'https://www.cnblogs.com/xiaoyuanqujing'

spider_baidu = spider_outer(url1)
spider_blog = spider_outer(url2)
spider_baidu()
spider_blog()

spider_outer(url1)
闭包函数的应用

 

 装饰器

'''
装饰器
    不修改原函数的源代码
    不修改原函数的调用方式
    
    被装饰对象:即需要添加功能的函数
    装饰器:被装饰函数添加的新功能的函数
'''

例子:假设我们现在有一个函数,我们要为其添加一个统计执行时间的功能

def download_movie():
    print('Start downloading.....')
    time.sleep(3)
    print('Download completed.....')

讲道理我们是这么执行的

# 不使用装饰器
start_time = time.time()
download_movie()
stop_time = time.time()
print(f'Running time is {stop_time - start_time}....')

问题虽然解决了,但针对每个需要添加统计时间功能的函数,都得敲一遍代码。这显然不符合python所要求的简洁优雅~

所以我们考虑:

# 使用装饰器(无返回值时)
def timer(func):
    def inner():
        start_time = time.time()
        func()  # >>>>>被装饰对象<<<<<
        stop_time = time.time()
        print(f'Running time is {stop_time - start_time}....')

    return inner


timer(download_movie)  # >>>>>>>>>> 返回inner
download_movie = timer(download_movie)  # >>>>>>>>>>>以变量名download_movive接收inner
download_movie()

问题似乎解决了,然鹅,如果被装饰对象有返回值,如:

def download_movie():
    print('Start downloading.....')
    time.sleep(3)
    print('Download completed.....')
    return 'Aoi.avi'  # 当被装饰对象有返回值时,上述装饰器需做修改

我们好像就得不到被装饰对象执行结束后返回的值的,这显然没有什么软用。于是修改装饰器

def timer(func):
    def inner():
        start_time = time.time()
        res = func()  # >>>>>>>>>>>用res接收func()产生的返回值<<<<<<<<<<<<<<<
        stop_time = time.time()
        print(f'Running time is {stop_time - start_time}....')
        return res  # 返回>>>>>>>>>>res<<<<<<<<<< (本例子中即download_movie的返回值)

    return inner


download_movie = timer(download_movie)
print(download_movie())  # >>> Aoi.avi

问题好像又解决了,然鹅,如果被装饰对象有参数,如:

# 使用装饰器(被装饰对象有参数)
def download_movie(url):
    print(f'From {url}\nStart downloading........')
    time.sleep(3)
    print(f'Download completed')

如果还是 download_movie = timer(download_movie)
download_movie(url) >>>>>># 会报错,因为download实际上是inner的别名。调用的是inner(url)

于是修改装饰器:

def timer(func):
    def inner(url):
        start_time = time.time()
        func(url)  # 从闭包函数inner获取参数
        stop_time = time.time()
        print(f'Running time is {stop_time - start_time}....')

    return inner


download_movie = timer(download_movie)  # >>>>>返回inner >>>>>>更名为download_movie
download_movie('https://www.baidu.com/')

问题好像又解决了,然鹅,如果不确定被装饰对象的参数个数那又该怎么办呢?

修改装饰器,最终版本:

def timer(func):
    def inner(*args, **kwargs):
        start_time = time.time()
        res = func(*args, **kwargs)  # >>>>>>>>>>>用res接收func()产生的返回值<<<<<<<<<<<<<<<
        stop_time = time.time()
        print(f'Running time is {stop_time - start_time}....')
        return res  # 返回>>>>>>>>>>res<<<<<<<<<< (本例子中即download_movie的返回值)

    return inner

解决~~~~

 欸,等下~~~~~~~~~~~这装饰器用起来还是有点麻烦啊

python大佬为我们提供了更简洁的装饰器调用方式。装饰器语法糖@

def timer(func):
    def inner(*args, **kwargs):
        start_time = time.time()
        res = func(*args, **kwargs)  # >>>>>>>>>>>用res接收func()产生的返回值<<<<<<<<<<<<<<<
        stop_time = time.time()
        print(f'Running time is {stop_time - start_time}....')
        return res  # 返回>>>>>>>>>>res<<<<<<<<<< (本例子中即download_movie的返回值)

    return inner


@timer
def func():
    print('hello world')


# 当我们调用func()时
func()
# 等价于  func = timer(func)  func()

 

 

 

 

 

 

 

 

posted @ 2019-11-12 13:38  GhostAnt  阅读(208)  评论(0编辑  收藏  举报