python——装饰器

一、名称空间(name space)

  存放名字的地方,存什么名字呢?

如变量x=1,1存放于内存中,那名字x存放在哪里呢?名称空间正是存放名字x与1绑定关系的地方

名称空间共3种:

  • locals: 是函数内的名称空间,包括局部变量和形参
  • globals: 全局变量,函数定义所在模块的名字空间
  • builtins: 内置模块的名字空间

不同变量的作用域不同就是由这个变量所在的命名空间决定的。

作用域即范围

  • 全局范围:全局存活,全局有效
  • 局部范围:临时存活,局部有效

查看作用域方法 globals(),locals()

作用域查找顺序:

 locals -> enclosing function(相邻的) -> globals -> __builtins__LEGB

二、闭包

有一下代码:

def outer():
    name = 'alex'

    def inner():
        print("在inner里打印外层函数的变量",name)

    return inner


f = outer()
f()

输出:
在inner里打印外层函数的变量 alex
View Code

外层函数outer返回了内层函数的函数名inner给变量f,在函数外部执行语句:f()是就相当于调用了内层函数inner,并且inner又引用了外层函数的变量name。

对于这种,外层函数返回子函数的函数名(即子函数内存地址),在函数外调用了子函数,子函数又引用了外层函数的变量,就形成了闭包。

闭包的意义:返回的函数对象,不仅仅是一个函数对象,在该函数外还包裹了一层作用域,这使得,该函数无论在何处调用,优先使用自己外层包裹的作用域。

三、装饰器

  可以理解为一种装饰函数的工具,其本质就是一个函数。

  软件开发的原则之一:开放—封闭原则

开放:对现有功能的扩展开放

封闭:已实现的功能代码块不应该被修改

  所以,装饰器的原则是:

1、不能修改被装饰函数的源码

2、不能修改被装饰函数的调用方式

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

高阶函数:1、将函数名作为实参传递给另一个函数(不改变源码)

     2、return返回另一个函数名(不改变函数的调用方式)

装饰器实例

你是一家视频网站的后端开发工程师,你们网站已上线运行了该模块

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

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


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


def henan():
    print("----河南专区----")


home()
america()
japan()
henan()
View Code

现在,你们老板跟你说,现在用户越来越多啦,准备将"america"和"henan"两个板块改为收费才能进入,所以你就要给它们新增一个登陆认证的功能,但是系统已经上线了,你是不可能修改源码的,用户一直也都习惯了这种调用方式去进去各板块,所以你想到了装饰器:

status = False  # 用于判断用户登录状态


def auth(func):  # 将函数作为参数传入  func=america or func = henan
    def inner():
        global status  # 定义全局变量
        _username = "qwe"  # 假设这是数据库用户信息
        _password = "123"
        if status is False:  # 未登录
            username = input("username:")
            password = input("password:")
            if username == _username and password == _password:  # 用户名与密码是否正确
                print("\033[35;1m-----Welcome-----\033[0m")
                status = True  # 修改用户登录状态
            else:
                print("Wrong username or password")
        if status is True:  # 已登录
            print("\033[35;1m-----用户已登录-----\033[0m")
            func()  # 允许进入相关板块  america() or henan()
    return inner  # 返回内层函数名



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


@auth  # 给函数加上装饰器,相当于执行:auth(america) ,稍后auth 会将函数inner返回,即america=inner
def america():
    print("----欧美专区----")


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


@auth  # 给函数加上装饰器
def henan():
    print("----河南专区----")


home()
america()  # 此时的america已经被“偷梁换柱”,相当于inner()
japan()
henan()  # inner()

输出:
---首页----
username:qwe
password:123
-----Welcome-----
-----用户已登录-----
----欧美专区----
----日韩专区----
-----用户已登录-----
----河南专区----
View Code

你可以在里面传参数,最好使用非固定参数,因为万一有些函数你要传参,有些不用,方便动态管理:

status = False  # 用于判断用户登录状态


def auth(func):  # 将函数作为参数传入  func=america or func = henan
    def inner(*args, **kwargs):
        global status  # 定义全局变量
        _username = "qwe"  # 假设这是数据库用户信息
        _password = "123"
        if status is False:  # 未登录
            username = input("username:")
            password = input("password:")
            if username == _username and password == _password:  # 用户名与密码是否正确
                print("\033[35;1m-----Welcome-----\033[0m")
                status = True  # 修改用户登录状态
            else:
                print("Wrong username or password")
        if status is True:  # 已登录
            print("\033[35;1m-----用户已登录-----\033[0m")
            func(*args, **kwargs)  # 允许进入相关板块  america() or henan()
    return inner  # 返回内层函数名



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


@auth  # 给函数加上装饰器,相当于执行:auth(america) ,稍后auth 会将函数inner返回,即america=inner
def america(n):  # 传递参数
    print(n)
    print("----欧美专区----")


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


@auth  # 给函数加上装饰器
def henan():
    print("----河南专区----")


home()
america("付费啦,可以看美剧啦!!!")  # 此时的america已经被“偷梁换柱”,相当于inner()
japan()
henan()  # inner()

输出:
---首页----
username:qwe
password:123
-----Welcome-----
-----用户已登录-----
付费啦,可以看美剧啦!!!
----欧美专区----
----日韩专区----
-----用户已登录-----
----河南专区----
View Code

但是,老板又提新需求了,要求"america"板块用“QQ”认证,"henan"板块用“WeChat”认证,其实就是在装饰器外面再加一层函数:

status = False  # 用于判断用户登录状态


def outer(auth_login):
    def auth(func):  # 将函数作为参数传入
        def inner(*args, **kwargs):
            if auth_login == "QQ" or auth_login == "WeChat":
                global status  # 定义全局变量
                _username = "qwe"  # 假设这是数据库用户信息
                _password = "123"
                if status is False:  # 未登录
                    username = input("username:")
                    password = input("password:")
                    if username == _username and password == _password:  # 用户名与密码是否正确
                        print("\033[35;1m-----Welcome-----\033[0m")
                        status = True  # 修改用户登录状态
                    else:
                        print("Wrong username or password")
                if status is True:  # 已登录
                    print("\033[35;1m-----用户已登录-----\033[0m")
                    func(*args, **kwargs)  # 允许进入相关板块  america() or henan()
                else:
                    print("Wrong authentication login:", func)  # 认证方式错误提示
        return inner  # 返回内层函数名
    return auth



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


@outer("QQ")  # 带参数的装饰器
def america(n):  # 传递参数
    print(n)
    print("----欧美专区----")


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


@outer("WeChat")  # 带参数的装饰器
def henan():
    print("----河南专区----")


home()
america("付费啦,可以看美剧啦!!!")
japan()
henan()  # inner()
View Code

猜你喜欢

转载自www.cnblogs.com/yanlin-10/p/9016084.html
今日推荐