在使用logging这个模块进行单一log文件记录的时候碰到了log重复的问题,原因在于反复的调用log对象可能会加入重复的file或console handler,导致一次调用多次打印的问题
解决方法:
1.可以在每次用完logger对象后删除handler
2.不同的地方创建logger时,使用不同的name
3.使用单例
本文是对单例实现的几种方法作简单分析
1.通过重载__new__方法
class MyLogger(object):
_instance = None
def __new__(cls, *args, **kwargs):
if not cls._instance:
cls._instance = super(MyLogger, cls).__new__(cls, *args, **kwargs)
return cls._instance
在创建类的时候重写__new__方法
首先加一个类变量_instance = None
在__new__方法中做判断,如果_instance为初始的None,就通过super继承object的__new__方法来生成实例对象返回给_instance,这样从第二次开始实例化类,_instance对象都不再是None,会直接return cls._instance返回第一次那个实例化的对象。
2.通过装饰器
from functools import wraps
def singleton(cls):
_instance = {}
@wraps(cls)
def wrapper(*args, **kwargs):
if cls not in _instance:
_instance[cls] = cls(*args, **kwargs)
return _instance[cls]
return wrapper
@singleton
class my_singleton(object):
pass
a = my_singleton()
b = my_singleton()
print(id(a))
print(id(b))
通过装饰器实现的话原理类似,def singleton(cls):中的cls是传入的类实例
在装饰器函数中写一个_instance字典初始为空,如果cls这个实例对象不在_instance中就把这个实例对象添加到字典中,然后通过字典返回这个实例对象。从第二次开始创建实例,都是直接返回_instance字典中的第一次存入的实例对象。
需要注意:
由于使用装饰器后实际上是返回了一个新的函数,一些原本的信息会丢失,比如函数名,在这个例子中我原本的类名是my_singleton,使用装饰器后类名就变成装饰器singleton的名字。
所以这里还引入了functools中的wraps装饰器,这个装饰器实现的作用是将原本的信息保留,注意使用@wraps的时候需要把参数cls传递,否则内层的wrapper无法获取到cls
3.使用元类 metaclass singleton
class Singleton(type):
def __call__(cls, *args, **kwargs):
if not hasattr(cls, '_instance'):
cls._instance = super().__call__(*args, **kwargs)
return cls._instance
class MyClass(object, metaclass=Singleton):
pass
class1 = MyClass()
class2 = MyClass()
print(id(class1))
print(id(class2))
解析:对于一个类的实例化,如上例中的
class1 = MyClass()
通过类MyClass()实例化了一个对象class1,()是调用,MyClass()实际上执行的是MyClass类的__call__方法,所以从元类的角度实现单例的话,可以通过限制类的__call__。
每次实例化对象的时候调用的都是同一个__call__,得到的自然是同一个对象。
另外,元类Singleton必须继承type,python中类默认通过type来创建,要写一个元类来创建别的类,也就必须要继承type。