什么是单例模式
1.装饰器
参考:Python 函数装饰器
50道硬核python面试题
装饰器可以修饰类或函数,在不修改已有函数的代码和调用方式,为函数或类提供额外的功能。这些功能与正常的业务没有必然联系,可以动态添加和删除。本质上是一个闭包函数(内部函数调用外部函数的变量)。
1.1装饰器使用场景
Q1:记录函数执行时间的装饰器
1)用函数实现装饰器
from functools import wraps
from time import time
def record_time(func):
@wraps(func)
def wrapper(*args,**kwargs):
start = time()
result = func(*args,**kwargs)
print(f'{func.__name__}执行时间:{time()-start}秒')
return result
return wrapper
2)用类实现装饰器
类有魔术方法__call__,该类对象就是可调用对象,可以当做装饰器来使用
from functools import wraps
from time import time
class Record:
def __call__(self,func):
@wraps(func)
def wrapper(*args,**kwargs):
start = time()
result = func(*args,**kwargs)
print(f'{func.__name__}执行时间:{time()-start}')
return result
return wrapper
Q2:装饰器打印日志
1)使用函数实现装饰器
from functools import wraps
def logit(logfile='out.log'):
def logging_decorator(func):
@wraps(func)
def wrapper(*args,**kwargs):
log_string = func.__name__ +"was called"
print(log_string)
with open(logfile,'a') as opened_file:
opened_file.write(log_string+'/n')
return func(*args,**kwargs)
return wrapper
return logging_decorator
@logit(logfile='func2.log')
def myfunc2():
pass
#输出:myfunc2 was called
2)使用类实现装饰器
from functools import wraps
class logit(object):
def __init__(self,logfile='out.log'):
self.logfile = logfile
def __call__(self,func):
@wraps(func)
def wrapper(*args,**kwargs):
log_string = func.__name__+"was called"
print(log_string)
with open(logfile,'a') as opened_file:
opened_file.write(log_sting+'\n')
return func(*args,**kwargs)
return wrapper
Q3:访问网络出现指定异常,重试指定次数
1) 使用函数实现装饰器
from functools import wraps
from time import time
def retry(*,retry_times=3,max_wait_secs=5,errors=(Exception,)):
def decorate(func):
@wraps(func)
def wrapper(*args,**kwargs):
for _ in range(self.retry_times): # _ 表示可丢弃变量
try:
return func(*args,**kwargs)
except errors:
sleep(max_wait_secs)
return None
return wrapper
return decorate
2) 使用类实现装饰器
from functools import wraps
from time import time
class Retry(object):
def __init__(self,*,retry_times=3,max_wait_secs=5,errors=(Exception,)):
self.retry_times = retry_times
self.max_wait_secs = max_wait_secs
self.errors = errors
def __call__(self,func):
@wraps(func)
def wrapper(*args,**kwargs):
for _ in range(self.retry_times):
try:
return func(*args,**kwargs):
except self.errors:
sleep(max_wait_secs)
return None
return wrapper
Q4:装饰器实现单例
如下装饰器实现的单例不是线程安全的,如果要线程安全,需要对创建对象的代码加锁。
加锁方式:使用threading模块的RLock对象提供锁,使用锁对象的acquire和release来加锁和解锁
也可以使用所对象的with上下文语法来隐身加解锁
def sigleton(cls):
instance={}
@wraps(cls)
def cls_need_decorator(*args,**kwargs):
if cls not in instance:
instance[cls]=cls(*args,**kwargs)
return instance[cls]
return cls_need_decorator
@sigleton
class President:
pass
print(President())
#President = President.__wrapped__
print(President())
#注释President = President.__wrapped__输出
<__main__.President object at 0x00000213236F3B80>
<__main__.President object at 0x00000213236F3B80>
#注释President = President.__wrapped__输出
<__main__.President object at 0x0000020C9A4F3B80>
<__main__.President object at 0x0000020C9A4F3FD0>
Q5:装饰器使用方式
1)装饰器使用方式一
装饰器函数的参数传入函数,装饰器函数的返回值赋值给被装饰的函数名
def new_decorator(func):
def wrapTheFunc():
print("before")
func()
print("after")
return wrapTheFunc
def func_need_decorator():
print("doing sth")
func_need_decorator()
print("-----------------------")
func_need_decorator=new_decorator(func_need_decorator)
func_need_decorator()
#输出结果
doing sth
-----------------------
before
doing sth
after
2)装饰器函数使用方式二
函数上一行添加 @装饰器函数名
def new_decorator(func):
def wrapTheFunc():
print("before")
func()
print("after")
return wrapTheFunc
@new_decorator
def func_need_decorator():
print("doing sth")
func_need_decorator()
#输出结果
before
doing sth
after
1.2 相关知识:@wraps
使用@wraps定义的装饰器,对被装饰的函数或类添加__wrapped__属性,会保留被装饰前的函数或类,当不需要装饰器是可以通过__wrapped__来取消装饰器
使用@wraps定义函数装饰器,接受一个函数进行装饰,并加入复制函数名,参数列表,注释文档的功能,事例
def new_decorator(func):
@wraps(func)
def wrapTheFunc(*args,**kwargs):
print("before")
func()
print("after")
return func(*args,**kwargs)
return wrapTheFunc
@new_decorator
def fun_need_decorator():
"""Docstring"""
print("doing sth")
print(fun_need_decorator.__name__)
print(fun_need_decorator.__doc__)
#不注释@wraps(func)输出
fun_need_decorator
Docstring
#注释@wraps(func)输出
wrapTheFunc
None
1.2.1 wraps官方文档解释
参考文献
@functools.wraps
@functools.wraps(wrapped, assigned=WRAPPER_ASSIGNMENTS, updated=WRAPPER_UPDATES)
这是一个装饰器工厂函数,用于在定义装饰器函数时发起调用 update_wrapper() 作为函数装饰器。 它等价于** partial(update_wrapper, wrapped=wrapped, assigned=assigned, updated=updated)**。 例如:
>>> from functools import wraps
>>> def my_decorator(f):
... @wraps(f)
... def wrapper(*args, **kwds):
... print('Calling decorated function')
... return f(*args, **kwds)
... return wrapper
...
>>> @my_decorator
... def example():
... """Docstring"""
... print('Called example function')
...
>>> example()
Calling decorated function
Called example function
>>> example.__name__
'example'
>>> example.__doc__
'Docstring'
如果不使用这个装饰器工厂函数,则 example 函数的名称将变为 ‘wrapper’,并且 example() 原本的文档字符串将会丢失。
工厂函数:能够产生类实例的内建函数。比如, list(),tuple():生成列表或者元组
装饰器工厂函数:生产装饰器的函数
1.2.3 functools库
functools 模块应用于高阶函数,即——参数或(和)返回值为其他函数的函数。通常来说,此模块的功能适用于所有可调用对象。
1.2.4 functools.partial()
partial 函数的功能就是:把一个函数的某些参数给固定住,返回一个新的函数。
如果不固定参数,输入参数会赋给输入函数最左边的参数
def multiply(x, y):
return x * y
def double(x, y=2):
return multiply(x, y)
#可以不用自己定义 double,利用 partial,我们可以这样
from functools import partial
double = partial(multiply, y=2)
double(3) #输出6
1.2.5 update_wrapper()
@functools.update_wrapper(wrapper, wrapped, assigned = WRAPPER_ASSIGNMENTS, updated = WRAPPER_UPDATES)
参数:
wrapper:包装函数
wrapped:被包装的函数
assigned:包装函数的属性以元组(可选参数)的形式分配给包装函数的匹配属性。
updated:包装函数的属性相对于原始函数属性(作为元组(可选参数))进行了更新。
update_wrapper作用使被包装函数的模块级常量(例如__name , doc__等)保留原始数据
参考:update_wrapper()用法及代码示例
1.3 相关知识__call__的理解
参考python中的 call()
一个类实例要变成可调用对象(callable),就需要实现方法__call__()
2.元类
参考:Python中metaclass解释
mateclass的实例是class,class的实例是instance
语句class*MyObject(object): 表示python将在内存中创建一个名称为MyObject的对象
str函数用于创建字符串对象,int函数用于创建整型对象
type是一个metaclass用于在后台创建类
type可以将类的描述(类名称,继承元组,属性键值对)作为参数,然后生成一个类。
type(name of the class,
tuple of the parent class (for inheritance, can be empty),
dictionary containing attributes names and values)
MyShinyClass = type('MyShinyClass', (), {}) # returns a class object
>>>print MyShinyClass
#输出<class '__main__.MyShinyClass'>
2.1 如何使用metaclass
浅析python的metaclass
python将使用mateclass创建类时,会在新建的类中寻找__metaclass__如果找到了就用它创建,没有将在parent中找,没有就去module级别找,还是没有就用type创建类
1)用类的形式
首先,类继承type;
其次,将需要使用metaclass来构建class的类的__metaclass__属性赋值为Meta(继承于type的类)
class UpperAttribute(type):
def __new__(cls,class_name,class_parents,class_attr):
upper_attr={}
for name,val in class_attr.items():
if not name.startswith('__'):
upper_attr[name.upper()] = val
else:
upper_attr[name]=val
# return type.__new__(cls,class_name,class_parents,pper_attr)
return super(UpperAttribute,cls).__new__(cls,class_name,class_parents,pper_attr)
__new__是在__init__之前调用的方法,
__new__是创建并返回对象的方法
__init__初始化对象
2)用函数的形式
首先,构建一个函数metaclas_new,需要3个参数:name, bases, attrs
name:类的名字
bases:基类,通常是tuple类型
attrs:类的属性或者函数,字典类型
其次,将需要使用mateclass来构建class的类的__mateclass__属性赋值为函数metaclas_new
def upper_attr(class_name,class_parents,class_attr):
upper_attr={}
for name,val in class_arrt.items():
if not name.startswith('__'):
upper_attr[name.upper()]=val
else:
upper_attr[name]=val
return type(class_name,class_parents,upper_attr)
__mateclass__=upper_attr#影响模块中所有类
class Foo():
bar = 'bip'
print hasattr(Foo,'bar')
#输出False
print hasattr(Foo,'BAR')
#输出True
2.2 使用元类实现单例模式
class SingletonMeta(type):
def __init__(cls,*args,**kwargs):
cls.__instance=None
super().__init__(*args,**kwargs)
def __call__(cls,*args,**kwargs):
if cls.__instance is None:
cls.__instance=super().__call__(*args,**kwargs)
return cls.__instance
class President(metaclass=SingletonMeta):
pass