python之装饰器

复制代码
1、函数名的应用
1、函数名是一个特殊的变量,函数名存放的是函数的内存地址
def func():
    print('hello')


print(func)  # <function func at 0x000001A228B01E18>


2、函数名能够做为一个变量
def func():
    print('hello')


f = func  # 把函数名当成变量赋值给另一个变量
f()  # 经过变量f调用函数


3、函数名能够做为容器类型的元素
def func1():
    print('func1')
    

def func2():
    print('func2')
    
    
def func3():
    print('func3')
    
    
def func4():
    print('func4')


list1 = [func1, func2, func3, func4]
for f in list1:
    f()


4、函数名能够做为函数的参数
def func1():
    print('func1')


def func2(arg):
    print('func2')
    arg()  # 执行传递进来的arg

    
func2(func1)  # 把func1当成参数传递给func2


5、函数名能够做为函数的返回值
def func1():
    print('func1')

    def func2():
        print('func2')
    return func2  # 把func2当成返回值返回


ret = func1()  # 调用func1,把返回值赋值给ret
ret()  # 调用ret



2、闭包
1、定义
内层函数对外层函数(非全局)的变量的引用,这个内层函数就成为闭包。
在Python中,咱们可使用__closure__来检测函数是不是闭包。
有cell元素的是闭包。


例如:
def func():
    name = '番薯'

    def func2():
        print(name)  # 引用外层函数的变量
    func2()
    print(func2.__closure__)  # (<cell at 0x000002591EA794F8: str object at 0x000002591EACB500>)

func()
print(func.__closure__)  # None


2、闭包的例子
2-1、这里并无引用外层函数的变量,而是把外层函数的变量传给func2,因此不算闭包
def func1():
    name = '番薯'

    def func2(arg):
        print(arg)
    func2(name)
    print(func2.__closure__)
    
    
func1()


2-2def func1():
    name = '番薯'

    def func2():
        print(name)  # 引用外层函数的变量,造成闭包
    func2()
    print(func2.__closure__)


func1()


2-3def func1(name):
    def func2():
        print(name)  # 引用外层函数的变量,造成闭包
    func2()
    print(func2.__closure__)


func1('番薯')


3、闭包的应用
3-1、把内部函数(闭包)当成返回值返回就可使用闭包了
def func1():
    name = '番薯'

    def func2():
        print(name)

    return func2  # 把内部函数当成是返回值返回


ret = func1()  # 把返回值赋值给变量ret
ret()  # 调用内部函数


3-2、多层嵌套的闭包
def func1():
    def func2():
        def func3():
            print('func3')
        return func3
    return func2


f2 = func1()  # func2
f3 = f2()  # func3
f3()


4、闭包的好处
能够在任什么时候间从外界访问到内部函数。
可是咱们知道一个函数执行完毕后,这个函数中的变量以及局部命名空间中的内容都是会被销毁的。
在闭包中若是变量被销毁了,那内部函数就不能正常执行了。
因此一旦这个内部函数引用了外层函数中的变量造成了闭包,那么这个变量将不会随着外层函数的结束而销毁,它会在内存中保留。
也就是说,闭包能够保留变量的引用。



3、装饰器初识
1、开放封闭原则
软件设计的原则: 开闭原则, 又被成为开放封闭原则。
开放封闭原则是指对扩展代码的功能是开放的,
可是对修改源代码是封闭的。
这样的软件设计思路能够保证咱们更好的开发和维护咱们的代码。


好比:你开了一家老字号(下面的代码就至关于源码)
def old_shop():
    print('离百年老字号还差99年')


old_shop()


而后你的老店要新增空调(至关于新增功能):
def old_shop():
    print('新增空调')
    print('离百年老字号还差99年')


old_shop()

这样添加功能确实是能够的,可是违背了开放封闭原则,直接在源码上进行修改了。
那怎么办呢?新建一个函数不就行了
def old_shop_with_conditioner():
    print('新增空调')
    print('离百年老字号还差99年')


old_shop_with_conditioner()

可是,问题又来了,你的老字号很火,开了不少分店,每家分店都是调用了以前的old_shop函数开店,
那么你修改了以后,全部调用原来函数的分店都须要从新调用新的函数,这么作实际上是很番薯的行为。

那么如何在不改变函数的结构和调用方式的基础上,动态的给函数添加功能呢?
能够利用闭包
def old_shop():
    print('离百年老字号还差99年')


def a(func):
    def b():
        print('新增空调')
        func()
    return b


ret = a(old_shop)  # 内层的b函数
ret()

而后问题又来了,如今虽然没有直接修改源码,可是函数名仍是改变了,那又怎么办?
重命名不就好了吗:
def old_shop():
    print('离百年老字号还差99年')


def a(func):
    def b():
        print('新增空调')
        func()
    return b


old_shop = a(old_shop)  # 内层的b函数
old_shop()

这样不就遵循了开发封闭原则了吗,即没有修改源码,扩展代码又是开放的,也没有改变函数原来的调用方式


2、装饰器语法糖
刚才上面的代码只是装饰器的原理和雏形,Python中针对于上面的功能提供了一个快捷的写法,俗称装饰器语法糖。
使用装饰器语法糖的写法,实现一样功能的代码以下:
def a(func):  # a是咱们定义的装饰器函数,func是被装饰的函数(old_shop)
    def b():
        print('新增空调')
        func()
    return b


@a    # 至关于把被装饰的函数old_shop当成参数传给a,而后把返回值b再从新赋值给被装饰的函数名old_shop
def old_shop():
    print('离百年老字号还差99年')


old_shop()  # 至关于调用了内层函数b



4、装饰器进阶
1、装饰带返回值的函数
def wrapper(func):
    def inner():
        print('新功能')  # 你要新增的功能
        result = func()  # 拿到被装饰函数的返回值
        return result  # 返回被装饰的函数的返回值
    return inner


@wrapper
def f():
    return 'Hello'


ret = f()
print(ret)


2、装饰带参数的函数
def wrapper(func):
    def inner(x, y):  # 实际执行函数的参数
        print('新功能')
        r = func(x, y)
        return r
    return inner


@wrapper
def my_sum(x, y):
    return x + y


ret = my_sum(1, 2)  # inner(10, 20)
print(ret)

可是通常来讲,咱们把参数设置成动态参数会更便于拓展
如果三个数相加,或者四个数相加,只须要修改my_sum的参数就能够了
def wrapper(func):
    def inner(*args, **kwargs):
        print('新功能')
        r = func(*args, **kwargs)
        return r
    return inner


@wrapper
def my_sum(x, y, z):
    return x + y + z


ret = my_sum(1, 2, 3)
print(ret)


3、带参数的装饰器
装饰器若是要带参数的话,能够嵌套更多层:
def outer(arg):
    def wrapper(func):
        def inner(*args, **kwargs):
            print('欢迎来到%s' % arg)
            func(*args, **kwargs)
        return inner
    return wrapper


@outer('英雄联盟')  # 会先执行outer,而后返回函数名wrapper,至关于@wrapper,在闭包内还能使用最外层的函数变量
def lol():
    print('这里是召唤师峡谷')


@outer('地下城与勇士')  # @wrapper
def dnf():
    print('这里是阿拉德大陆')


lol()
dnf()


4、装饰器修复技术

被装饰的函数最终都会失去原本的__doc__等信息,就是说,若是这个函数被装饰了,
那么它里面的文档信息(注释信息,一般注释信息很重要,注释写明了改函数的功能和参数的信息等)就会消失,
 Python给咱们提供了一个修复被装饰函数的工具,用于找回这些信息。
 from functools import wraps


def wrapper(func):
    @wraps(func)
    def inner(*args, **kwargs):
        print('这是新功能')
        func(*args, **kwargs)
    return inner


@wrapper
def f1(x, y):
    """
    这里写这个函数的主要功能
    :param x: 这个参数的类型
    :param y: 这个参数的类型
    :return: 返回值
    """
    print('我是帅哥')


print(f1.__doc__)  # 打印这个函数的文档信息(注释内容)
print(f1.__name__)  # 打印这个函数名



5、多个装饰器装饰同一函数
def wrapper1(func):
    print('w1')

    def inner1():
        print('inner1')
        return '<i>{}</i>'.format(func())
    return inner1


def wrapper2(func):
    print('w2')

    def inner2():
        print('inner2')
        return '<b>{}</b>'.format(func())
    return inner2


@wrapper1
@wrapper2
def f1():
    return "小明"


ret = f1()
print(ret)

结果:
w2
w1
inner1
inner2
<i><b>小明</b></i>

分析:
在装饰阶段会直接执行装饰函数,并拿到返回值,即
@wrapper2  --->  wrapper2(f1)  ---> print('w2') ---> return inner2 ---> 把变量f1从新指向inner2
@wrapper1 --->  wrapper1(f1)[此时的f1其实是inner2]  ---> print('w1') ---> return inner1 ---> 把变量f1从新指向inner1
而后执行f1()至关于执行inner1()
print('inner1')
return '<i>{}</i>'.format(func())
此时的func是传进来的参数inner2,因此又去执行inner2
print('inner2')
return '<b>{}</b>'.format(func())
此时的func是传进来的参数f1[被装饰的f1],因此拿到返回值<b>小明</b>,拼接到inner1的返回值那里,最后
<i><b>小明</b></i>



5、数码暴龙进化装饰器
1、类装饰器
咱们除了可使用函数装饰函数外,还能够用类装饰函数。
class Page(object):
    def __init__(self, a=None):
        self.a = a
        self.mode = "装饰"

    def __call__(self, *args, **kwargs):
        if self.mode == "装饰":
            self.func = args[0]  # 默认第一个参数是被装饰的函数
            self.mode = "调用"
            return self
        # 当self.mode == "调用"时,执行下面的代码(也就是调用使用类装饰的函数时执行)
        if self.a:
            print("欢迎来到{}页面。".format(self.a))
        else:
            print("欢迎来到首页。")
        self.func(*args, **kwargs)


@Page()
def index(name):
    print("Hello {}.".format(name))


@Page("电影")
def movie(name):
    print("Hello {}.".format(name))


if __name__ == '__main__':
    index('番薯')
    movie('番薯')


2、装饰类
上面全部的例子都是装饰一个函数,返回一个可执行函数。Python中的装饰器除了能装饰函数外,还能装饰类。
可使用装饰器,来批量修改被装饰类的某些方法
# 定义一个类装饰器
class D(object):
    def __call__(self, cls):
        class Inner(cls):
            # 重写被装饰类的f方法
            def f(self):
                print('Hello 番薯')
        return Inner


@D()
class C(object):  # 被装饰的类
    # 有一个实例方法
    def f(self):
        print("Hello world.")


if __name__ == '__main__':
    c = C()
    c.f()

    
    
6、装饰器小结(重)
1、装饰器的标准结构
from functools import wraps


def wrapper(func):  # func:被装饰的函数
    @wraps(func)  # 把func指向的函数的__doc__、__name__等属性复制到inner上面
    def inner(*args, **kwargs):  # *args和**kwargs是被装饰函数的参数
        print('新功能')
        r = func(*args, **kwargs)
        print('新功能也能够在这里')
        return r
    return inner


@wrapper  # 此时会执行wrapper,因此wrapper必须定义在这一行以前
def hello():
    print('Hello World!')


hello()


2、带参数的装饰器
from functools import wraps


def outer(k=None):
    def wrapper(func):
        @wraps(func)
        def inner(*args, **kwargs):
            if k == 'start':
                print('节目开始')
                r = func(*args, **kwargs)
                return r
            else:
                print('节目还未开始')
        return inner
    return wrapper


@outer('start')
def hello():
    """这里是hello函数"""
    print('Hello World!')


hello()
print(hello.__doc__)
print(hello.__name__)


3、多个装饰器同时装饰一个函数
"""
给Hello World!包两层标签,
<div><p>Hello World!</p></div>
"""
from functools import wraps


def wrapper1(func):  # 包p标签
    @wraps(func)
    def inner1(*args, **kwargs):
        r = func(*args, **kwargs)
        return '<p>{}</p>'.format(r)
    return inner1


def wrapper2(func):  # 包div标签
    @wraps(func)
    def inner2(*args, **kwargs):
        r = func(*args, **kwargs)
        return '<div>{}</div>'.format(r)
    return inner2


@wrapper2
@wrapper1
def hello():
    return "Hello World!"


print(hello())










    
复制代码
相关文章
相关标签/搜索