解惑 python3 闭包、装饰器

一、闭包

定义:内部函数包含对外部作用域而非全局作用域变量的引用,该内部函数称为闭包函数

nonlocal关键字:如果想要在内函数中修改外函数的值,需要使用 nonlocal 关键字声明变量(见例子6)

判断是否为闭包:print( 内层函数名.__closure__ ) 结果为cell说明为闭包,结果为None说明不是闭包

注意:当程序执行遇到函数执行时,会在内存空间开辟局部命名空间,当函数执行完毕,该命名空间会被销毁。但是如果这个函数内部形成闭包,则该内存空间不会随着函数执行完毕而消失。

闭包的好处:当函数开始执行时,如果遇到了闭包,它有一个机制,它会永远开辟一个内存空间,将闭包中的变量等值放入其中,不会随着函数的执行完毕而消失。

闭包函数伪代码:

def 外层函数(参数):
    def 内层函数():
        print("内层函数执行:",参数)
    return 内层函数

内层函数的引用 = 外层函数("传入参数")
内层函数的引用()


注意:
外层函数中的参数不一定存在,根据情况而定,
但是一般情况下都会有并在内层函数中使用到。

举例:

例子1:
def wrapper():
    name = 'summer'
    def inner():
        print(name)
    inner()
    print(inner.__closure__)
wrapper()     
执行输出:
summer
(<cell at 0x0000017FC9C90B58: str object at 0x0000017FCA349AD0>,)


例子2:函数调用了外层变量而非全局变量,那么它就是闭包。
name = 'summer'
def wrapper():
    def inner():
        print(name)
    inner()
    print(inner.__closure__)
wrapper() 
结果输出:
summer
None


例子3:只要引用了外层变量至少一次,非全局的,它就是闭包
name = 'summer'
def wrapper2():
    name1 = 'spring'
    def inner():
        print(name)
        print(name1)
    inner()
    print(inner.__closure__)
wrapper2()
结果输出:
summer
spring
(<cell at 0x030B7310: str object at 0x03043680>,)


例子4:
虽然wraaper2传了一个全局变量,
但是在函数wraaper2内部,inner引用了外层变量,
相当于在函数inner外层定义了 n = 'summer',
所以inner是一个闭包函数。    
name = 'summer'
def wraaper2(n):  #相当于n = 'summer' 
  def inner():
        print(n)
    inner()
    print(inner.__closure__)  
wraaper2(name)
结果输出:
summer
(<cell at 0x03867350: str object at 0x037F3680>,)


例子5:
def func(a, b):
    def line(x):
        return a * x - b

    return line


line = func(2, 3)
print(line(5))
结果输出:
7

例子6:nonclocal关键字声明变量!
def func(a, b):
    def line(x):
        nonlocal a
        a = 3
        return a * x - b

    return line


line = func(2, 3)
print(line(5))
结果输出:
12

例子7:传如函数
import time
def timmer(f):
    def inner():
        start_time = time.time()
        print('1')
        f()
        print('2')
        end_time = time.time()
        print("此函数的执行时间为:",str(end_time - start_time))
    return inner

def func1():
    print("in func1")
    time.sleep(1)

func12 = timmer(func1)
func12()
结果输出:
1
in func1
2
此函数的执行时间为: 1.0986361503601074

 

二、装饰器 decorator

本质:本质是函数,用 def 定义。

基于类实现的装饰器:

(1)不带参数的类装饰器:必须实现__call__ 和__init__ 这两个内置函数。(例子4)

__init__: 接收被装饰函数 ; __call__ : 实现装饰逻辑。

(2)带参数的类装饰器:必须实现__call__ 和__init__ 这两个内置函数。(例子5)

__init__: 不再接收被装饰函数,而是接收传入参数 ; __call__ :接收被装饰函数, 实现装饰逻辑。

功能:装饰其他函数,即为其他函数添加附加功能。

原则:不能修改被装饰的函数的源代码、调用方式、执行结果。即:装饰器对待被修饰的函数是完全透明的。

必须满足的条件:闭包、外部函数的返回值必须是内部函数。

应用场景:插入日志、性能测试、事务处理、缓存....

functools.wraps装饰器:作用就是将被修饰的函数的一些属性值赋值给修饰器函数,最终让属性的显示更符合我们的直觉。(例子6 例子7)

内置装饰器 property:通常存在与类中,可以将一个函数定义成一个属性,属性的值就是该函数的return的内容。同时会将这函数变成另外一个装饰器。私有属性添加@XXX.setter 、@XXX.deleter方法(例子8)

例子:

例子1:日志打印器

def logger(func):
    def wrapper(*args, **kwargs):
        print('我准备开始计算:{}函数了'.format(func.__name__))

        #真正执行的是这行
        func(*args, **kwargs)
        print('哈哈,我计算完啦。给自己加个鸡腿!')
    return wrapper

@logger
def add(x, y):
    print('{}+{}={}'.format(x, y, x+y))

add(100, 100)
输出结果:
我准备开始计算:add函数了
100+100=200
哈哈,我计算完啦。给自己加个鸡腿!



例子2:时间计时器--计算函数执行时长

import time
def timer(func):
    def wrapper(*args, **kwargs):
        t1 = time.time()
        # 这是函数真正执行的地方
        func(*args, **kwargs)
        t2 = time.time()
        print('花费时长:{}秒'.format(t2-t1))
    return wrapper

@timer
def want_sleep(sleep_time):
    time.sleep(sleep_time)

want_sleep(10)
输出结果:
花费时长:10.000004291534424秒


例子3:带参数的函数装饰器 --- 多一层嵌套传参即可!

def say_hello(country):
    def wrapper(func):
        def deco(*args, **kwargs):
            if country == 'China':
                print('你好!')
            elif country == 'america':
                print('hello!')
            else:
                return
            #真正执行函数的地方
            func(*args, **kwargs)
        return deco
    return wrapper

@say_hello('China')
def chinese():
    print('我来自中国!')

@say_hello('america')
def america():
    print('I am from America!')

america()
print('------------')
chinese()
结果输出:
hello!
I am from America!
------------
你好!
我来自中国!
---------------------------------------------------------------------------------------

例子4:不带参数的类装饰器

class logger():
    def __init__(self, func):
        self.func = func

    def __call__(self,*args,**kwargs):
        print("[INFO]:the function {func}() is running..."
              .format(func = self.func.__name__))
        return self.func(*args,**kwargs)
@logger
def say(something):
    print("say {}".format(something))
    
say("hello")
输出结果:
[INFO]:the function say() is running...
say hello


例子5:带参数的类装饰器

class logger():
    def __init__(self, level = 'INFO'):
        self.level = level

    def __call__(self,func):  # 接收函数
        def wrapper(*args,**kwargs):
            print("[{level}]:the function {func}() is running..."
                    .format(level = self.level,func = func.__name__))
            func(*args,**kwargs)
        return wrapper #返回函数
@logger(level = 'WARNING')
def say(something):
    print("say {}".format(something))
    
say("hello")
结果输出:
[WARNING]:the function say() is running...
say hello

---------------------------------------------------------------------------------------

例子6:
def wrapper(func):
    def inner_function():
        pass
    return inner_function

@wrapper
def wrapped():
    pass

print(wrapped.__name__)
输出结果:
inner_function

例子7:
from functools import wraps
def wrapper(func):
    @wraps(func) 
    def inner_function():
        pass
    return inner_function
@wrapper
def wrapped():
    pass

print(wrapped.__name__)
输出结果:
wrapped

----------------------------------------------------------------------------------

例子8: @property内置装饰器应用

class Student(object):
    def __init__(self, name):
        self.name = name
        self.name = None

    @property
    def age(self):
        return self._age

    @age.setter
    def age(self, value):
        if not isinstance(value, int):
            raise ValueError('输入不合法:年龄必须为数值!')
        if not 0 < value < 100:
            raise ValueError('输入不合法:年龄范围必须0-100')
        self._age=value

    @age.deleter
    def age(self):
        del self._age

XiaoMing = Student("小明")

# 设置属性
XiaoMing.age = 25

# 查询属性
XiaoMing.age

# 删除属性
del XiaoMing.age

参考链接:https://zhuanlan.zhihu.com/p/65968462

 

 

  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值