装饰器

  • 定义:本质是函数,功能是:装饰其他函数。就是为其他函数添加附加功能

  • 原则:

     1.不修改被装饰函数的源代码
     2.不能修改被装饰的函数的调用方式
    

    总结:装饰器对被修饰的函数是完全透明的

例题:

def timmer(func):
    def warpper(*args, **kwargs):
        start_time = time.time()
        func()
        stop_time = time.time()
        print("the func time is %s" % (stop_time - start_time))
    return warpper


def test1():
    time.sleep(3)
    print("in the test1")
    
test1()
# 结果是:
# in the test1

再看:

def timmer(func):
    def warpper(*args, **kwargs):
        start_time = time.time()
        func()
        stop_time = time.time()
        print("the func time is %s" % (stop_time - start_time))
    return warpper

@timmer
def test1():
    time.sleep(3)
    print("in the test1")

test1()

#结果:
# in the test1
# the func time is 3.0004844665527344

可以看出:装饰器本身就是函数,装饰器不修改被装饰函数的源代码,不能修改被装饰的函数的调用方式

实现装饰器需要得知识储备:

  • 1.函数就是“变量”

    python是解释型语言:定义函数和定义变量是一样的。先将消息体存放在内存中,在调用。只要在调用之前,内存地址里面有函数体。调用的时候就不会报错
    下面介绍函数就是“变量”是啥意思:
    把pass所代表的函数体,赋值给test变量。定义变量的时候,在内存里找一块地址,存放函数体。(跟变量的概念一样)
    python的内存回收机制是解释器做的,当没有变量指向他的时候。
    有的函数是不要起名字的叫匿名函数:lambda 匿名函数没有函数名,就会立马被回收掉

calc = lambda x:x * 3
print(calc(3))
#结果:
#9
  • 2.高阶函数

    满足下面两个条件之一就是高阶函数:

    • 1.把一个函数名,当做实参传给另一个函数(在不修改被装饰函数源代码的情况下为其添加功能)

    • 2.返回值中包含函数名(不修改函数的调用方式)
      (理念就是函数就是“变量”)

下面演示,按第一个标准(把一个函数名,当做实参传给另一个函数)定义高阶函数

def bar():
    print("in the bar")

def test1(func):
    print(func)

test1(bar)

#结果:<function bar at 0x02AC3810>
#结果是内存地址,类似于门牌号。加上小括号是就是调用这个函数

再看:

def bar():
    print("in the bar")


def test1(func):
    print(func)
    func()  # func就是函数的门牌号,加上(),就是调用这个函数,类似于func = bar,所以func()就是bar(),可以想变量一样赋值


test1(bar)

# 结果:
# <function bar at 0x00B63810>
# in the bar
# 结果是内存地址,类似于门牌号。加上小括号是就是调用这个函数

再看

def bar():
    time.sleep(3)
    print("in the bar")


def test1(func):
    start_time = time.time()
    func()  # 运行的是bar函数
    stop_time = time.time()
    print("the func run time is %s" % (stop_time - start_time))  # test1函数就是给函数附加了一个计时的功能,统计bar的运行时间


test1(bar)
#不能实现装饰器的功能(因为不满足“不能修改被装饰的函数的调用方式”)。至少提供了一种思路:在不修改源代码的情况下,为源代码加上功能。

下面演示按第二个标准(返回值中包含函数名)定义高阶函数:

import time


def bar():
    time.sleep(3)
    print("in the bar")


def test2(func):
    print(func)
    return func


print(test2(bar))
#结果
# <function bar at 0x01723810>      #print内存地址
# <function bar at 0x01723810>      #print打印的返回值
#想法就是:有了内存地址之后,加上括号就是调用函数


test2(bar)      #意思是传的内存地址
test2(bar())    #意思是传的bar()的返回值

再看

import time


def bar():
    time.sleep(3)
    print("in the bar")


def test2(func):
    print(func)
    return func


t = test2(bar)  # 意思是传的内存地址,在test2函数里面在将他返回出来,可以通过变量获取到
print(t)            #t变量就是bar的内存地址

#结果
# <function bar at 0x01D83810>
# <function bar at 0x01D83810>

再看

import time


def bar():
    time.sleep(3)
    print("in the bar")


def test2(func):
    print(func)
    return func


t = test2(bar)  # 意思是传的内存地址,在test2函数里面在将他返回出来,可以通过变量获取到
t()    #就代表运行bar这个函数

#结果:
# <function bar at 0x033E3810>
# in the bar

再看

import time


def bar():
    time.sleep(3)
    print("in the bar")


def test2(func):
    print(func)
    return func


bar = test2(bar)  # 这里相当于用test2加上了新的功能,(print理解为加了很多功能)
bar()    #函数的调用方式没有改变。


#结果:
# <function bar at 0x033E3810>
# in the bar
  • 3.嵌套函数

定义:在函数的函数体内,用def申明一个新的函数,叫嵌套函数。(而不是去调用别的函数)
嵌套函数的作用域:

import time


def foo():
    print("in the foo")

    def bar():
        print("in the bar")

    bar()  # 这个函数具有局部变量的特性,再能在内部调用
foo()

#结果:
# in the foo
# in the bar

局部作用域和全局作用域的访问顺序

x = 0
def grandpa():
    x = 1
    def dad():
        x = 2
        def son():
            x = 3
            print(x)
        son()
    dad()
grandpa()
#结果:3
#从里往外一层一层的找 

高阶函数+嵌套函数 才是 装饰器

import time


def deco(func):
    start_time = time.time()
    func()
    stop_time = time.time()
    print("the func run time is %s" % (stop_time - start_time))


def test1():
    time.sleep(3)
    print("in the test1")


def test2():
    time.sleep(3)
    print("in the test2")


# 思考怎么给test1,test2增加新功能,先写一个高阶函数deco
deco(test1)
deco(test2)  # 但是这是在改变函数的调用方式
# 结果:
# in the test1
# the func run time is 3.0005288124084473
# in the test2
# the func run time is 3.000913143157959

# 可以这样:
# 前面一直在使用高阶函数,也可以考虑嵌套函数

再看:

import time


def timer(func):  # timer(test1) ,func = test 就是把test1的内存地址传给func
    def deco():  # 函数的嵌套。这里就是在内存地址上申明了一个变量
        start_time = time.time()
        func()  # 运行了test1
        stop_time = time.time()
        print("the func run time is %s" % (stop_time - start_time))

    return deco  # 高阶函数。这里就是返回了函数的内存地址


def test1():
    time.sleep(3)
    print("in the test1")


def test2():
    time.sleep(3)
    print("in the test2")


test1 = timer(test1)  # print(timer(test1) )的结果就是deco函数的内存地址
test1()  # 执行的是deco,

# 结果:(没有改变test1的源代码,没有改变test1的调用方式。用到函数的嵌套和高阶函数,但是这样做有点麻烦,每次都要运行装饰器,还要赋值给一个变量名一样的变量)
# in the test1
# the func run time is 3.000809669494629

再看:

如果要直接test1()调用,如何实现。

 import time


def timer(func):  # timer(test1) ,func = test 就是把test1的内存地址传给func
    def deco():  # 函数的嵌套。这里就是在内存地址上申明了一个变量
        start_time = time.time()
        func()  # 运行了test1
        stop_time = time.time()
        print("the func run time is %s" % (stop_time - start_time))

    return deco  # 高阶函数。这里就是返回了函数的内存地址


@timer  # 就等于test1 = timer(test1)
def test1():
    time.sleep(3)
    print("in the test1")


@timer  # 就等于test2 = timer(test1)
def test2():
    time.sleep(3)
    print("in the test2")


test1()
test2()


#结果:#可以全部选中打断点,查看运行逻辑
# in the test1
# the func run time is 3.0002071857452393
# in the test2
# the func run time is 3.000702381134033

再看:
如果被装饰的函数有参数:

import time


def timer(func):
    def deco(*args, **kwargs):
        start_time = time.time()
        func(*args, **kwargs)
        stop_time = time.time()
        print("the func run time is %s" % (stop_time - start_time))

    return deco  # 高阶函数。这里就是返回了函数的内存地址


@timer  # 就等于test1 = timer(test1)
def test1():  # 没有参数
    time.sleep(3)
    print("in the test1")


@timer  # 就等于test2 = timer(test2)
def test2(age):  # 有参数
    time.sleep(3)
    print("test2的参数", age)


test1()

test2(23)
#结果
# in the test1
# the func run time is 3.0001745223999023
# test2的参数 23
# the func run time is 3.000623941421509

decorator就是装饰器的意思,又叫语法糖

有个小需求,某公司的网站有很多页,如果将每个页面定义为一个函数,有些页面需要有权限才能查看

user, passwd = "xf", "4941"




# 写装饰器
def anth(func):
    def warpper(*args, **kwargs):
        username = input("Username:").strip()
        password = input("Password:").strip()
        if user == username and passwd == password:
            print("\033[32;1mUser has passed authentication\033[0m")  # 加色
            func(*args, **kwargs)
        else:
            exit("\033[31;1mInvalid username or password\033[0m")

    return warpper


def index():
    print("welcome to index page")


@anth
def home():
    print("welcome to home page")


@anth
def bbs():
    print("welcome to bbs page")


index()
home()
bbs()
#结果
# welcome to index page
# Username:xf
# Password:4941
# User has passed authentication
# welcome to home page
# Username:xf
# Password:4941
# User has passed authentication
# welcome to bbs page

再看
home()函数有返回值

user, passwd = "xf", "4941"


# 写装饰器
def anth(func):
    def warpper(*args, **kwargs):
        username = input("Username:").strip()
        password = input("Password:").strip()
        if user == username and passwd == password:
            print("\033[32;1mUser has passed authentication\033[0m")  # 加色
            func(*args, **kwargs)
        else:
            exit("\033[31;1mInvalid username or password\033[0m")

    return warpper


def index():
    print("welcome to index page")


@anth
def home():
    print("welcome to home page")
    return "from home"


@anth
def bbs():
    print("welcome to bbs page")


index()
print(home())
bbs()
 #结果
# welcome to index page
# Username:xf
# Password:4941
# User has passed authentication
# welcome to home page
# None #home()函数的返回值变成空了,因为在装饰器func()里面的返回结果没有赋值给变量

再看:

user, passwd = "xf", "4941"


# 写装饰器
def anth(func):
    def warpper(*args, **kwargs):
        username = input("Username:").strip()
        password = input("Password:").strip()
        if user == username and passwd == password:
            print("\033[32;1mUser has passed authentication\033[0m")  # 加色
            res = func(*args, **kwargs)
            print("---after authenticaion")  # 随便砸装饰一点东西
            return res
        else:
            exit("\033[31;1mInvalid username or password\033[0m")

    return warpper


def index():
    print("welcome to index page")


@anth
def home():
    print("welcome to home page")
    return "from home"


@anth
def bbs():
    print("welcome to bbs page")


index()
print(home())
bbs()
#结果:
welcome to index page
Username:xf
Password:4941
User has passed authentication
welcome to home page
---after authenticaion
from home           #现在就有返回值了
Username:
Logo

瓜分20万奖金 获得内推名额 丰厚实物奖励 易参与易上手

更多推荐