python 魔术方法(二) 对象的创建与单例模式的实现

2019-04-07 14:10:31   最后更新: 2019-04-07 14:10:31   访问数量:101




上一篇文章中,我们详细介绍了 Python 中的几个最常用的魔术方法

python 魔术方法(一) 自定义容器类与类属性控制

 

但上一篇文章中没有介绍 Python 创建对象的两个魔术方法 -- __new__ 与 __init__,而这两者的区别却常常困扰着很多 Python 开发人员

本文我们就来详细讲解他们的区别和用法

 

 

__init__

__init__ 方法作为类似于其他面向对象语言中构造函数的存在,是十分常用的,我们通常在 __init__ 方法中放入对象的初始化工作

def __init__(self)

 

 

__new__

与 __init__ 方法不同,__new__ 方法必须具有一个返回值,返回所创建对象的实例

def __new__(cls, *args, **kwargs)

 

 

class TechlogTest: def __init__(self): print('Techlog __init__') def __new__(cls, *args, **kwargs): print('Techlog __new__') return object.__new__(cls, *args, **kwargs) if __name__ == '__main__': testobj = TechlogTest()

 

 

运行结果如下:

Techlog __new__

Techlog __init__

 

可以看到,__new__ 方法在 __init__ 方法前被调用,加上 __new__ 方法必须返回当前类的实例,我们就可以知道他们的区别了

__new__ 方法担负了对象的创建工作,而 __init__ 方法则在对象完成创建后对该对象进行必要的初始化工作

同时,__new__ 方法的首个参数是 cls,实际上他是一个属于类的静态方法,这也是我们能够通过 object.__new__ 的方法来调用他的原因

 

因为 __new__ 方法担负了所有类对象的创建,因此我们可以通过实现 __new__ 方法就可以控制类对象的创建流程

单例模式就是一个很好的例子

 

单例模式

我们曾经介绍过单例模式:

单例模式 -- Singleton

java 实现单例的各种方式

 

他保证了一个类仅有一个实例,并且提供访问这个实例的全局访问方式

很多情况下,保证一个类同时最多只有一个实例是非常必要的,例如项目中的线程池组件,之所以使用线程池,往往是为了降低反复创建、销毁线程的开销,如果项目中维护多个线程池将是很令人头疼的一件事

有时,一个类也并没有必要存在多个实例,例如对于线程安全的类来说,一个实例可以处理并发环境下的所有请求,如果为每一个请求单独创建一个类实例,那么会造成很大程度上的资源浪费

 

python 实现单例模式

class SingleTon: _instance = {} def __new__(cls, *args, **kwargs): if cls not in cls._instance: cls._instance[cls] = super(SingleTon, cls).__new__(cls, *args, **kwargs) return cls._instance[cls]

 

 

上面这个类通过一个类成员 _instance 保存各个类型的单例实例,我们通过继承 SingleTon 类就可以实现单例模式了

继承到子类中的 __new__ 方法确保了无论如何创建,都保证只获取到一个对象,而 _instance 作为一个 dict 让我们可以同时创建多个单例模式类型

 

示例

class SingleTon: _instance = {} def __new__(cls, *args, **kwargs): if cls not in cls._instance: cls._instance[cls] = super(SingleTon, cls).__new__(cls, *args, **kwargs) return cls._instance[cls] class TechTest(SingleTon): testfield = 12 if __name__ == '__main__': tech01 = TechTest() tech02 = TechTest() print(tech01) print(tech02)

 

 

打印出了:

<__main__.TechTest object at 0x000001E40736A710>

<__main__.TechTest object at 0x000001E40736A710>

 

可以看到,两个对象的地址是一模一样的,他们实际上是同一个对象

 

欢迎关注微信公众号,以技术为主,涉及历史、人文等多领域的学习与感悟,每周三到七篇推文,全部原创,只有干货没有鸡汤

 

 






python      面向对象      设计模式      new      魔术方法     


京ICP备15018585号