什么是装饰器?---python中的装饰器是指任何可以修改函数或类的可调用对象。 当一个函数或类被调用时,它被传递给装饰器,装饰器返回一个修改过的函数/类。 

 

先看几个实例,了解python函数的一些特点。

实例1:

def first_func(value):  # 测试函数
    print(value)


new_name = first_func  # python中函数可以有多个名称,还可以将函数分配给一个或多个变量

first_func('午餐肉')  # 函数名调用
new_name('火腿')  # 函数别名调用

结果为:

午餐肉
火腿

 

实例2:可以使用python内置的函数如map和filter来实现:

def mult(x, y):return x * y  # 返回x,y乘积


def div(x, y):
    return x / y  # 返回x,y相除


def math(func, x, y):
    result = func(x, y)  # 把x,y通过func函数进行处理,函数名可以作为参数传递
    return result


print(math(mult, 4, 2))
print(math(div, 4, 2))

结果:

8
2.0

 

实例3:函数可以嵌套在其他函数中:

def person(name):
    def greetting():
        return "你好,我在和你打招呼,"

    greet = greetting() + name + "!"  # 调用内部函数greetting和参数name及“!”组合
    return greet


print(person('小强'))
你好,我在和你打招呼,小强!

 

实例4:函数可以作为其他函数的参数(因为函数是对象,因此可以用作参数。python一切皆对象)

def greeting(name):
    return '' + name


def call_me(func):  # 接收其他函数作为参数
    nickname = '伯虎'
    return func(nickname)


print(call_me(greeting))
唐伯虎

 

实例5:函数可以返回函数:

def func_1():
    def saying():
        return '发生什么事了?'

    return saying


a = func_1()
print(a())  # 这里注意,是a()而不是a;a是返回的函数名,a()执行内部函数
发生什么事了?

 

实例6:嵌套函数可以访问其父函数的作用域,这称为闭包。必须注意,这种访问是只读的。嵌套函数不能改变外部作用域。

def func_1(name):
    def greeting():
        return '你好,我是' + name

    return greeting


greet = func_1('马保国')
print(greet())
你好,我是马保国

试着改变父函数作用域的值,报错,因为输入的函数func_2只是其传递给greeting函数,并不是变量:

def func_2(name):
    test=100
    def greeting():
        test +=100
        return '你好,我是' + name

    return greeting
greet = func_2('马保国')
print(greet())
G:\pyprojects\python.exe G:/pyprojects/hello.py
Traceback (most recent call last):
  File "G:/pyprojects/hello.py", line 17, in <module>
    print(greet())
  File "G:/pyprojects/hello.py", line 12, in greeting
    test +=100
UnboundLocalError: local variable 'test' referenced before assignment

Process finished with exit code 1

 

有以上基础,即可以讨论装饰器了。

装饰器将一个函数包装在另一个函数中,该函数以某种方式修改原始函数,如添加功能、修改参数或结果等等。

装饰器主要工作是在其中定义wrapper函数。wrapper英文意思为包装纸,包装材料。形象的描述。

wrapper()函数是一个嵌套的函数,进行实际执行修改工作,尽管调用的是装饰器的名称。

 

def fun_decorator(func):  # 装饰器函数
    def wrapper():  # 实际工作区
        print("这里是装饰器进行装饰的功能区域")
        for i in range(10):
            print(i)
        print('装饰结束,返回原功能')
        print(func())

    return wrapper


def test():  # 测试函数,测试装饰器功能
    text = "我是马保国,浑元形意门掌门人"
    return text


test = fun_decorator(test)  # 通过此句test已经被fun_decorator装饰

test()  # 调用,查看结果

结果:

G:\pyprojects\venv\Scripts\python.exe G:/pyprojects/hello.py
这里是装饰器进行装饰的功能区域
0
1
2
3
4
5
6
7
8
9
装饰结束,返回原功能
我是马保国,浑元形意门掌门人

Process finished with exit code 0

查看结果可知,原简单的test函数,确实被增加了功能。

 

其实可以通过语法糖@,来替代行:test = fun_decorator(test)的功能。

语法糖是指:编程语言中一种特殊的语法,旨在通过其使代码更易于读写,令程序员更加轻松。

def fun_decorator(func):  # 装饰器函数
    def wrapper():  # 实际工作区
        print("这里是装饰器进行装饰的功能区域")
        for i in range(10):
            print(i)
        print('装饰结束,返回原功能')
        print(func())

    return wrapper


@fun_decorator
def test():  # 测试函数,测试装饰器功能
    text = "我是马保国,浑元形意门掌门人"
    return text


test()  # 调用,查看结果

运行后结果一致。所以只需在待测试函数上一行加 @装饰器函数,就可达到目的。

 

装饰器的工作原理:当一个带有修饰器的函数被调用时,装饰器函数会捕捉这个调用,执行其工作。完成装饰器函数的工作后,移交给原始函数,从而进行后续任务。从本质上说,

装饰器劫持了函数调用,修改原始的结果,并将修改结果替换为原始函数提供的结果。

 

如果一个被修饰后的函数被赋值给一个变量,那么这个变量每次可以用被修饰后的函数功能,而不是原始的函数。

 

装饰器显然可以应用到多个函数中去,实现复用的功能。否则还不如直接将功能写入某个函数中。

 

 

综合实例:

 

import math


def arg_check(func):
    def wrapper(num):  # 通过装饰器来实现判断参数合法性
        if type(num) != int:
            raise TypeError('参数不是整数!')
        elif num <= 0:
            raise ValueError('参数不是正数!')
        else:
            return func(num)
    return wrapper


@arg_check
def circle_measures(radius):  # 给出半径算出圆属性
    zhouchang = 2 * math.pi * radius  # 圆周长
    area = math.pi * radius * radius  # 圆面积
    zhijing = 2 * radius  # 直径
    return (zhijing, zhouchang, area)


a, b, c = circle_measures(6)
print('直径为:', a, '\n周长为:', b, '\n面积为:', c)

结果:

G:\pyprojects\venv\Scripts\python.exe G:/pyprojects/zhuangshiqi.py
直径为: 12 
周长为: 37.69911184307752 
面积为: 113.09733552923255

Process finished with exit code 0
# 装饰器检查值是否为整数,是否为正数,如果满足要求,允许函数完成,否则报错