1、什么是装饰器?
装饰器就是:动态地给一个现有对象或函数附加额外功能,同时又不修改原有类或函数的源代码、不通过继承扩展子类。
和继承的区别:
- 继承是静态绑定:编译期就确定好了功能,运行时无法随时增删功能;
- 装饰器模式:运行时动态给对象叠加 / 移除功能;
- 如果依靠类继承扩展功能:每增加一种功能就要新建一个子类,功能组合一多,子类会爆炸式增多
2、Python装饰器
Python中的装饰器:一般是用于包装函数,函数式编程思想;
Python使用@符号,标记装饰器,是 Python 语法糖,在不修改原函数代码的前提下,给函数增加额外功能,比如:异常捕获、计时、日志、权限校验等。
2.1 异常捕获+日志
异常捕获装饰器:给被修饰的函数,添加捕获异常功能,并将异常记录到日志中;
防止因为没有处理被装饰的函数的异常,而导致崩溃;
python
def safe_wrapper(logger=None , default = None):
def outer(func):
def inner(*args, **kwargs):
try:
return func(*args, **kwargs)
except Exception as e:
if logger : logger.exception(f"{func.__doc__}失败 : {e}")
return default
return inner
return outer
1)第一层safe_wrapper:接收两个参数:日志对象、出错要返回的默认值;
2)第二层outer:标准装饰器入口,接收要被包装的函数 func;
3)第三层inner:真正替换原函数的内层函数,原函数所有参数传递给 *args **kwargs;
4)第四层 try-except:将原函数包装到异常处理中;
5)第五层func:执行原函数,返回执行结果;
用法:
python
import logging
logging.basicConfig(level=logging.INFO)
log = logging.getLogger()
@safe_wrapper(logger=log, default=None)
def div(a, b):
return a / b
print(div(10, 0))
捕获除零异常,不会崩溃,返回None,并打印异常日志
2.2 不使用语法糖
如果不使用Python的语法糖@,等同于使用下面的方式执行:div(10, 0)
python
def div(a, b):
return a / b
outer = safe_wrapper(logger=log, default=None)
inner = outer(div)
inner(10, 0)
2.3 统计函数运行时间
python
import time
def timer(func):
def wrap(*args, **kwargs):
start = time.time()
res = func(*args, **kwargs)
print(f"耗时:{time.time()-start:.2f}s")
return res
return wrap
@timer
def calc():
sum(range(1000000))
calc()

3、内置装饰器
3.1 @property 函数当变量
把类的函数伪装成实例变量(属性):
- 调用函数使用:class.fun();
- 伪装成变量后:class.fun;
- 默认不能赋值,添加setter修饰后,可以赋值;
python
class Person:
def __init__(self, birth_year):
self.birth_year = birth_year
self._age=0
self._set=False
@property
def age(self):
if(self._set):
return self._age
else:
return 2026 - self.birth_year
@age.setter
def age(self, val):
self._age = val
self._set = True

3.2 @staticmethod 静态函数
和C++一样:静态函数属于类,不用实例化类也可以直接调用的函数;
注意:静态函数中,不能调用非静态的函数和变量(实例属性);但是可以访问类属性
python
class Tools:
@staticmethod
def add(a, b):
return a + b
调用时,直接用类调用,不用创建对象也能调用
print(Tools.add(1, 2))
3.3 @classmethod 类函数
类函数 和 静态函数类似;区别如下:
- 类函数支持继承重写,静态函数不能;
- 类函数的第一个参数表示类本身,子类调用时,该参数自动变成子类;
python
class Tool:
PI = 3.14159
@staticmethod
def static_pi():
return Tool.PI
@classmethod
def class_pi(cls):
return cls.PI
class NewTool(Tool):
PI = 3.14
print(NewTool.static_pi())
输出,依然是父类的Tool值,static_pi中写死了 Tool.PI:
3.14159
print(NewTool.class_pi())
输出,是子类NewTool自己的PI:
3.14
- 静态函数:函数逻辑完全独立,既不调用类变量,也不调用实例变量,仅仅是把函数归类放到类中管理
- 类函数:需要调用类属性,例如:常用于工厂方法模式中,根据不同参数,创建类的实例
4、类属性和实例属性
C++中的成员变量,在Python中称为属性
类属性(类似C++中,静态成员变量)
- 写在 class 内部、
__init__外面,属于整个类本; - 所有实例共享同一份类属性;
- 可以通过类直接访问,也可以通过实例对象来访问;
实例属性(类似C++中,非静态成员变量)
- 在构造方法
__init__(self)里通过 self.xxx 定义,只属于每一个单独的对象实例; - 每个对象互相独立,修改互不影响;
- 只能通过实例对象访问,类无法直接访问;
可以通过 __dict__,来打印所有的属性字典
python
class Student:
school = "河北石油大学"
def __init__(self, name, age):
self.name = name
self.age = age
laoer = Student("laoer", 28)
print(laoer.__dict__)
print(Student.__dict__)
