装饰器 单例模式学习
一. 什么是装饰器?
装饰器本质上就是一个python函数,他可以让其他函数在不需要做任何代码变动的前提下,增加额外的功能,装饰器的返回值也是一个函数对象。 装饰器的应用场景:比如插入日志,性能测试,事务处理,缓存等等场景。
二,装饰器的形成过程。
现在我有一个需求,我想让你测试这个函数的执行时间,在不改变这个函数代码的情况下:
import time def func1(): #要进行测试软件 print('in func1') def timer(func): def inner(): start = time.time() func() print(time.time() - start) return inner func1 = timer(func1) #先执行右边的timer函数,把func传进去,并接受返回值inner,再把inner赋值给func,所以说此时的func是inner函数 func1() #执行func函数,注意此时的func其实是inner
如果有n给函数都需要测试时间,并且函数名不尽相同,那一个一个的写是不是很累?
python做了优化,给我们一个语法糖使用.
import time def timer(func): def inner(): start = time.time() func() print(time.time() - start) return inner @timer #这个语法糖相当于这句话 func1 = timer(func1) def func1(): print('in func1') func1()1
之前的func都是不带参数的,带参数的又怎么设计呢?
import time def timer(f): def inner(x,y): #func带有什么参数,inner就必须有什么参数,要不然,inner里面的func'真正执行的时候,拿不到值 start_time=time.time() f(x,y) end_time=time.time() print(end_time-start_time) return inner @timer #老样子,先执行timer,把func传进去,返回inner,inner赋值给func, def func(x,y): print(x+y) func(10,20) #此时func其实是inner,执行inner
每个函数的的参数个数,类型也有差别并不能解决
我们所有函数,所以下面请看,无敌参数版装饰器
import time def timer(f): def inner(*args,**kwargs): start_time=time.time() f(*args,**kwargs) end_time=time.time() print(end_time-start_time) return inner @timer def func(*args,**kwargs): print(*args,**kwargs) func(10,20,30,a=40)
上面是一样流程,反正func有什么的参数,
inner必须有相同的参数,,inner里面的func才能真正的拿到参数.
我们所看的某些函数是有返回值的对不对,那有返回值的
函数又该怎么设计呢?
import time def timer(f): def inner(*args,**kwargs): #3执行inner,把第一步func传进来的参数给inner start_time=time.time() ret=f(*args,**kwargs) #4执行f,其实就行执行func,并有返回值 end_time=time.time() print(end_time-start_time) return ret #返回ret,这边是inner执行就是返回给inner,但是inner又赋值给func了,所以其实返回给func return inner @timer #1执行func=timer(func),返回inner,此时func=inner def func(x,y): return x+y ret=func(10,20) #2执行func,其实就是执行inner print(ret)
上面的装饰器已经非常完美了,但是有我们正常情况下查看函数信息的方法在此处都会失效:
def index(): '''这是一个主页信息''' print('from index') print(index.__doc__) #查看函数注释的方法 print(index.__name__) #查看函数名的方法
如何解决呢?
from functools import wraps def deco(func): @wraps(func) #加在最内层函数正上方 def wrapper(*args,**kwargs): return func(*args,**kwargs) return wrapper @deco def index(): '''哈哈哈哈''' print('from index') print(index.__doc__) print(index.__name__)
三. 开放封闭原则。
1.对扩展是开放的 为什么要对扩展开放呢? 我们说,任何一个程序,不可能在设计之初就已经想好了所有的功能并且未来不做任何更新和修改。所以我们必须允许代码扩展、添加新功能。 2.对修改是封闭的 为什么要对修改封闭呢? 就像我们刚刚提到的,因为我们写的一个函数,很有可能已经交付给其他人使用了,如果这个时候我们对其进行了修改,很有可能影响其他已经在使用该函数的用户。 装饰器完美的遵循了这个开放封闭原则。
四,装饰器的主要功能和固定结构。
def timer(f):
def inner(*args,**kwargs):
"""执行函数之前要做的"""
ret=f(*args,**kwrags)
""""执行函数之后要做的"""
return ret
return inner
装饰器的固定格式--wraps版
from functools import wraps def deco(func): @wraps(func) #加在最内层函数正上方 def wrapper(*args,**kwargs): return func(*args,**kwargs) return wrapper
五,带参数的装饰器。
假如你有成千上万个函数使用了一个装饰器,现在你想把这些装饰器都取消掉,你要怎么做?
一个一个的取消掉? 没日没夜忙活3天。。。
过两天你领导想通了,再让你加上。。。
def outer(flag): def timer(func): def inner(*args,**kwargs): if flag: print('''执行函数之前要做的''') re = func(*args,**kwargs) if flag: print('''执行函数之后要做的''') return re return inner return timer @outer(False) def func(): print(111) func()
六,多个装饰器装饰一个函数。
def wrapper1(func): def inner(): print('wrapper1 ,before func') func() print('wrapper1 ,after func') return inner def wrapper2(func): def inner(): print('wrapper2 ,before func') func() print('wrapper2 ,after func') return inner @wrapper2 @wrapper1 def f(): print('in f') f()
关于单例模式
先来看看一个例子
class A: def __init__(self): self.x = 1 print('in init function') def __new__(cls, *args, **kwargs): print('in new function') return object.__new__(A, *args, **kwargs) a = A() #创建对象,先执行new构造函数,再执行init初始化函数 print(a.x) #再去a中取出x
接下里看一下单例模式的写法
class A: __instance = None def __new__(cls, *args, **kwargs): if cls.__instance is None: obj = object.__new__(cls) cls.__instance = obj return cls.__instance
关于单例模式的分析
单例模式是一种常用的软件设计模式。在它的核心结构中只包含一个被称为单例类的特殊类。通过单例模式可以保证系统中一个类只有一个实例而且该实例易于外界访问,从而方便对实例个数的控制并节约系统资源。如果希望在系统中某个类的对象只能存在一个,单例模式是最好的解决方案。 【采用单例模式动机、原因】 对于系统中的某些类来说,只有一个实例很重要,例如,一个系统中可以存在多个打印任务,但是只能有一个正在工作的任务;一个系统只能有一个窗口管理器或文件系统;一个系统只能有一个计时工具或ID(序号)生成器。如在Windows中就只能打开一个任务管理器。如果不使用机制对窗口对象进行唯一化,将弹出多个窗口,如果这些窗口显示的内容完全一致,则是重复对象,浪费内存资源;如果这些窗口显示的内容不一致,则意味着在某一瞬间系统有多个状态,与实际不符,也会给用户带来误解,不知道哪一个才是真实的状态。因此有时确保系统中某个对象的唯一性即一个类只能有一个实例非常重要。 如何保证一个类只有一个实例并且这个实例易于被访问呢?定义一个全局变量可以确保对象随时都可以被访问,但不能防止我们实例化多个对象。一个更好的解决办法是让类自身负责保存它的唯一实例。这个类可以保证没有其他实例被创建,并且它可以提供一个访问该实例的方法。这就是单例模式的模式动机。 【单例模式优缺点】 【优点】 一、实例控制 单例模式会阻止其他对象实例化其自己的单例对象的副本,从而确保所有对象都访问唯一实例。 二、灵活性 因为类控制了实例化过程,所以类可以灵活更改实例化过程。 【缺点】 一、开销 虽然数量很少,但如果每次对象请求引用时都要检查是否存在类的实例,将仍然需要一些开销。可以通过使用静态初始化解决此问题。 二、可能的开发混淆 使用单例对象(尤其在类库中定义的对象)时,开发人员必须记住自己不能使用new关键字实例化对象。因为可能无法访问库源代码,因此应用程序开发人员可能会意外发现自己无法直接实例化此类。 三、对象生存期 不能解决删除单个对象的问题。在提供内存管理的语言中(例如基于.NET Framework的语言),只有单例类能够导致实例被取消分配,因为它包含对该实例的私有引用。在某些语言中(如 C++),其他类可以删除对象实例,但这样会导致单例类中出现悬浮引用