python的__new__和__init__方法

文章目录

  • 1. __new__和__init__方法的区别
  • 2. 单例模式
    • 2.1 什么是单例
    • 2.2 单例模式
      • 懒汉式非线程安全的单例模式
      • 懒汉式线程安全的单例模式

1. __new__和__init__方法的区别

我们先看一个例子:

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__ 通常用于初始化一个新实例,控制这个初始化的过程,比如添加一些属性,做一些额外的操作,发生在类实例被创建完以后。它是实例级别的方法。

2. 单例模式

2.1 什么是单例

确保某一个类只有一个实例,而且自行实例化并向整个系统提供这个实例,这个类称为单例类。

例如,我们电脑的回收站就是单例模式,在整个操作系统中,回收站只能有一个实例,整个系统都使用这个唯一的实例,而且回收站自行提供自己的实例。

2.2 单例模式

实现单例模式的核心原理是——重写类方法中的__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

你可能感兴趣的:(Python学习笔记,python,单例模式)