python装饰器本质上是一个什么_「面试题解」什么是python装饰器?

装饰器在python面试里是你们必须迈过去的一道坎儿,在面试中非常常见。而且,在实际项目中,python装饰器也是一个非常有意和有用的功能,可以在不改变函数代码和调用方式的情况下给函数添加新的功能,广泛应用于权限校验、性能统计、日志打印等应用场景。本文会深入浅出的帮助大家理解装饰器,击破面试难点。

01

装饰器的由来

试想这样一种场景,当需要对某一些函数做耗时统计时怎么办?是在每一个函数的开头和结束各打印一个时间戳么?要是有几百函数怎么办?要是要修改计算的精度怎么办?ctrl+c&ctrl+v到山无棱么?有没有什么办法可以不侵入函数体,同时又代价最低呢,这个时候就需要使用到我们本文所讲到的装饰器。

02

基本概念

在理解装饰器之前,我们首先需要了解几个概念。

函数

在python里,一切皆对象,函数也是一个对象。函数名其实就是指向一段内存空间的地址。因此函数可以作为参数,也可以作为返回值。

嵌套函数

在函数体内又重新定义一个新的函数,称为嵌套函数。外部的我们叫它外函数,内部的我们叫他内函数。如下图就是一个嵌套函数,在outer的函数体内又定义了内函数inner。需要注意点一点是,当内函数在自己作用域内无法找到局部变量时,会向上一层作用域查找。具体的作用域相关的知识点在这里不详细介绍。

def outer():

a = 1

def inner():

b = a + 1

print(b)

inner()outer()

#输出结果 2

闭包

闭包是Python编程一个非常重要的概念。如果一个外函数中定义了一个内函数,且内函数体引用了外函数变量,同时外函数通过return返回内函数的引用时,会把定义时涉及到的外部引用变量和内函数打包成一个整体(闭包)返回。如下例。

def outer(out_param):

a = out_param

def inner(inner_param):

b = inner_param

print(a+b)

return inner

func = outer(1) # 返回inner函数对象+局部变量1(闭包)

func(10) # 相当于inner(10)。输出11

03

装饰器

有了上面的基础,就比较好理解装饰器。装饰器本质上是一个嵌套函数,它接受被装饰的函数(func)作为参数,并返回一个包装过的函数。通过这种方式扩展功能,主要有2个特点:a、不修改被装饰函数的调用方式;b、不修改被装饰函数的源代码。

无参装饰器

如下例通过@符号,实现嵌套函数的调用,记录函数执行时间。

import time

def outer(func): # 将test的地址传递给func

def inner():

start_time = time.time()

func() # func入参数是test函数对象地址

end_time = time.time()

print("运行时间为%s"%(end_time - start_time))

return inner # 返回inner的地址

@outer # 这里实际执行的是test = outer(test)

def test():

time.sleep(10)

print("test for decorator")

test()

# 输出

# test for decorator

# 运行时间为4.00339603424

有参装饰器

通过*args和**kwargs,实现参数传递

import timedef outer(func): # 将test的地址传递给funcdef inner(*args, **kwargs): start_time = time.time() func(*args, **kwargs) # func入参数是test函数对象地址 end_time = time.time() print("运行时间为%s"%(end_time - start_time)) return inner # 返回inner的地址@outerdef test(sleep_time): time.sleep(sleep_time) print("test for decorator")test(2)

# 输出

# test for decorator

# 运行时间为2.00034189224

类装饰器

装饰器不仅可以是函数,还可以是类,相比函数装饰器,类装饰器具有灵活度大、高内聚、封装性等优点。使用类装饰器主要依靠类的__call__方法(Python中,只要在创建类型的时候定义了__call__()方法,这个类型就是可调用的),当使用 @ 形式将装饰器附加到函数上时,就会调用此方法。

class Foo(object):def __init__(self, func): self._func = func def __call__(self): print('class decorator running') self._func() print('class decorator ending')@Foo # 实际调用方式为:bar = Foo(bar)

def bar():print('bar')bar()

# 输出# class decorator runing# bar# class decorator ending

04

思考题

如果有多个装饰器的情况下执行顺序是怎样的呢?

求职礼包

《2020届互联网名企最新校招信息汇总》《399套精选求职简历模板,只为免费送你》求职面经

《校招凉面-今日头条&京东算法岗面经》《校招热面-渣渣学长给学弟学妹的求职建议》《职场-互联网955上班公司名单-不要996》《爆尿-互联网公司福利大比拼》

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

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

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值