新闻动态

Python装饰器代码详解

发布日期:2021-12-18 13:13 | 文章来源:源码之家

一、理解装饰器

所有东西都是对象(函数可以当做对象传递)
由于函数也是一个对象,而且函数对象可以被赋值给变量,所以,通过变量也能调用该函数。

def function_one():
 print("测试函数")
#可以将一个函数赋值给一个变量,比如
foo =function_one #这里没有在使用小括号,因为我们并不是在调用function_one函数,而是在将它放在foo变量里。
foo()
'''
测试函数
Process finished with exit code 0
'''

闭包的概念:

1)函数嵌套

2)内部函数使用外部函数的变量

3)外部函数的返回值为内部函数

示例:

def outer_function(message):
 def inner_function():
  print(message)
 return inner_function
func = outer_function("你好")
func() #你好

二、装饰器原型

装饰器的作用就是 不修改源代码以及原函数调用方式的情况下 给原函数增加新的功能。

#将函数作为参数传给另一个函数
def decorator_function(original_function):
 def wrapper_function():
 	print('wrapper executed this before {}'.format(original_function.__name__))
  original_function()
 return wrapper_function
 '''
 返回wrapper_function而不是wrapper_function();这是因为当你把一对小括号放在后面,这个函数就会执行;
 然而如果你不放括号在它后面,那它可以被到处传递,并且可以赋值给别的变量而不去执行它。
	'''
def display():
 print('display function ran')
decorator_display = decorator_function(display)
decorator_display()

运行结果:

wrapper executed this before display
display function ran
Process finished with exit code 0

1、不带参数的装饰器

def decorator_function(original_function):
 def wrapper_function():
  print('wrapper executed this before {}'.format(original_function.__name__))
  original_function()
 return wrapper_function
@decorator_function
def display():  #等价于display =decorator_function(display)
 print('display function ran')
display()

运行结果:

wrapper executed this before display
display function ran

Process finished with exit code 0

2.带参数的被装饰的函数

def decorator_function(original_function):
 def wrapper_function(*args,**kwargs):
  print('wrapper executed this before {}'.format(original_function.__name__))
  original_function(*args,**kwargs)
 return wrapper_function
@decorator_function
def display():
 print('display function ran')
@decorator_function
def display_info(name,age):
 print('display_info ran with arguments ({},{})'.format(name,age))
display()
print('='*50)
display_info('Michal',20)

运行结果:

wrapper executed this before display
display function ran
==================================================
wrapper executed this before display_info
display_info ran with arguments (Michal,20)

Process finished with exit code 0

运行如下代码会出现一个问题

def decorator_function(original_function):
 def wrapper_function(*args,**kwargs):
  print('wrapper executed this before {}'.format(original_function.__name__))
  original_function(*args,**kwargs)
 return wrapper_function
@decorator_function
def display():
 print('display function ran')
@decorator_function
def display_info(name,age):
 print('display_info ran with arguments ({},{})'.format(name,age))
display_info = decorator_function(display_info)
print(display_info.__name__)

wrapper_function

Process finished with exit code 0

输出的应该是display_info,这里的函数被wrapper_function替代了,重写了我们函数的名字和注释文档(docstring)。Python中可以使用functools.wraps来解决这个问题。

from functools import wraps
def decorator_function(original_function):
 @wraps(original_function)
 def wrapper_function(*args,**kwargs):
  print('wrapper executed this before {}'.format(original_function.__name__))
  original_function(*args,**kwargs)
 return wrapper_function
@decorator_function
def display():
 print('display function ran')
@decorator_function
def display_info(name,age):
 print('display_info ran with arguments ({},{})'.format(name,age))
display_info = decorator_function(display_info)
print(display_info.__name__)

运行结果:

display_info

Process finished with exit code 0

3.带参数的装饰器

在函数中嵌入装饰器

from functools import wraps
def logit(logfile='out.log'):
 def logging_decorator(func):
  @wraps(func)
  def wrapped_function(*args, **kwargs):
log_string = func.__name__ + " was called"
print(log_string)
# 打开logfile,并写入内容
with open(logfile, 'a') as opened_file:
 # 现在将日志打到指定的logfile
 opened_file.write(log_string + '\n')
return func(*args, **kwargs)
  return wrapped_function
 return logging_decorator
@logit()
def myfunc1():
 pass
myfunc1()
# Output: myfunc1 was called
# 现在一个叫做 out.log 的文件出现了,里面的内容就是上面的字符串
@logit(logfile='func2.log')
def myfunc2():
 pass
myfunc2()
# Output: myfunc2 was called
# 现在一个叫做 func2.log 的文件出现了,里面的内容就是上面的字符串

4.使用类作为装饰器

class myDecorator(object):
  def __init__(self, f):
print("inside myDecorator.__init__()")
f() # Prove that function definition has completed
  def __call__(self):
print("inside myDecorator.__call__()")
 ​
 @myDecorator
 def aFunction():
  print("inside aFunction()")
 ​
 print("Finished decorating aFunction()")
 ​
 aFunction()

运行结果:

inside myDecorator.__init__()
inside aFunction()
Finished decorating aFunction()
inside myDecorator.__call__()
Process finished with exit code 0

被装饰后的函数aFunction()实际上已经是类myDecorator的对象。当再调用aFunction()函数时,实际上就是调用类myDecorator的对象,因此会调用到类myDecorator的__call__()方法。

因此使用类作为装饰器装饰函数来对函数添加一些额外的属性或功能时,一般会在类的__init__()方法中记录传入的函数,再在__call__()调用修饰的函数及其它额外处理。

class entryExit(object):
  def __init__(self, f):
self.f = f
  def __call__(self):
print("Entering", self.f.__name__)
self.f()
print("Exited", self.f.__name__)
 ​
 @entryExit
 def func1():
  print("inside func1()")
 ​
 @entryExit
 def func2():
  print("inside func2()")
 ​
 func1()
 func2()

运行结果:

Entering func1
inside func1()
Exited func1
Entering func2
inside func2()
Exited func2

Process finished with exit code 0

5.使用对象作为装饰器

空参:

from functools import wraps
class decorator_class:
 def __init__(self):
  print('执行decorator_class类的__init__()方法')
 def __call__(self, original_function):
  print('执行decorator_class类的__call__()方法')
  @wraps(original_function)
  def wrapped_function(*args, **kwargs):
print('call method executed this before {}'.format(original_function.__name__))
print('执行' + original_function.__name__ + '()')
original_function(*args, **kwargs)
print(original_function.__name__ + '()执行完毕')
  return wrapped_function
@decorator_class()
def display_info(name,age):
 print('display_info ran with arguments ({},{})'.format(name,age))
display_info('Michael',20)

运行结果如下:

执行decorator_class类的__init__()方法
执行decorator_class类的__call__()方法
call method executed this before display_info
执行display_info()
display_info ran with arguments (Michael,20)
display_info()执行完毕

Process finished with exit code 0

带参数:

from functools import wraps
class decorator_class:
 def __init__(self,arg1, arg2):
  print('执行decorator_class类的__init__()方法')
  self.arg1 =arg1
  self.arg2=arg2
 def __call__(self, original_function):
  print('执行decorator_class类的__call__()方法')
  @wraps(original_function)
  def wrapped_function(*args, **kwargs):
  	print('执行wrapped_function()')
print('call method executed this before {}'.format(original_function.__name__))
print('装饰器参数:', self.arg1, self.arg2)
print('执行' + original_function.__name__ + '()')
original_function(*args, **kwargs)
print(original_function.__name__ + '()执行完毕')
  return wrapped_function
@decorator_class('Hello', 'World')
def display_info(name,age):
 print('display_info ran with arguments ({},{})'.format(name,age))
display_info('Michael',20)

运行结果如下:

执行decorator_class类的__init__()方法
执行decorator_class类的__call__()方法
执行wrapped_function()
call method executed this before display_info
装饰器参数: Hello World
执行display_info()
display_info ran with arguments (Michael,20)
display_info()执行完毕

Process finished with exit code 0

示例2:

from functools import wraps
class logit(object):
 def __init__(self, logfile='out.log'):
  self.logfile = logfile
 def __call__(self, func):
  @wraps(func)
  def wrapped_function(*args, **kwargs):
log_string = func.__name__ + " was called"
print(log_string)
# 打开logfile并写入
with open(self.logfile, 'a') as opened_file:
 # 现在将日志打到指定的文件
 opened_file.write(log_string + '\n')
# 现在,发送一个通知
self.notify()
return func(*args, **kwargs)
  return wrapped_function
 def notify(self):
  # logit只打日志,不做别的
  pass
@logit()
def myfunc1():
 pass

6.多层装饰器的嵌套

#装饰器1
def decorator1(func):
 #定义装饰之后的函数
 def wrapper1():
  # 装饰器1
  print('1-----装饰1之前')
  # 调用基本函数
  func()
  # 扩展功能2
  print('1-----装饰1之后')
 return wrapper1
#装饰器2
def decorator2(func):
 #定义装饰之后的函数
 def wrapper2():
  # 装饰器2
  print('2-----装饰2之前')
  # 调用基本函数
  func()
  # 扩展功能2
  print('2-----装饰2之后')
 return wrapper2
#基本函数
@decorator2# 第二步:test = decorator2(eat) = test2
@decorator1# 第一步:test = decorator1(eat)  = test1
def test():
 print('测试')
#调用函数
test()

运行结果:

2-----装饰2之前
1-----装饰1之前
测试
1-----装饰1之后
2-----装饰2之后

Process finished with exit code 0

总结

本篇文章就到这里了,希望能够给你带来帮助,也希望您能够多多关注本站的更多内容!

版权声明:本站文章来源标注为YINGSOO的内容版权均为本站所有,欢迎引用、转载,请保持原文完整并注明来源及原文链接。禁止复制或仿造本网站,禁止在非www.yingsoo.com所属的服务器上建立镜像,否则将依法追究法律责任。本站部分内容来源于网友推荐、互联网收集整理而来,仅供学习参考,不代表本站立场,如有内容涉嫌侵权,请联系alex-e#qq.com处理。

相关文章

实时开通

自选配置、实时开通

免备案

全球线路精选!

全天候客户服务

7x24全年不间断在线

专属顾问服务

1对1客户咨询顾问

在线
客服

在线客服:7*24小时在线

客服
热线

400-630-3752
7*24小时客服服务热线

关注
微信

关注官方微信
顶部