0
点赞
收藏
分享

微信扫一扫

Python(十四、闭包和装饰器)

颜路在路上 2021-09-27 阅读 39

闭包

def test(x, y):
    def t1(z):
        return x * z + y

    return t1


t = test(2, 3)
print(t(3))
print(t(4))

闭包修改外部外部嵌套函数的变量值

# 错误案例
def test():
    msg=10
    def t1():
        print("%d"%msg)
        msg=100
        print("%d" % msg)
    return t1
t = test()
t()
"""
此时,第一个msg不会输出10而是直接报错,因为python会认为
t1函数扫描发现下面定义的有msg,类似于声明提升,但是没赋值,所以不允许使用
如果想使用,加上nonlocal,如下面的案例
"""

 # nonlocal适用于嵌套函数中内部函数修改外部变量的值(该属性python3专用)
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())
print(c1())

print(c2())
print(c2())

python2等效案例

def counter(start=0):
    count=[start]
    def incr():
        count[0] += 1
        return count[0]
    return incr

c1 = closeure.counter(5)
print(c1())  # 6
print(c1())  # 7
c2 = closeure.counter(100)
print(c2())  # 101
print(c2())  # 102

装饰器

基本案例

def t1(func):
    print("装饰器的装饰过程是在代码加载的时候就执行的")

    def t2():
        return func
    return t2


@t1
def T():
    print("t执行")
    return "haha"


t = T()
print(t())
"""
执行的结果:
装饰器的装饰过程是在代码加载的时候就执行的   
t执行
haha


说明:第一句输出,即使在t=T()没写的情况下也会执行,是解释器的自主行为
"""

装饰器的基本原理(闭包)

def t1(func):
    def t2():
        return func

    return t2


def T():
    print("t执行")
    return "haha"


T = t1(T)
t = T()
print(t())
"""
执行的结果:
t执行
haha

"""

多装饰器装饰同一个函数

def t1(func):
    print("t1初始化")

    def t2():
        print("t2执行")
        func()
    return t2


def t3(func):
    print("t3初始化")

    def t4():
        print("t4执行")
        func()
    return t4
@t3
@t1
def T():
    print("T执行")


t = T()
"""
执行的结果:
t1初始化
t3初始化
t4执行
t2执行
T执行

说明:初始化是解释器从上到下,所以到@t3时候,进行装饰,但是装饰必须先有函数传入(联想闭包)
所以肯定先去获取函数,所以执行到@t1,所以t1初始化先执行

但是装饰完毕之后,代码在一个内存中合并起来,是从上倒下的,所以t4先执行
"""

被装饰器的函数有参数

def t1(func):
    def t2(a, b):
        print("t2执行")
        func(a, b)

    return t2


@t1
def T(a, b):
    print("T执行", a, b)


T(1,2)
"""
执行的结果:
t2执行
T执行 1 2
"""

被装饰的函数有不定长参数

def t1(func):
    def t2(*args, **kwargs):
        func(*args, **kwargs)

    return t2


@t1
def T(*args, **kwargs):
    print("T执行", args,kwargs)


T(1,2,3,num=10)
"""
执行的结果:
T执行 (1, 2, 3) {'num': 10}
"""

带参数的装饰器

def t0(msg="hello"):
    def t1(func):
        def t2(*args, **kwargs):
            func(*args, **kwargs)
            print(msg)

        return t2

    return t1


@t0("zengqiang")
def T(*args, **kwargs):
    print("T执行", args, kwargs)


T(1, 2, 3, num=10)
"""
执行的结果:
T执行 (1, 2, 3) {'num': 10}
zengqiang

总结:带参数的装饰器,比一般装饰器要多一层函数定义
"""

类装饰器(扩展)

class Test():
    def __call__(self):
        print('call me!')

t = Test()
t()  # call me

类装饰器

class Test(object):
    def __init__(self, func):
        print("---初始化---")
        print("func name is %s"%func.__name__)
        self.__func = func
    def __call__(self):
        print("---装饰器中的功能---")
        self.__func()
#说明:
#1. 当用Test来装作装饰器对test函数进行装饰的时候,首先会创建Test的实例对象
#   并且会把test这个函数名当做参数传递到__init__方法中
#   即在__init__方法中的属性__func指向了test指向的函数
#
#2. test指向了用Test创建出来的实例对象
#
#3. 当在使用test()进行调用时,就相当于让这个对象(),因此会调用这个对象的__call__方法
#
#4. 为了能够在__call__方法中调用原来test指向的函数体,所以在__init__方法中就需要一个实例属性来保存这个函数体的引用
#   所以才有了self.__func = func这句代码,从而在调用__call__方法中能够调用到test之前的函数体
@Test
def test():
    print("----test---")
test()
showpy()#如果把这句话注释,重新运行程序,依然会看到"--初始化--"

"""
运行结果如下:

---初始化---
func name is test
---装饰器中的功能---
----test---

"""

举报

相关推荐

0 条评论