欢迎您访问程序员文章站本站旨在为大家提供分享程序员计算机编程知识!
您现在的位置是: 首页

闭包与装饰器

程序员文章站 2022-03-19 11:58:43
...

闭包

  • 什么是闭包

基于函数对象的概念,可以将函数返回到任意位置去调用,但作用域的关系是在定义完函数时就已经被确定了的,与函数的调用位置无关。

也就是说函数被当做数据处理时,始终以自带的作用域为准。若内嵌函数包含对外部函数作用域(而非全局作用域)中变量的引用,那么该’内嵌函数’就是闭包函数,简称闭包(Closures)

总结: 

1.定义在函数内部的函数,该函数引用了外部函数名称空间的名字,通常将闭包函数用return返回,然后可以在任意位置使用

2.闭包也是给函数传参的一种方式

def outter(x,y):
    # x = 1
    # y = 2
    def my_max():
        if x > y:
            return x
        return y
    return my_max
res = outter(1,2)   # outter加()本身会得到一个返回值my_max,实参1和2相当于变量值传给了x和y两个变量名
print(res())

闭包与装饰器

 

import requests  # 爬虫模块

#方式一 直接给函数传参
info1 =  'https://www.baidu.com'
info2 = '......'
def my_get(rul):
    resuit = requests.get(rul)
    if resuit.status_code == 200:
        print(len(resuit.text))
my_get(info1)
my_get(info2)

#方式二 用闭包的方式给函数传参
def outter(func):
    # func =  'https://www.jd.com'
    def my_get(func):
        resuit = requests.get(func)
        if resuit.status_code == 200:
            print(len(resuit.text))
    return my_get
res_jd = outter( 'https://www.jd.com')  # res_jd = my_get
res_jd()  # 相当于 my_get()

__closure__ 属性

查看到闭包函数所包裹的外部变量

x=1
def outer():
    x=2
    def inner():
        print(x)
    return inner

func=outer()
func() # 结果为2
print(func.__closure__)
# (<cell at 0x000001B27B9184C8: int object at 0x0000000066EA6E00>,)
print(func.__closure__[0].cell_contents)
# 2

注意:“闭”代表函数是内部的,“包”代表函数外’包裹’着对外层作用域的引用。因而无论在何处调用闭包函数,使用的仍然是包裹在其外层的变量。

装饰器

  • 为什么要用装饰器

软件的设计应该遵循开放封闭原则,即对扩展是开放的,而对修改是封闭的。对扩展开放,意味着有新的需求或变化时,可以对现有代码进行扩展,以适应新的情况。对修改封闭,意味着对象一旦设计完成,就可以独立完成其工作,而不要对其进行修改。

软件包含的所有功能的源代码以及调用方式,都应该避免修改,否则一旦改错,则极有可能产生连锁反应,最终导致程序崩溃,而对于上线后的软件,新需求或者变化又层出不穷,我们必须为程序提供扩展的可能性,这就用到了装饰器。

开放:对扩展开放

封闭:对修改封闭

  • 什么是装饰器

’装饰’代指为被装饰对象添加新的功能,’器’代指器具/工具,装饰器与被装饰的对象均可以是任意可调用对象。概括地讲,装饰器的作用就是在不修改被装饰对象源代码和调用方式的前提下为被装饰对象添加额外的功能。装饰器经常用于有切面需求的场景,比如:插入日志、性能测试、事务处理、缓存、权限校验等应用场景,装饰器是解决这类问题的绝佳设计,有了装饰器,就可以抽离出大量与函数功能本身无关的雷同代码并继续重用。

  • 装饰器模板

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

案例1: 装饰器的使用

# 简单版:
def func():
    time.sleep(2)  # 让cpu睡眠2秒
    print('form func')

star_res = time.time()
func()
over_res = time.time()
print('运行时间: %s'%(over_res - star_res))
# >>> 运行时间: 2.00064754486084
#此时的计算时间功能只能func函数单独使用,如果其他函数也要用到时间计算功能需要重新编写代码,
# 可以把计算时间功能封装成一个函数给其他函数使用

#有装饰器版
def func():
    time.sleep(2)  # 让cpu睡眠2秒
    print('form func')

def outter(info):  # info = 原始func的内存地址
    def time_func():
        star_res = time.time()
        info()  # 相当于 func() 函数运行
        over_res = time.time()
        print('运行时间: %s'%(over_res - star_res))
    return time_func
func = outter(func)  # 新 func = time_func的内存地址
func()  # 相当于 time_func()函数运行
# >>> form func
# >>> 运行时间: 2.0006558895111084

案例2: 有参装饰器使用

import time
def index():
    time.sleep(3)
    print('澳门最大线上赌场开业啦 性感tank在线发牌!')
    return 'index'
# res1 = index()

def login(name):
    time.sleep(1)
    print('%s is sb'%name)
    return 'login'
# res = login('egon')

def outter(func):  # func = 最原始的login函数的内存地址
    def get_time(*args, **kwargs):  # args = ('egon',) kwargs = {}
        start = time.time()
        res = func(*args, **kwargs)  # 最原始的login函数的内存地址() 直接调用  func('egon')
        end = time.time()
        print('func run time:%s'%(end-start))
        return res
    return get_time
login = outter(login)  # outter(最原始的login函数的内存地址)
res = login('egon')
print(res)
# >>>egon is sb
# >>>func run time:1.000328779220581
# >>>login

index = outter(index)
res1 = index()
print(res1)
# >>>澳门最大线上赌场开业啦 性感tank在线发牌!
# >>>func run time:3.0011234283447266
# >>>index

案例3

def func():
    time.sleep(2)  # 让cpu睡眠2秒
    print('form func')
    return '函数func的返回值'

def user(name):
    time.sleep(1)
    print('welcome %s'%name)
    return '函数user的返回值'

def outter(info):  # info = 原始func的内存地址  # info = 原始user的内存地址
    def time_get(*args,**kwargs):
        # args = ' '
        # kwargs = ' '  闭包函数的特性
        star_res = time.time()
        res = info(*args,**kwargs)  # 相当于 func() 函数运行  # 相当于user()函数运行,但函数user必须要传参才能执行
        over_res = time.time()
        print('运行时间: %s'%(over_res - star_res))
        return res
    return time_get
func = outter(func)  # 新 func = time_get的内存地址
res_1 =func()  # 相当于 time_get()函数运行
print(res_1)   # res_1 = res = 函数func的返回值
# >>> form func
# >>> 运行时间: 2.0006558895111084
# >>> 函数func的返回值

user = outter(user)  # 新 user = rime_get 的内存地址
res_2 = user('wong')  # 相当于 time_get()运行函数
print(res_2)
# >>> welcome wong
# >>> 运行时间: 1.000464677810669
# >>> 函数user的返回值

语法糖

@装饰器函数名        

相当于它拿到了它所装饰的哪个函数的内存地址,当作实参传给装饰器的形参

import time

def outter(info):  # info = 原始func的内存地址  # info = 原始user的内存地址
    def time_get(*args,**kwargs):
        # args = ' '
        # kwargs = ' '  闭包函数的特性
        star_res = time.time()
        res = info(*args,**kwargs)  # 相当于 func() 函数运行  # 相当于user()函数运行,但函数user必须要传参才能执行
        over_res = time.time()
        print('运行时间: %s'%(over_res - star_res))
        return res
    return time_get
# func = outter(func)  # 新 func = time_get的内存地址
# res_1 =func()  # 相当于 time_get()函数运行
# print(res_1)

@outter  # func = outter(func)
def func():
    time.sleep(2)  # 让cpu睡眠2秒
    print('form func')
    return '函数func的返回值'
func()

@outter  # user = outter(func)
def user(name):
    time.sleep(1)
    print('welcome %s'%name)
    return '函数user的返回值'
user('wong')

多层装饰器

装饰器在装饰的时候  顺序从下往上

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

def outter1(func1): #func1=wrapper2的内存地址
    print('加载了outter1')
    def wrapper1(*args,**kwargs):
        print('执行了wrapper1')
        res1=func1(*args,**kwargs)  # func1=wrapper2的内存地址
        return res1  
    return wrapper1

def outter2(func2): #func2=wrapper3的内存地址
    print('加载了outter2')
    def wrapper2(*args,**kwargs):
        print('执行了wrapper2')
        res2=func2(*args,**kwargs)  # func2=wrapper3的内存地址  相当于wrapper3() 并执行 
        return res2
    return wrapper2

def outter3(func3): # func3=最原始的那个index的内存地址
    print('加载了outter3')
    def wrapper3(*args,**kwargs):
        print('执行了wrapper3')
        res3=func3(*args,**kwargs)  # func3=最原始的那个index的内存地址  相当于 index() 并执行 
        return res3
    return wrapper3

@outter1 # outter1(wrapper2的内存地址)======>index=wrapper1的内存地址
@outter2 # outter2(wrapper3的内存地址)======>wrapper2的内存地址
@outter3 # outter3(最原始的那个index的内存地址)===>wrapper3的内存地址  # wrapper = outter3(index)
def index():  # func3(*args,**kwargs)的时候运行此函数
    print('from index')  # 最后一步
index()  # 相当于 wrapper1()

闭包与装饰器

wraps

from functools import wraps

def deco(func):
    @wraps(func) #加在最内层函数正上方
    def wrapper(*args,**kwargs):
        return func(*args,**kwargs)
    return wrapper

@deco
def index():
    '''哈哈哈哈'''
    print('from index')

print(index.__doc__)  # 哈哈哈哈
index()  # from index

 

相关标签: 函数