python学习系列之装饰器的实现

1. 前言

装饰器是python中的一种特殊语法,装饰器是在闭包的基础上实现的。

像python中的functools包是python中高级函数的用法,其中有wrapper,update_wrapper,particial等其实都是装饰器,只不过各有各的作用。

装饰器本质上是一个 Python 函数或类,它可以让其他函数或类在不需要做任何代码修改的前提下增加额外功能,装饰器的返回值也是一个函数/类对象。它经常用于有切面需求的场景,比如:插入日志、性能测试、事务处理、缓存、权限校验等场景,装饰器是解决这类问题的绝佳设计。有了装饰器,我们就可以抽离出大量与函数功能本身无关的雷同代码到装饰器中并继续重用。概括的讲,装饰器的作用就是为已经存在的对象添加额外的功能。

上文来自 Python 装饰器

简单讲一下装饰器


@decorator
def test():
	pass

#实际等价于
test = decorator(test)

带参数的装饰器

# 带参数的装饰器
@decorator(a, b)
def test():
	pass

#实际等价于
test = decorator(a, b)(test)

带参数的装饰器就是,就是用执行了该装饰器函数之后所返回的函数再来装饰原函数。

2. 装饰器的实现

装饰器可以通过函数实现或类实现。

2.1 函数实现装饰器

from functools import wraps


def decorator(func):
	print("装饰时调用此句,原函数name是{}".format(func.__name__))
	@wraps(func)
	def inner(*args, **kwargs):
		print("这是装饰器添加的日志")
		return func(*args, **kwargs)
	
	return inner


@decorator
def test(name, age=None, gender=None):
	print('name is {}, age is , gender is {}'.format(name, age, gender))
	print('test函数的name是{}'.format(test.__name__))

if __name__ == '__main__':
	test('tom', 18, 'boy')
	pass

输出结果如下:

# 输出,使用wraps之前
装饰时调用此句,原函数name是test
这是装饰器添加的日志
name is tom, age is 18, gender is boy
test函数的name是inner

# 输出,使用wraps之后
装饰时调用此句,原函数name是test
这是装饰器添加的日志
name is tom, age is 18, gender is boy
test函数的name是test

使用functools中的wraps是因为,直接通过函数定义装饰器之后,会改变原函数的一些属性,例如,__name__属性等。

有一点需要注意的是,哪些是在调用了被装饰函数时才执行的,哪些是在装饰的时候就已经执行的。

当注释了test(‘tom’, 18, ‘boy’)之后,发现仍然会打印 “装饰时调用此句,原函数name是test”

2.2 类实现装饰器

 class Decorator(object):
      def __init__(self, func):
          print("这是在使用装饰器的时候,{}".format(func.__name__))
          self.func = func

      def __call__(self, *args, **kwargs):
          print("这是装饰器添加的日志")
          return self.func(*args, **kwargs)
 
   
  @Decorator
  def test(name, age=None, gender=None):
      print("name is {}, age is {}, gender is {}".format(name, age, gender))
      #print('test函数的name是{}'.format(test.__name__))
 
 
  if __name__ == '__main__':
      #pass
      test('tom', 18, 'boy')                            

输出结果如下:

# 输出
装饰时调用此句,原函数name是test
这是装饰器添加的日志
name is tom, age is 18, gender is boy


在装饰 test函数时,实际上时创建了一个Decorator的实例对象,当调用test(‘tom’, 18, ‘boy’)时,其实就是调用了这个实例对象,调用实例对象实际就是调用的类中的__call__方法。

需要注意的是,如果要用类来实现带参数的装饰器,那么__call__方法必须成为一个装饰器(在__call__中还需要定义一个函数)。例如,

class Decorator(object):
      def __init__(self, a, b):
      	  print("这是在使用装饰器的时候,{}".format(func.__name__))
          self.a = a
          self.b = b
		  
      def __call__(self, func):
          print("这也是在使用装饰器的时候就执行的")
          def inner(*args, **args):
          	print("这是装饰器添加的日志")
          	return self.func(*args, **kwargs)
          	
          return inner


@Decorator(a, b)
def test(*args, **kwargs):
	pass

3. 装饰器的使用

3.1 装饰函数

3.1.1 函数装饰函数

2.1中就是函数装饰函数的例子。

3.1.2 类装饰函数

2.2中就是类装饰函数的例子。

3.2 装饰类

3.2.1 函数装饰类

from functools import wraps


def decorator(cls):
	print('装饰类时调用')
	@wraps
	def inner(*args, **kwargs):
		print('这是添加日志的地方{}'.format(cls.__name__))
		return cls(*args, **kwargs) # 此时是创建类的实例
	
	return inner

@decorator
class Test(object):
	def __init__(self, name, age, gender):
		self.name = name
		self.age = age
		self.gender = gender
	
	def run(self):
		print('这是run方法')

3.2.2 类装饰类

class Decorator(object):
	def __init__(self, cls):
		print("这是在使用装饰器的时候,{}".format(cls.__name__))
		self.cls = cls
	
	def __call__(self, *args, **kwargs):
		print("这是装饰器添加的日志")
		return self.cls(*args, **kwargs)

@Decorator
class Test(object):
	def __init__(self, name, age, gender):
		self.name = name
		self.age = age
		self.gender = gender
	
	def run(self):
		print('这是run方法')

4. 总结

函数之所以可以装饰,是将函数的引用传递给了装饰器函数。装饰器函数执行某些语句之后才执行真正的传递进来的原函数。

通过画图可以看出再装饰器装饰过程种的引用变化,可以帮助理解装饰器。

5. 参考文献

[1] Python 装饰器
[2] python装饰器的4种类型:函数装饰函数、函数装饰类、类装饰函数、类装饰类
[3] Python各种类型装饰器详解说明

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

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

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值