我们先看一个例子:
class Idol(object):
def __new__(cls, name, age):
print('__new__ called')
return super(Idol, cls).__new__(cls)
def __init__(self, name, age):
print('__init__ called')
self.name = name
self.age = age
def __str__(self):
return 'Idol: %s-%s' % (self.name, self.age)
if __name__ == '__main__':
name = Idol('kobe', 24)
print(name)
实例化一个对象,输出结果:
__new__ called
__init__ called
Idol: kobe-24
通过这段代码我们看到,实例化一个类的时候,__new__
方法的调用发生在 __init__
之前。
和其他静态语言不一样,其他静态语言,直接调用了构造方法,一般情况下初始化的程序也写在构造方法之中。而 Python 实例化一个对象和初始化是分开的。
__new__
是类方法,__init__
是实例方法,也就是说,__init__
是在对象已经创建完成之后,才执行。
实例化一个类的语句为 p = Idol(name, age)
,其具体的执行逻辑如下:
Idol
类的 __new__
方法,这个 __new__
方法会返回 Idol
类的一个实例(通常情况下是使用 super(Idol, cls).__new__(cls)
这样的方式);__init__
方法,上一步里面 __new__
产生的实例也就是 __init__
里面的的 self
。由此,__init__
和 __new__
的区别在于:
__new__
通常用于控制生成一个新实例的过程。它是类级别的方法。__new__
至少要有一个参数 cls
,代表要实例化的类,该参数在实例化时由 Python 解释器自动提供;__new__
必须要有返回值,返回实例化出来的实例;__init__
通常用于初始化一个新实例,控制这个初始化的过程,比如添加一些属性,做一些额外的操作,发生在类实例被创建完以后。它是实例级别的方法。确保某一个类只有一个实例,而且自行实例化并向整个系统提供这个实例,这个类称为单例类。
例如,我们电脑的回收站就是单例模式,在整个操作系统中,回收站只能有一个实例,整个系统都使用这个唯一的实例,而且回收站自行提供自己的实例。
实现单例模式的核心原理是——重写类方法中的__new__方法
。
使用 类名()
创建对象时,Python 解释器首先会调用 __new__
方法为对象分配空间并返回对象的引用,Python 解释器获得对象的引用 后,将引用作为第一个参数,传递给 __init__
方法。
这是最基本的实现方式,因为没有加锁
synchronized
,所以它不支持多线程。
实现如下:
class Singleton(object):
__instance = None
def __new__(cls, num, name):
# 先判断是否有这个类的实例,没有就实例化一个并赋值给引用,有就直接返回该实例,以此保证单例
if not cls.__instance:
cls.__instance = super(Singleton, cls).__new__(cls)
return cls.__instance
def __init__(self, num, name):
self.num = num
self.name = name
if __name__ == '__main__':
a = Singleton(24, "kobe")
print(id(a))
print(a.num)
b = Singleton(30, "curry")
print(id(b))
print(b.num)
输出:
140392531769904
24
140392531769904
30
由输出我们看到,上面的代码实现了 Singleton
类的单例;同时我们也发现,单例类的 __new__
方法只能调用一次,但 __init__
方法会调用多次,我们可以在 __init__
方法中修改属性。
我们也可以实现只执行一次 __init__
方法的单例,通过在 __init__
方法中增加一个标志位来实现,如下:
class Singleton(object):
__instance = None
__first_init = False
def __new__(cls, num, name):
# 先判断是否有这个类的实例,没有就实例化一个并赋值给引用,有就直接返回该实例,以此保证单例
if not cls.__instance:
cls.__instance = super(Singleton, cls).__new__(cls)
return cls.__instance
def __init__(self, num, name):
if not self.__first_init:
self.num = num
self.name = name
self.__first_init = True
if __name__ == '__main__':
a = Singleton(24, "kobe")
print(id(a))
print(a.num)
b = Singleton(30, "curry")
print(id(b))
print(b.num)
输出:
139650736351792
24
139650736351792
24
通过在非线程安全的单例模式基础上加锁
synchronized
,实现线程安全的单例模式。
实现如下:
import threading
def synchronized(func):
func.__lock__ = threading.Lock()
def lock_func(*args, **kwargs):
with func.__lock__:
return func(*args, **kwargs)
return lock_func
class Singleton(object):
__instance = None
@synchronized
def __new__(cls, num, name):
# 先判断是否有这个类的实例,没有就实例化一个并赋值给引用,有就直接返回该实例,以此保证单例
if not cls.__instance:
cls.__instance = super(Singleton, cls).__new__(cls)
return cls.__instance
def __init__(self, num, name):
self.num = num
self.name = name