闭包与装饰器超详细

54

附加

生命周期

​ 生命周期,每一个存储的数据都是有生命周期的

def outer():
    a = 10
    print(f"a is {a}")
#a的生命周期在outer中,出outer就到头了
outer()
print(a)
变量解析原则 --LEGB
  • L – Local
  • E – Enclosing function locals
  • G – Global
  • B – Builtin

闭包

什么是闭包
  • 在函数中可以(嵌套)定义另一个函数时,如果内部的函数引用了外部的函数的变量,则可能产生 闭包。
  • 闭包可以用来在一个函数与一组“私有”变量之间创建关联关系。
  • 在给定函数被多次调用的过程中,这些私有变量能够保持其持久性。
闭包条件
  1. 必须要有一个内嵌函数
  2. 内函数必须引用外函数的变量
  3. 外函数必须返回内函数
#1.简单闭包
def outer(x):
    a = 300
    def inner():
        print(f"两数之和为:{x+a}")
    return inner

d = outer(10)    #d是inner
d()
#形成闭包dir(d)就会有__closure__属性
#2。例子
def outer():
    tmp_list = []
    def inner(name):
        tmp_list.append(1)
        print(f"{name} -- {tmp_list}")
    return inner

d1 = outer()
d2 = outer()
d1("d1")
d2("d2")
d1("d1")
print(id(d1),id(d2))
print(id(outer()),id(outer()))
输出:
d1 -- [1]
d2 -- [1]
d1 -- [1, 1]
1657755987984 1657758207280
1924411675632 1924411675632 
#虽然代码一样,但每次调用外函数,都会重新执行,创建一个新的tmp_list和inner
#这里的outer()是一个匿名变量
闭包
  • 形成闭包之后,闭包函数会获得一个非空的__closure__属性(对比我们最后的函数test,test是一 个不具备闭包的函数,它的__closure__属性是None),这个属性是一个元组
  • 元组里面的对象为cell对象,而访问cell对象的cell_contents属性则可以得到闭包变量的当前值 (即上一次调用之后的值)
  • 而随着闭包的继续调用,变量会再次更新
  • 所以可见,一旦形成闭包之后,python确实会将__closure__和闭包函数绑定作为储存闭包变量的 场所

装饰器

什么是装饰器
  • 装饰器是这样一种设计模式:如果一个类(函数)希望添加其他类(函数)的一些功能,而不希望 通过继承或是直接修改源代码实现,那么可以使用装饰器模式

  • 简单来说Python中的装饰器就是指某些函数或其他可调用对象,以函数或类作为可选输入参数, 然后返回函数或类的形式。通过这个在Python2.6版本中被新加入的特性可以用来实现装饰器设计 模式

  • 装饰器就是一个可以接受调用也可以返回调用的函数,该函数接受被装饰的函数作为其位置参数。 装饰器通过使用该参数来执行某些操作,然后返回原始参数或一些其他的调用

  • 函数也是对象,也可以当做参数传递

    在这里插入代码片简而言之:装饰器就是在不改变函数或类的源代码的基础上,添加额外功能装饰器的本质就是闭包。它和闭包的不同是它需要传入一个callable对象

#统计运行时间的装饰器
import time
def runtime(func):
    def inner():
        start = time.time()
        func()
        end = time.time()
        print(f"函数执行花了{end - start}s")
    return inner

import time
@runtime    #@修饰符去使用装饰器
def func1():
    time.sleep(2)
    print("func1....")
@runtime    #func2 = runtime(func2)
def func2():
    time.sleep(2)
    print("func2....")
func1()
func2()
输出:
func1....
函数执行花了2.000455856323242s
func2....
函数执行花了2.0076518058776855s

改良版(可以传递0-~参数,返回返回值)

import time
def runtime(func):
    print("this is runtime")
    def inner(*args,**kwargs):
        start = time.time()
        result = func(*args,**kwargs)
        end = time.time()
        print(f"函数执行花了{end - start}s")
        print(result)
        return result
    return inner

import time
@runtime    #@修饰符去使用装饰器
def func1():
    time.sleep(2)
    print("func1....")
@runtime    #func2 = runtime(func2)
def func2(a,b):
    time.sleep(2)
    print(f"func2....{a},{b}")
    return a+b
func1()
print(func2(1,2))
输出:
this is runtime
this is runtime
func1....
函数执行花了2.002455711364746s
None
func2....1,2
函数执行花了2.009401559829712s
3
3

username = ''
def login_required(func):
    def inner(*args,**kwargs):
        if username == 'root':
            print("欢迎")
            result = func(*args,**kwargs)
            return result
        else:
            return '没有权限'

    return inner
#可以应用多个装饰器,但是要注意装饰器的执行顺序
#从最上面的装饰器的内函数开始执行
@runtime  #add = runtime(login_required(add))
@login_required
def add(a,b):
        return a+b

username = input("请输入你的username:")
print(add(1,2))

编写和使用装饰器

什么是元数据

​ 元数据(Metadata),又称中介数据、中继数据,为描述数据的数据(data about data),主要是 描述数据属性(property)的信息(函数的重要的元信息比如名字、文档字符串、注解和参数签名等)

保留元数据

​ 利用@functools.wraps(fun),将一个函数的重要内容复制到另一个函数

​ 温馨提醒:任何时候你定义装饰器的时候,都应该使用 functools 库中的 @wraps 装饰器来注解底层 包装函数

内置的装饰器

Python还提供了类装饰器与@staticmethod,@classmethod,@property和这三个在面向对象编 程中常用的装饰器

  • staticmethod:把类中定义的实例方法变成静态方法-
  • classmethod:把类中定义的实例方法变成类方法
  • property:把类中定义的实例方法变成类属性
import functools
import time
def runtime(func):
    print("this is runtime")
    #保留传递进来的函数的元数据,将他的元数据赋值给Inner
    @functools.wraps(func)
    def inner(*args,**kwargs):
        start = time.time()
        result = func(*args,**kwargs)
        end = time.time()
        print(f"函数执行花了{end - start}s")
        print(result)
        return result
    return inner
username = ''
def login_required(func):
    def inner(*args,**kwargs):
        if username == 'root':
            print(f"欢迎执行{func.__name__}函数")
            result = func(*args,**kwargs)
            return result
        else:
            return f'执行{func.__name__}权限不够'

    return inner
#可以应用多个装饰器,但是要注意装饰器的执行顺序
#从最上面的装饰器的内函数开始执行
@login_required
@runtime  #add = runtime(login_required(add))
def add(a,b):
        return a+b

username = input("请输入你的username:")
print(add(1,2))

装饰器带参数

  • 自身不传入参数的装饰器,使用两层函数定义
  • 自身传入参数的装饰器,使用三层函数定义
def deco1(name):
    def login_required(func):
        def inner(*args,**kwargs):
            if name == 'root':
                print(f"欢迎执行{func.__name__}函数")
                result = func(*args,**kwargs)
                return result
            else:
                return f'执行{func.__name__}权限不够'
        return inner
    return login_required
#login_required = deco1(name = "root")
# add = login_required(add)
name = input("请输入你的name:")
@deco1(name)
def add(a,b):
        return a+b

用类实现装饰器

#基于类实现装饰器
import time
class A:
    def __init__(self,func):
        self.func = func
    def __call__(self, *args, **kwargs):    #-->inner
        start = time.time()
        print("this is call")
        result = self.func(*args, **kwargs)
        time.sleep(1)
        end = time.time()
        print(end - start)
        return result

@A
def func1(a,b):
    print("i am func1...")
    return a+b

print(func1(1,2))

#带参数的
import time
class A:
    def __init__(self,name):
        self.name = name
    def __call__(self,func):    #-->inner
        def deco(*args,**kwargs):
            start = time.time()
            result = func(*args, **kwargs)
            end = time.time()
            print(f"函数执行花了{end - start}")
            return result
        return deco

@A(name = input("请输入name:"))
#a1 = A("name")
#func1 = a1(func1)
def func1(a,b):
    print("i am func1...")
    return a+b

print(func1(1,2))

装饰类

def outer(cls):
    def inner(*args,**kwargs):
        print(f"class name is:{cls.__name__}")
        return cls(*args,**kwargs)
    return inner

@outer    #A = outer(A)
class A:
    def __init__(self,name):
        self.name = name

print(type(A))
m = A("sc")
print(m.name)
  • 1
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 1
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论 1
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值