Python之闭包与装饰器


闭包

当有两个函数嵌套,且内层函数使用了外部函数的变量,且外层函数以内层函数为返回值,则称这个内层函数为闭包(closure),外层函数称为闭包函数。

闭包的作用是生成一个函数,这个函数可以以外部函数的局部变量为基点,进行各种操作。

闭包使得外层函数的局部变量在外部函数调用完后,不会被系统自动销毁。只有闭包调用完后才会销毁。

闭包的简单使用
def out_func(....):
    #...
    def inner_func(....):
        #...(使用out_func中的局部变量)
        
return inner_func()

如:

def n_power(n):   #外部函数
    exponent=n
    
    def power_of(base):    #内部函数
        result = base**exponent   #使用外部函数的局部变量exponent
       	return result
    
    return power_of  #返回内部函数

闭包作为函数被返回,这使得外层函数就像一个函数生产器:以自己的变量(参数)为基准,制造出一个新函数。

square=n_power(2)  #根据参数装配新函数
cube=n_power(3)

print(square(4))
#16
print(cube(3))
#27

又如:

def person_speak(name):
    def speak(words):
        print(f'{name} said:"{words}"')

    return speak


jack_speak = person_speak("Jack")
rose_speak = person_speak("Rose")
rose_speak("I promise.")
jack_speak("Never let go.")
rose_speak("I'll never let go.I'll never let go,Jack.")


"""
Rose said:"I promise."
Jack said:"Never let go."
Rose said:"I'll never let go.I'll never let go,Jack."
"""

相比于直接用一个函数,两个参数,speak(name,words),闭包的代码更清晰,也更实用

在闭包内修改外部局部变量的值

类似于用关键字global声明全局变量,用nonlocal声明外部变量。

如:

def out_func():
    i=5
    
    def inner_func():
    	i=6
        
    inner_func()
    print(i)
    return inner_func

out_func()
# 5

就跟在函数内直接重新定义全局变量一样,这只是为内层函数自己重新创建了一个新的变量i而已,并不是在修改外层函数的i.

def out_func():
    i=5
    
    def inner_func():
    	nonlocal i   #声明i为外层函数的i
        i=6
        
    inner_func()
    print(i)
    return inner_func

out_func()
# 6

装饰器

装饰器是一个函数,它以已有的函数作为参数,再通过闭包在已有函数的基础上增加新的代码(功能),并将装饰后的函数返回。

特点
  • 不修改已有函数的源代码
  • 不修改已有函数的调用方式
  • 给已有函数增加新功能
  • 可重复利用
使用方法
#定义装饰器
def decorator(func_to_decotate):
    
    def decorated(*args, **kwargs):
        #...(额外功能)
        result=func_to_decotate(*args, **kwargs)
        #...(额外功能)
        return result
    
    return decorated

#使用装饰器之普通版本
def func_to_decotate(a,b,c):
    #...(已有函数)

decorated_func=decorator(func_to_decotate)   #获取装饰完后的新函数
decorated_func(...)   #调用装饰完后的新函数

#使用装饰器之语法糖版本
@decorator     #相当于自动执行func_to_decotate=decorator(func_to_decotate=decorator)
def func_to_decotate(a,b,c):
    #...(已有函数)
  • 装饰后的函数即为装饰器内部的闭包
  • 在函数定义前加上@decorator,可以直接获得装饰完的函数,且函数名不变

如:

def lines(func):
    
    def inner(*args, **kwargs):
    	print("*"+"-"*18+"*")
    	result=func(*args, **kwargs)
        print("-"*20)
        print("*"+"-"*18+"*")
        return result
    
    return inner

#普通版本
def print1(words):
    print(words)
    
print1=lines(print1)
print1("Hello,world!")

"""
*------------------*
Hello,world!
--------------------
*------------------*
"""

#语法糖版本
@lines
def print2(words):
    print(words)
    
print2("Hello,python!")

"""
*------------------*
Hello,python!
--------------------
*------------------*
"""
运行时间装饰器
import time


def timer(func):
    def inner(*args, **kwargs):
        print("*" * 2 + "-" * 16 + "*" * 2)
        begin_time = time.time()
        result=func(*args, **kwargs)
        end_time = time.time()
        run_time = end_time - begin_time
        print(f"运行时间:{run_time:.2f} s")
        print("*" * 2 + "-" * 16 + "*" * 2)
        return result

    return inner


@timer
def test(words):
    for i in range(50000000):
        if i == 4000:
            print(words)


test("Python")

"""
**----------------**
Python
运行时间:1.78 s
**----------------**
"""
多个装饰器
@decorator1
@decorator2
def func(...):
    ...

等价于

func=decorator2(func)
func=decorator1(func)
  • 即多个装饰器由内而外依次进行装饰

如:

import time


def lines(func):
    
    def inner(*args, **kwargs):
    	print("*"+"-"*18+"*")
    	result=func(*args, **kwargs)
        print("-"*20)
        print("*"+"-"*18+"*")
        return result
    
    return inner

def timer(func):
    
    def inner(*args, **kwargs):
        begin_time = time.time()
        result=func(*args, **kwargs)
        end_time = time.time()
        run_time = end_time - begin_time
        print(f"运行时间:{run_time:.2f} s")
        return result
        
    return inner

@lines
@timer
def test1(words):
    for i in range(50000000):
        if i == 4000:
            print(words)

@timer
@lines
def test2(words):
    for i in range(50000000):
        if i == 4000:
            print(words)
            
test1("Python")
"""
*------------------*
Python
运行时间:1.67 s
--------------------
*------------------*
"""

test2("Python")
"""
*------------------*
Python
--------------------
*------------------*
运行时间:1.65 s
"""
含参装饰器
def decoratorName(param1,param2...):
    def decorator(func):
        def decorated(*args, **kwargs):
            #...(额外功能,可能用到param1,param2...)
            result=func_to_decotate(*args, **kwargs)
            #...(额外功能,可能用到param1,param2...)
            return result
    
    	return decorated
    return decorator

@decoratorName(param1,param2....)
def func_to_decotate(a,b,c):
    ...
  • 要想在使用装饰器时能够传入参数,可以在 原本的装饰器函数 外面再嵌套一层以 原本的装饰器函数 为返回值的函数
  • 相当于先执行decorator=decoratorName(param1,param2....)@decorator

如:

def math(flag):
        def decorator(func):
            def inner(*args,**kwargs):
                if flag == "+":
                    print("---------加法运算中--------")
                if flag == "*":
                    print("---------乘法运算中--------")
                result=func(*args,**kwargs)
                return result
            return inner
        return decorator

@math("+")
def plus(*args):
    s=0
    for i in args:
        s+=i
    return s

plus(1,2,3,4,5)
"""
---------加法运算中--------
15
"""

@math("*")
def multipy(*args):
    s=1
    for i in args:
        s*=i
    return s
multipy(1,2,3,4,5)
"""
---------乘法运算中--------
120
"""
类装饰器

重写了__call__魔法方法后,类也能像函数一样进行调用。

class Decorator(object):
    def __init__(self, func):
        #加载需装饰的函数
        self.__func = func

    # 实现__call__方法,表示对象是一个可调用对象,可以像调用函数一样进行调用。
    def __call__(self, *args, **kwargs):  #__call__即为装饰后的新函数
        #...(额外功能)
        result=self.__func(*args, **kwargs)
        #...(额外功能)
        return result


@Decorator
def func(a,b,c):
    #...已有函数
  • @Decorator相当于func=Decorator(func),创建了一个名为func的Decorator实例。而Decorator的实例因为有了__call__方法,所以可像函数一样可调用。

  • 1
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 打赏
    打赏
  • 4
    评论

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

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包

打赏作者

木子希卡利

你的鼓励将是我创作的最大动力

¥1 ¥2 ¥4 ¥6 ¥10 ¥20
扫码支付:¥1
获取中
扫码支付

您的余额不足,请更换扫码支付或充值

打赏作者

实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

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

余额充值