Python函数之装饰器

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

编写装饰器的原则:

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

接下来模拟一个应用场景从头编写一个装饰器:
有一个视频网站有四个模块:

def home():
    print("---首页----")

def domestic():
    print("----国内专区----")

def america():
    print("----欧美专区----")

def japan():
    print("----日韩专区----")
 

视频网站运营一段一时间后积攒了些客户,现在要为欧美和日韩模块进行收费,所以,调用该模块时,需要判断是否是付费vip会员。
通过之前的函数基础知识的学习,轻易能想到的实现思路是:
单独定义一个登陆函数,在调用欧美和日韩模块时调用:

user_status = False #用户登录了就把这个改成True
 
def login():
    _username = "LZ" #假装这是DB里存的用户信息
    _password = "abc123" #假装这是DB里存的用户信息
    global user_status
 
    if user_status == False:
        username = input("user:")
        password = input("pasword:")
 
        if username == _username and password == _password:
            print("welcome login....")
            user_status = True
        else:
            print("wrong username or password!")
    else:
        print("用户已登录,验证通过...")

def home():
    print("---首页----")

def domestic():
    print("----国内专区----")

def america():
   login() #执行前加上验证
    print("----欧美专区----")

def japan():
   login() #执行前加上验证
    print("----日韩专区----")

 #调用模块
home()
domestic()
america()
japan()

这样就能完成需求。但是!!这违反了软件开发中的一个原则“开放-封闭”原则,简单来说,它规定已经实现的功能代码不允许被修改,但可以被扩展:

开放——封闭

  • 封闭:已实现的功能代码块
  • 开放:对扩展开放

抱着不改变功能模块源码的想法我们会想到用高阶函数,把模块函数当参数传入login()函数中:

user_status = False #用户登录了就把这个改成True
 
def login(func): #把要执行的模块从这里传进来
    _username = "LZ" #假装这是DB里存的用户信息
    _password = "abc123" #假装这是DB里存的用户信息
    global user_status
 
    if user_status == False:
        username = input("user:")
        password = input("pasword:")
 
        if username == _username and password == _password:
            print("welcome login....")
            user_status = True
        else:
            print("wrong username or password!")
 
    if user_status == True:
        func() # 看这里看这里,只要验证通过了,就调用相应功能
#原功能模块不改
#调用模块
home()
domestic()
login(america)
login(japan)

这样,不改变功能模块源码也完成了需求,但是改变了调用方式!试想:每个模块不同的人负责,这样的话就得要求负责相应模块的人都得去改调用方式。这。。。很危险啊。

现在在分析一下:之前函数基础时讲过函数也是变量,不改变函数调用方式的话,我们可以将login(america)赋值给america。但是有一个问题是当执行america = login(america)代码时,就已经把america执行了啊!我们接下来要做的就是,当america = login(america)代码时,返回一个函数,但是不执行。等再调用america ()才执行:

def login(func): #把要执行的模块从这里传进来
 
    def inner():#再定义一层函数
        _username = "alex" #假装这是DB里存的用户信息
        _password = "abc!23" #假装这是DB里存的用户信息
        global user_status
 
        if user_status == False:
            username = input("user:")
            password = input("pasword:")
 
            if username == _username and password == _password:
                print("welcome login....")
                user_status = True
            else:
                print("wrong username or password!")
 
        if user_status == True:
            func() # 看这里看这里,只要验证通过了,就调用相应功能
 
    return inner #用户调用login时,只会返回inner的内存地址,下次再调用时加上()才会执行inner函数

这样当调用时先america = login(america)你在这里相当于把america这个函数替换了,然后在调用america ()。Python中提供语法在要被装饰的函数前写@login就相当于america = login(america)。所以调用时:

 #调用模块
home()
domestic()
@login
america()
@login
japan()

执行完america = login(america)之后,america就相当于调用的时inner.所以,如果原来的america如果有参数的话,在inner函数上添加参数即可:

def login(func): #把要执行的模块从这里传进来
 
    def inner(*args):#再定义一层函数
        _username = "alex" #假装这是DB里存的用户信息
        _password = "abc!23" #假装这是DB里存的用户信息
        global user_status
 
        if user_status == False:
            username = input("user:")
            password = input("pasword:")
 
            if username == _username and password == _password:
                print("welcome login....")
                user_status = True
            else:
                print("wrong username or password!")
 
        if user_status == True:
            func() # 看这里看这里,只要验证通过了,就调用相应功能
 
    return inner #用户调用login时,只会返回inner的内存地址,下次再调用时加上()才会执行inner函数

#调用

@login
america('jack')

写到这,login就是一个装饰器了。它是一个函数,只是给原来的函数增加了验证功能。既没有改变原函数代码,也没有改变调用方式!

最后编辑于
©著作权归作者所有,转载或内容合作请联系作者
平台声明:文章内容(如有图片或视频亦包括在内)由作者上传并发布,文章内容仅代表作者本人观点,简书系信息发布平台,仅提供信息存储服务。

推荐阅读更多精彩内容

  • Android 自定义View的各种姿势1 Activity的显示之ViewRootImpl详解 Activity...
    passiontim阅读 173,925评论 25 709
  • 有时候我们想为多个函数,同意添加某一种功能,比如及时统计,记录日志,缓存运算结果等等,而又不想改变函数代码那就定义...
    ketchup阅读 3,085评论 0 3
  • 如果我想念一个朋友,我想念的到底是什么。 是她或他的一个习惯,是她或他的一个品质。 最近巧合地延续了一个多年前朋友...
    细雨蔷薇阅读 197评论 0 0
  • 前言碎语 前阵子上映的电影《喜欢你》,是一部以美食和爱情为主题的电影。在看影评时,有一段描述写的特别好: “笑到累...
    一池YiCHi阅读 402评论 0 1