Python笔记(六):装饰器

装饰器:本质是函数
功能:装饰其他函数(为其他函数添加附加功能)

装饰器原则:
1、不能修改被装饰函数的源代码
2、不能修改被装饰函数的调用方式

装饰器相对于源代码而言就是个小透明,没有修改源代码,也不影响源代码的运行。
在不修改源代码的情况下,为其添加新的功能;



函数即变量

简单举例

x = 1
y = x

def test():
    print("in the text")
  • 相当于在内存中创建一个空间,里面存储 1,门牌号是x

  • y 同样指向 x 门牌号所代表的空间,y 同样也是个门牌号

  • 函数test() 也同样创建一个空间,里面存储函数体(print(“in the text”)),门牌号为test ,加上括号即可调用

test只是一个门牌号

print(test)

>> <function test at 0x00000000052340D0>

高阶函数

高阶函数满足:
- 把一个函数名(内存地址)作为实参传给另一个函数
- 返回值中包含函数名

例如:

import time
def bar():
    time.sleep(3)              #延时3秒输出
    print("in the bar")

def deco(func):            #用来计时
    start_time = time.time()
    func()
    stop_time = time.time()
    print("the func run time is %s" %(stop_time-start_time))

#bar()
deco(bar)

**注:**deco()具有装饰器的作用,但不是装饰器,因为不符合装饰器原则2
改变了原本 bar() 调用方式

思考:如何不改变原函数的调用方式而实现添加计时功能呢?

#可以将test()添加返回值,返回原地址;
def deco(func):            #用来计时
    start_time = time.time()
    func()
    stop_time = time.time()
    print("the func run time is %s" %(stop_time-start_time))
    return func
bar = deco()

bar()    #此时,便可以不改变原函数的调用方式了

嵌套函数

函数也类似变量,分为全局函数局部函数

定义:在一个函数体内,用 def 去声明另一个函数,不能在外部直接调用

x = 0
def grandpa():
    x = 1
    def father():
        x = 2
        def son():
            x = 3
            print(x)
        son()
    father()
grandpa()

逐级嵌套,逐级调用。

装饰器案例及分析

高阶函数 + 嵌套函数 ==> 装饰器

装饰器函数

接上述例子:

import time
def timer(func):
    def deco():  # 用来计时
        start_time = time.time()
        func()
        stop_time = time.time()
        print("the func run time is %s" % (stop_time - start_time))
    return deco

def bar():  # 延时3秒,打印输出内容
    time.sleep(3)
    print("in the bar")

bar = timer(bar)

bar()
  • 首先程序会先执行 bar = timer(bar) ,进入timer 函数内
  • 然后传 bar 进入函数后直接返回该 bar 地址作为 deco,赋给bar
  • 接着执行bar 函数,会执行timer内部潜逃的deco()函数

进一步优化

进一步优化:取消 bar = timer(bar) 这一过程

#想装饰哪个,就加在哪个的开头
@timer                  #此步直接执行了 bar = timer(bar)
def bar():  # 延时3秒,打印输出内容
    time.sleep(3)
    print("in the bar")

bar()

使装饰器通用

再进一步:使装饰器通用,同时装饰输入参数数量不同的函数;

不同的函数统一加上计时功能,观察不同的函数运行时间:

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():  # 延时3秒,打印输出内容
    time.sleep(1)
    print("in the test1")

@timer
def test2(name, age):               #test2(name) = deco(name)
    print("test2:",name)

test1()
test2("Evan",22)    

#test2()有参数传入,相当于deco()有参数传入,需要定义形参(不定参数)
不知道有没有参数传入,传入多少个参数

装饰器的使用

加入验证功能

目标:共有100个函数,要给其中20个加入验证功能

import time

#装饰器
def auth(func):           
    def wrapper(*args,**kwargs):
        username = input("Username:".strip())
        password = input("password:".strip())
        if user == username and passwd == password:
            print("\033[32:1mUser login succeeded\033[0m")
            func(*args,**kwargs)              #(1)
        else:
        exit("\033[31:1mInvalid username or password\033[0m")
    return wrapper


def index():
    print("Welcome to index page")
@auth
def home():
    print("Welcome to homepage")
@auth
def bbs():
    print("Welcome to bbs page")

目标功能实现。

解决返回值问题

再进一步考虑:如果被修饰的函数是有返回值的,例如:

def index():
    print("Welcome to index page")
@auth
def home():
    print("Welcome to homepage")
    return "from home"
@auth
def bbs():
    print("Welcome to bbs page")

home()        

#此时执行home() = 执行wrapper(),但返回值丢失了
#因为在执行到上述(1)步时,得到的返回值并没有使用,没有输出出来

所以改进,在(1)处再加上返回值

def auth(func):           
    def wrapper(*args,**kwargs):
        username = input("Username:".strip())
        password = input("password:".strip())
        if user == username and passwd == password:
            print("\033[32:1mUser login succeeded\033[0m")
            res = func(*args,**kwargs)              #(1)
            print("----after login----")
            return res          
        else:
        exit("\033[31:1mInvalid username or password\033[0m")
    return wrapper

在 (1)处再加入返回值,即可实现返回值不丢失。

改进认证方式

再进一步,如果要将两个登陆用户名密码分别采用不同的验证方式:
- home()采用本地文件认证
- bbs()采用远程ldap(统一认证管理)认证

解决方式
- @auth定义时传入参数:例如@auth(auth_type = “local”);
- 装饰器函数再嵌套一层

import time

user, passwd = "Evan", "0000"

def auth(auth_type):
    print("auth func:",auth_type)
    def out_wrapper(func):
        def wrapper(*args, **kwargs):
            print("wrapper func args:", *args, **kwargs)
            if auth_type == "local":
                username = input("Username:".strip())
                password = input("password:".strip())
                if user == username and passwd == password:
                    print("\033[32;1mUser login succeeded\033[0m")
                    res = func(*args, **kwargs)  # (1)
                    print("----after login----")
                    return res
                else:
                    exit("\033[31;1m Invalid username or password \033[0m")
            elif auth_type == "ldap":
                print("LDAP验证")
        return wrapper
    return out_wrapper

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

@auth(auth_type="local")            #home = wrapper
def home():
    print("Welcome to homepage")
    return "from home"

@auth(auth_type="ldap")
def bbs():
    print("Welcome to bbs page")

index()
home()
bbs()

以 home() 为例
- 最开始传入 auth(auth_type) 的参数为local。。。auth_type:”local”


- 然后定义out_wrapper()函数,但并未进入内部,return out_wrapper


- 即 home = wrapper


- 再在wrapper内进行判断,是 local 还是 ldap 验证.

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

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

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值