Python基础进阶之装饰器

个人理解:用函数修饰函数 在执行函数之前我们做一些判断或者其他的逻辑操作,有点类似于go的func类型然后通过func 做一些修饰

一 什么是装饰器

装饰器利用了函数也可以作为参数传递和闭包的特性,可以让我们的函数在执行之前或者执行之后方便的添加一些代码。这样就可以做很多事情了,比如@classmethod装饰器可以将一个普通的方法设置为类方法,@staticmethod装饰器可以将一个普通的方法设置为静态方法等。所以明白了装饰器的原理以后,我们就可以自定义装饰器,从而实现我们自己的需求。

二 理解

拿网站开发的例子来说。网站开发中,经常会碰到一些页面是需要登录后才能访问的。那么如果每次都在访问的视图函数中判断,很麻烦,而且代码很难维护,示例如下:

user = {
    "is_login": False
}

def edit_user():
    if user['is_login'] == True:
        print('用户名修改成功')
    else:
        print('跳转到登录页面')

def add_article():
    if user['is_login'] == True:
        print('添加文章成功')
    else:
        print('跳转到登录页面')

edit_user()
add_article()

以上现在是只有两个函数,如果以后网站越来越大,需要做判断的地方越来越大,那么这种判断将显得非常低效并且难以维护,因此这时候我们可以采用装饰器来解决:

def edit_user():
    print('用户名修改成功')

def add_article():
    print('添加文章成功')

def login_required(func):

    def wrapper():
        if user['is_login'] == True:
            func()
        else:
            print('跳转到登录页面')

    return wrapper

login_required(edit_user)
login_required(add_article)

这样我们把这个判断用户是否登录的逻辑就已经单独抽出放到login_required这个装饰器中了,以后如果某个函数想要做登录限制,那么就先传给login_required这个装饰器就可以了。
但是以上方式写法很别扭,每次调用一个函数的时候要记得先传给login_required,容易忘记每次都要写,因此我们采用另外一种写法:

def login_required(func):

    def wrapper():
        if user['is_login'] == True:
            func()
        else:
            print('跳转到登录页面')

    return wrapper

@login_required
def edit_user():
    print('用户名修改成功')

@login_required
def add_article():
    print('添加文章成功')

edit_user()
add_article()

以上这种写法是装饰器中正确的写法,在函数定义开头的地方,通过@装饰器名就可以了,这样在调用edit_user和add_article的时候,就不需要手动的传给login_required了。

三 被装饰的函数带有参数

被装饰的函数有参数是非常普遍的一种情况,这时候我们只需要在里面的函数中传递参数就可以了:

def login_required(func):

    def wrapper(username):
        if user['is_login'] == True:
            func(username)
        else:
            print('跳转到登录页面')

    return wrapper

@login_required
def edit_user(username):
    print('用户名修改成功:%s'%username)

edit_user("zhiliao")

也有可能被装饰的函数中,参数是不固定的,因此这时候写一个固定的参数,不能成为一个普遍的装饰器,这时候可以采用*args和**kwargs组合起来,包含所有的参数:

def login_required(func):

    def wrapper(*args,**kwargs):
        if user['is_login'] == True:
            func(*args,**kwargs)
        else:
            print('跳转到登录页面')

    return wrapper

@login_required
def edit_user(username):
    print('用户名修改成功:%s'%username)

edit_user()

四 带参数的装饰器

装饰器也可以传递参数。只不过如果给装饰器传递参数了,那么就要在这个装饰器中写两个方法了,示例代码如下:

def login_required(site='front'):
    def outter_wrapper(func):
        def inner_wrappre(*args,**kwargs):
            if site == 'front':
                if user['is_login'] == True:
                    print('进入到前台了')
                    func(*args,**kwargs)
                else:
                    print('跳转到前台的首页')
            else:
                if user['is_login'] == True:
                    print('进入到后台了')
                    func(*args,**kwargs)
                else:
                    print('跳转到后台的首页')
        return inner_wrappre
    return outter_wrapper


@login_required('front')
def edit_user():
    print('用户名修改成功')

@login_required('front')
def add_article():
    print('添加文章成功')

edit_user()

五 wraps装饰器

采用之前的装饰器,会让我们的函数失去一些属性,比如__name__,这样在一些代码中会产生错误,比如Flask开发中。如果我们想要用装饰器,并且仍想保留函数的一些属性,比如__name__,那么可以使用wraps装饰器,以下是没有使用wraps装饰器的代码:

def login_required(func):

    def wrapper(*args,**kwargs):
        if user['is_login'] == True:
            func(*args,**kwargs)
        else:
            print('跳转到登录页面')

    return wrapper

@login_required
def edit_user(username):
    print('用户名修改成功:%s'%username)


edit_user()
print(edit_user.__name__)
# 打印wrapper

以下再使用wraps装饰器,来优化代码:

from functools import wraps

def login_required(func):
    @wraps
    def wrapper(*args,**kwargs):
        if user['is_login'] == True:
            func(*args,**kwargs)
        else:
            print('跳转到登录页面')

    return wrapper

@login_required
def edit_user(username):
    print('用户名修改成功:%s'%username)


edit_user()
print(edit_user.__name__)
# 打印edit_user
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包

打赏作者

来自万古的忧伤

你的鼓励将是我创作的最大动力

¥1 ¥2 ¥4 ¥6 ¥10 ¥20
扫码支付:¥1
获取中
扫码支付

您的余额不足,请更换扫码支付或充值

打赏作者

实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

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

余额充值