装饰器

装饰器

装饰模式有很多经典的使用场景,例如插入日志、性能测试、事务处理等等,有了装饰器,就可以提取大量函数中与本身功能无关的类似代码,从而达到代码重用的目的。

装饰器引入

下面就简单举个例子:

一天,A程序员接到一个登入的需求,写了一个方法。

def login():
    print('登入')


login()     

# 输出  
登入

突然,产品经理想加入一个登入事件。于是A程序员对方法进行了修改。

def login():
    print('事件记录')
    print('登入')


login()    

# 输出  
事件记录    
登入

然后,A的老大看到了说,你这样违反了开闭原则,好好思考一下如何改进。后面A就改进,通过闭包的方式,定义了一个event函数。

# 定义一个event函数,记录事件
def event(func):
    def inner():
        print('事件记录')
        func()
    return inner

def login():
    print('登入')


login = event(login)    # 这个代码意思是,把定义的login函数作为变量传递给event作为参数。event函数的返回值在赋值login变量
login()     # 调用login变量的方法

# 输出
事件记录
登入

这里有个比较难理解的地方就是,def定义了一个login函数,实际上它是一个login变量指向def定义的login函数的地址值。

eg:

def login():
    print('登入')

print(login)    # <function login at 0x000001D8D0543E18>
a = login       # 将login函数的内存地址指向给a变量
a()             # 调用a函数,实际上调用的是a指向的地址值的方法

# 输出
登入

这个例子就说明login是一个变量,这个变量指向login函数地址值。

理解了这个例子,那么login = event(login)就比较好解释了。

login = event(login)


等号左边的login是event()的返回值,是经过inner变量赋值,inner变量指向的是inner函数的地址值。

等号右边的login变量是指向login函数的地址值。作为参数传递给event函数。实际上是在inner函数中调用。


login()     

调用login函数,因为login变量经过inner变量赋值,所以这时的login变量指向的是inner函数的地址值。

也就是说,login()调用,实际上调用的是inner函数,inner函数执行的顺序是:

    print('事件记录')
    func()      

func参数是由event函数传入,也就是login = event(login)式子等号右边的login,这个login变量指向的是def定义login()。

所以输出的结果是:

事件记录
登入

装饰器语法糖

在Python中,可以使用@语法糖来精简装饰器的代码:

使用了@语法糖后,我们就不需要额外代码来给login重新赋值了。

eg:

def event(func):
    def inner():
        print('事件记录')
        func()
    return inner

@event          # 实际上做了 login = event(login)操作
def login():
    print('登入')

login()

# 输出
事件记录
登入

这里login()@event,其实本质就是login = event(login)@做了一步赋值操作。当认清了这一点后,后面看带参数的装饰器就简单了。

被装饰的函数带参数

我们只带login()作为登入函数,实际上是有用户名和密码的,这个时候login()需要2个参数,那么怎么处理呢。

eg:

def event(func):
    def inner(username, password):
        print('事件记录')
        func(username, password)
    return inner

@event
def login(username, password):
    print('username is %s password is %s,登入成功' %(username,password))


login('amy','123456')

# 输出 
事件记录
username is amy password is 123456

如果这时候需要加上一个注册的方法,注册需要3个参数,怎么使用呢?这个时候需要使用可变参数*args, **kwargs

eg:

def event(func):
    def inner(*args, **kwargs):
        print('事件记录')
        func(*args, **kwargs)

    return inner


@event
def login(username, password):
    print('username is %s , password is %s,登入成功' % (username, password))


@event
def register(username, password, email):
    print('username is %s , password is %s , email is %s,注册成功' % (username, password, email))


login('amy','123456')

# 输出
事件记录
username is amy , password is 123456 , 登入成功


register('anne', '654321' , 'abc@126.com')

# 输出
事件记录
username is amy , password is 123456 , email is abc@126.com , 注册成功
被修饰的函数带返回值

现在登入和注册成功后,需要有返回值,那么怎么处理,请看下面例子:

def event(func):

    def inner(*args, **kwargs):
        print('事件记录')
        ret = func(*args, **kwargs)
        return ret

    return inner


@event
def login(username, password):
    print('username is %s , password is %s' % (username, password))
    return '登入成功'


@event
def register(username, password, email):
    print('username is %s , password is %s , email is %s' % (username, password, email))
    return '注册成功'


ret = login('amy','123456')
print(ret)      # 输出 登入成功


ret = register('anne', '654321' , 'abc@126.com')
print(ret)      # 输出 注册成功

如果函数没有返回值,怎么处理,函数没有返回值,其实返回的是None。上面的装饰器是比较通用的装饰器。

带参数的装饰器

eg:

def event(debug=True):
    if (debug):
        def deco(func):
            def inner(*args, **kwargs):
                print('事件记录')
                return func(*args, **kwargs)

            return inner
    else:
        def deco(func):
            return func
    return deco


@event()    # 使用默认参数
def login(username, password):
    print('username is %s , password is %s' % (username, password))
    return '登入成功'


@event(debug=False)     # debug参数设置成False,也可以直接写False
def register(username, password, email):
    print('username is %s , password is %s , email is %s' % (username, password, email))
    return '注册成功'


login('amy', '123456')
# 输出
事件记录
username is amy , password is 123456


register('anne', '654321', 'abc@126.com')
# 输出
username is anne , password is 654321 , email is abc@126.com

@event加上参数,目的是可以灵活的控制函数的输出,如是否打开调试信息等。

如果@event加上参数,在login()函数定义之前就开始执行。我们可以看一下装饰器的执行顺序。

eg:

def event(debug=True):
    print('event函数调用')
    if (debug):
        def deco(func):
            print('deco函数调用')
            def inner(*args, **kwargs):
                print('inner函数调用')
                # print('事件记录')
                return func(*args, **kwargs)

            return inner
    else:
        def deco(func):
            print('deco函数调用')
            return func
    return deco


@event()
def login(username, password):
    print('login函数调用')
    # print('username is %s , password is %s' % (username, password))
    return '登入成功'



# 在未调用函数时,运行,打印输出:
event函数调用
deco函数调用



login()

# 调用函数后,运行,输出:

event函数调用
deco函数调用
inner函数调用
login函数调用

也就是说,定义的event方法和deco方法在未调用时Python解释器就已经开始执行了。

执行步骤:

1.定义event函数
2.调用event函数,执行打印event函数调用语句,返回deco函数的引用
3.使用@event,执行打印deco函数调用语句,返回inner函数的引用
4.使用func进行装饰
5.调用login函数
6.执行inner函数调用语句
7.执行login函数调用语句
  • 1
    点赞
  • 1
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

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

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值