python中的闭包和装饰器

闭包

闭包的定义:(1)在函数中又定义了一个函数,并且这个内部函数用到了外部函数的变量,那么将内部的函数称为闭包。(2)内部函数对外部函数作用域里变量的引用(非全局变量),则称内部函数为闭包。

def addNumber(a):
    def add(b):
        print(a+b)
    return add
c = addNumber(10)
c(20)
c(50)
结果是30,60

global适用于函数内部修改全局变量的值

nonlocal适用于嵌套函数中内部函数修改外部变量的值

如果没有使用以上关键字,对全局变量或者外部变量进行修改,python会默认将全局变量或外部变量隐藏起来

def counter(start=0):
    def incr():
        nonlocal start
        start += 1
        return start
    return incr
c1 = counter(5)
print(c1())
print(c1())
c2 = counter(50)
print(c2())
print(c2())

print(c1.__closure__)

6
7
51
52
(<cell at 0x06ADFB10: int object at 0x502AC070>,)

闭包三要素

1)必须有一个内嵌函数(函数里定义的函数)——这对应函数之间的嵌套
2)内嵌函数必须引用一个定义在闭合范围内(外部函数里)的变量——内部函数引用外部变量
3)外部函数必须返回内嵌函数——必须返回那个内部函数

形成闭包之后,闭包函数会获得一个非空的__closure__属性,这个属性是一个元组。元组里面的对象为cell对象,而访问cell对象的cell_contents属性则可以得到闭包变量的当前值(即上一次调用之后的值)。而随着闭包的继续调用,变量会再次更新。所以可见,一旦形成闭包之后,python确实会将__closure__和闭包函数绑定作为储存闭包变量的场所。

所谓闭包其实是创建了一片内存空间,这个内存空间中包含了内部函数和外部变量,外部变量对于内部函数来说相当于是全局变量的存在。一般情况下,在我们认知当中,如果一个函数结束,函数的内部所有东西都会释放掉,还给内存,局部变量都会消失。但是闭包是一种特殊情况,如果外函数在结束的时候发现有自己的临时变量将来会在内部函数中用到,就把这个临时变量绑定给了内部函数,然后自己再结束。

装饰器

python装饰器就是用于拓展原来函数功能的一种函数,这个函数的特殊之处在于它的返回值也是一个函数,使用python装饰器的好处就是在不用更改原函数的代码前提下给函数增加新的功能。

import time
def getTime(func):
    def wapper():
        starttime = time.time()
        func()
        endtime = time.time()
        print(endtime-starttime)
    return wapper
@getTime
def login():
    print("填写姓名")
    time.sleep(2)
login()

填写姓名
2.016256093978882

装饰器原理:

当python解释器执行到@getTime这句话的时候,会去调用getTime函数,同时将被装饰的函数名login作为参数传入,在执行getTime函数的时候,此时直接把wapper函数返回了,同时把它赋值给login,此时的login已经不是未加装饰时的login了,而是指向了函数地址。相当于是  login = getTime(login)

接下来,在调用login()的时候,其实调用的是wapper函数,那么此时就会先执行计算时间,然后再调用原来的login(),该处的login就是通过装饰传进来的参数login。这样下来,就完成了对login的装饰,实现了计算时间的功能。当python解释器执行到@getTime时,就开始进行装饰了

一个函数有多个装饰器过程

当有两个或两个以上装饰器装饰函数时,先装饰后面的,执行时先执行前面的,装饰顺序从下到上,执行顺序从上到下。

def makeBold(fun):
    print('----a----')

    def inner():
        print('----1----')
        return '<b>' + fun() + '</b>'

    return inner
def makeItalic(fun):
    print('----b----')

    def inner():
        print('----2----')
        return '<i>' + fun() + '</i>'

    return inner
@makeBold    #后装饰先执行
@makeItalic  #先装饰后执行
def test():
    print('----c----')
    print('----3----')
    return 'hello python decorator'


ret = test()
print(ret)

----b----
----a----
----1----
----2----
----c----
----3----
<b><i>hello python decorator</i></b>

为什么呢,分两步来分析一下。

  1. 装饰时机 通过上面装饰时机的介绍,我们可以知道,在执行到@makeBold的时候,需要对下面的函数进行装饰,此时解释器继续往下走,发现并不是一个函数名,而又是一个装饰器,这时候,@makeBold装饰器暂停执行,而接着执行接下来的装饰器@makeItalic,接着把test函数名传入装饰器函数,从而打印’b’,在makeItalic装饰完后,此时的test指向makeItalic的inner函数地址,这时候有返回来执行@makeBold,接着把新test传入makeBold装饰器函数中,因此打印了’a’。
  2. 在调用test函数的时候,根据上述分析,此时test指向makeBold.inner函数,因此会先打印‘1‘,接下来,在调用fun()的时候,其实是调用的makeItalic.inner()函数,所以打印‘2‘,在makeItalic.inner中,调用的fun其实才是我们最原声的test函数,所以打印原test函数中的‘c‘,‘3‘,所以在一层层调完之后,打印的结果为<b><i>hello python decorator</i></b> 。

装饰带有参数的或有返回值的函数的装饰器

对于带有参数或有返回值的函数进行装饰时,我们需要在装饰器的内部函数中也传入相应的参数和返回被装饰的函数

def  yanzheng(func):
    def wapper(a):
        print("验证权限")
        return func(a)
    return wapper
@yanzheng
def add(a):
    return a+2
print(add(3))
#下面是通用装饰器写法
def decato(func):
    def inner(*args,**kwargs):
        #装饰操作
        print("验证权限")
        return func(*args,**kwargs)
    return inner

自身带参数的装饰器

有时候有需要装饰器自身带有参数,比如权限验证时候对不同权限的人加上不同的权限验证。

带参数的装饰器其实就是将原来的装饰器看做内部函数,外部加上一个函数,将内部函数返回,并将需要传递的参数当做外部函数的参数即可。

def decatorr(level):
    def decato(func):
        def inner(*args,**kwargs):
            #装饰操作
            if level ==1:
                print("验证权限1")
            else:
                print("不需要权限验证")
            return func(*args,**kwargs)
        return inner
    return decato
@decatorr(1)
def add(a):
    return a+2
print(add(10))


验证权限1
12
不需要权限验证
delete一些东西

类装饰器

除了函数可以做装饰器外,类也可以作为装饰器来使用。

装饰器函数其实是一个接口约束,它必须接受一个callable对象作为参数,然后返回一个callable对象。 
在python中,一般callable对象都是函数,但是也有例外。比如只要某个对象重写了__call__方法,那么这个对象就是callable的。

类装饰器就是利用了对象创建时候调用__init__()方法和实例名()时调用__call__方法的原理:将函数当做参数传入到类的__init__()方法中,然后生成对象()形式会自动调用__call__()方法。

class Mydecator:
    def __init__(self,func):
        print("类初始化代码")
        self._func=func
    def __call__(self, *args, **kwargs):
        print("装饰代码")
        return self._func(*args,**kwargs)
@Mydecator                     #相当于 test=Mydecator(test)
def test(str):
    return "打印的语句是%s"%str

print(test("类装饰器"))

类初始化代码
装饰代码
打印的语句是类装饰器

带参数的类装饰器

带参数和不带参数的类装饰器有很大的不同。

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

class logger(object):
    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")

使用偏函数与类实现装饰器

import functools
import time


class DelayFunc:
    def __init__(self, duration, func):
        self.duration = duration
        self.func = func

    def __call__(self, *args, **kwargs):
        print(f'Wait for {self.duration} seconds...')
        time.sleep(self.duration)
        return self.func(*args, **kwargs)


def delay(duration):
    """
    装饰器:推迟某个函数的执行。
    同时提供 .eager_call 方法立即执行
    """
    # 此处为了避免定义额外函数,
    # 直接使用 functools.partial 帮助构造 DelayFunc 实例
    return functools.partial(DelayFunc, duration)


@delay(1)
def add(a, b):
    return a + b


add(2, 5)

 

使用装饰器实现单例模式

def Singleton(cls):
    _instance = {}

    def _singleton(*args, **kargs):
        if cls not in _instance:
            _instance[cls] = cls(*args, **kargs)
        return _instance[cls]

    return _singleton


@Singleton
class A(object):
    a = 1

    def __init__(self, x=0):
        self.x = x


a1 = A(2)
a2 = A(3)

 

 

 

 

 

 

 

 

 

 

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

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

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值