面向切面编程
面向切面的编程Aspect Oriented Programming:
在运行时,动态地将代码切入到类的指定方法、指定位置上的编程思想;
或者通过预编译方式和运行期动态代理实现程序功能的统一维护的一种技术。
OOP(面向对象编程)针对业务处理过程的实体及其属性和行为进行抽象封装,以获得更加清晰高效的逻辑单元划分。
AOP是针对业务处理过程中的切面进行提取,它所面对的是处理过程中的某个步骤或阶段,以获得逻辑过程中各部分之间低耦合性的隔离效果。这两种设计思想在目标上有着本质的差异。
一般而言,我们管切入到指定类指定方法的代码片段称为切面,而切入到哪些类、哪些方法则叫切入点。有了AOP,我们就可以把几个类共有的代码,抽取到一个切片中,等到需要时再切入对象中去,从而改变其原有的行为。这样看来,AOP其实只是OOP的补充而已。OOP从横向上区分出一个个的类来,而AOP则从纵向上向对象中加入特定的代码。有了AOP,OOP变得立体了。如果加上时间维度,AOP使OOP由原来的二维变为三维了,由平面变成立体了。从技术上来说,AOP基本上是通过代理机制实现的。
AOP在编程历史上可以说是里程碑式的,对OOP编程是一种十分有益的补充
装饰器概述
装饰器是一个很著名的设计模式,经常被用于有切面需求的场景,较为经典的有插入日志、性能测试、事务处理等。装饰器是解决这类问题的绝佳设计,有了装饰器,我们就可以抽离出大量函数中与函数功能本身无关的雷同代码并继续重用。
概括的讲,装饰器的作用就是为已经存在的对象添加额外的功能。
装饰器本身的形式是处理其他的可调用对象的可调用对象或者说是装饰器自身是一个返回可调用对象的可调用对象
装饰器可以是函数,类及其他形式。
装饰器的很多神奇之处可归结为自动重绑定操作。
通过在一个函数的def语句的末尾来运行另一个函数,把最初的函数名重新绑定到结果。
函数装饰器可以用来管理函数调用和函数对象,类装饰器可以用来管理类实例和类自身。
管理函数调用
装饰器函数:
装饰器参数往往意味着可调用对象的3个层级:
接受装饰器参数的一个可调用对象,
它返回一个可调用对象以作为装饰器,
该装饰器返回一个可调用对象来处理对最初的函数或类的调用。
管理函数对象
以这种方式返回最初装饰的对象,而不是返回一个包装器,我们就可以管理函数和类自身,而不只是管理随后对它们的调用
3个python内置装饰器
@staticmethod | 类静态方法 | 与实例方法的区别是没有self参数,并且可以在类不进行实例化的情况下调用 |
@classmethod | 类方法 | 与实例方法的区别是接收的第一个参数不是self(类实例的指针),而是cls(当前类的具体类型) |
@property | 属性方法 | 将一个类方法转变成一个类属性,只读属性 |
函数装饰器
函数装饰器在函数定义的时候进行名称重绑定,提供一个逻辑层来管理函数和方法或随后对它们的调用
函数装饰器安装包装器对象,以在需要的时候拦截随后的函数调用并处理它们
由@符号以及紧随其后的对于元函数的一个引用组成
@metafunction
def function():
pass
# 等价于
function = metafunction(function)
类装饰器
类装饰器在类定义的时候进行名称重绑定, 提供一个逻辑层来管理类, 或管理随后调用它们所创建的示例。
类装饰器安装包装器对象,以在需要的时候拦截随后的实例创建调用并处理它们。
类装饰器:函数和类的组合
第三方包
decorator包
wrapt包
装饰器示例
@functools.wraps()
为了保证被装饰器装饰后的函数仍拥有原来的属性,Python的functools包中提供了一个叫wraps的装饰器(decorator),来消除被装饰函数变成了字符串
##############################################################
# funA 作为装饰器函数
def funA(fn):
print("A_1")
fn("B") # 执行传入的fn参数
print("A_2")
return "Result"
@funA
def funB(s):
print(s)
print("-----")
print(funB) # 这里funB变成了字符串,本来funB应该是函数的
funB("hhhhh")
##############################################################
"""
为了保证被装饰器装饰后的函数仍拥有原来的属性,Python的functools包中提供了一个叫wraps的装饰器(decorator)来消除这样的副作用
"""
# # funA 作为装饰器函数
# import functools
#
#
# def funA(func=None):
# print("A_1")
# if func is not None:
# @functools.wraps(func)
# def wrapper(*args, **kwargs):
# print('Calling decorated function...')
# func(*args, **kwargs)
#
# return wrapper
# print("A_2")
# return "Result"
#
#
# @funA
# def funB(s):
# print(s)
#
#
# print("--分界线,开始执行funB---")
# print(funB)
# funB("hhhhh")
函数装饰器分两种
元函数接受函数作为参数(不带参数的装饰器)
from functools import wraps
def logit(func):
@wraps(func)
def with_logging(*args, **kwargs):
print(func.__name__ + " was called")
return func(*args, **kwargs)
return with_logging
## 没有括号的是直接传函数进去的 ##
@logit
def addition_func(x):
"""Do some math."""
return x + x
result = addition_func(4)
元函数接受非函数作为参数(带参数的装饰器)
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 的文件出现了,里面的内容就是上面的字符串