[Python] 使用new构造单例模式

1. self 和 cls

首先来简要介绍一下类中的self和cls,如下栗:

class A(object):
 def foo1(self):
    print("Hello", self)

 @classmethod
 def foo2(cls):
    print("Hello", cls)

调用foo1:

>>>a = A()
>>>a.foo1()
Hello <__main__.A object at 0x1040263c8>
>>>print(a)
Hello <__main__.A object at 0x1040263c8>

可以发现self和实例a指向了同一个对象,再调用类方法foo2:

>>>A.foo2()
Hello <class '__main__.A'>

发现此时指向的就是A类本身,从这个栗子我们可以得出,self指向了实例化对象而cls指向了类本身。

2. __init____new__

我们再来看一下__init__,相信大家对__init__已经很熟悉了,__init__通常用于初始化一个类实例,如下例:

class A(object):
   def __init__(self, x, y):
       self.x = x
       self.y = y

由于它是初始化一个类实例的,所有需要传入self。

那么__new__方法又是做什么用的呢?看下面的栗子:

class A(object):
    def __init__(self, x, y):
        print('__init__ has been called')
        self.__x = x
        self.__y = y

    def __new__(cls, *args, **kwargs):
        print('__new__ has been called')
        return super(A, cls).__new__(cls, *args, **kwargs)

    def __str__(self):
        return 'A : x is {}, y is {}'.format(self.__x, self.__y)

这里有个小插曲,当我尝试实例化A类,并将其传入参数x,y时,报了以下错误:

>>>tt=A('xxx','yyy')

in __new__
    return super(A, cls).__new__(cls, *args, **kwargs)
TypeError: object() takes no parameters

仔细阅读报的错,发现是由于__new__返回的是父类实例化后的对象的—__new__方法,而父类object__new__方法不接受任何参数,可我却对其传了参。解决方法就是不对父类实例化的对象的__new__方法传参,将上述__new__方法改成如下:

    def __new__(cls, *args, **kwargs):
        print('__new__ has been called')
        return super(A, cls).__new__(cls)

再次运行

>>>tt=A('xxx','yyy')
>>>print(tt)

__new__ has been called
__init__ has been called
A : x is xxx, y is yyy

一切都平静了,可以看到,当实例化对象时,先是调用了__new__,然后才调用了__init__进行了初始化。其中__new__方法中可以return父类__new__出来的实例,也可以直接将其他类__new__出来的实例返回(但这就意义不大了)。
那可不可以调用自身的__new__来制造实例呢?当然不行,因为这会造成死循环(一定要避免return cls.__new__(cls))。

新式类(最终继承到object)开始实例化时,__new__会返回一个实例,然后该类的__init__方法作为构造方法回接收这个实例(即self)作为自己的第一个参数,然后依次传入__new__方法中接收的其它参数。

如果 __new__并未返回实例,那么当前类的__init__方法是不会被调用的。

3. 使用__new__构造单例模式

说了那么多,__new__方法到底有什么作用呢?接下来介绍一下如何使用它来创建单例模式。

单例模式简单来说就是即一个类只有一个实例,看下面的栗子:

class A(object):
    _a = {}

    def __new__(cls, *args, **kwargs):
        ob = super(A, cls).__new__(cls)
        ob.__dict__ = cls._a
       
        return ob

实例化A,赋给a,并向_a添加新的键值对:

>>>a = A()
>>>a._a['a'] = 1

再次实例化A,赋给b,查看b的__dict__

>>>b = A()
>>>print(b.__dict__)
{'a':1}
>>>print(b.a)
1

居然有先前实例a的属性,而且居然还可以直接调用!
我们将实例b也赋予一些属性,并尝试通过实例a去调用:

>>>b.b=2
>>>print(a.b)
2

仿佛实例a与实例b成了同一个实例,只是有了两个名字!这就实现了单例模式。
我们来看一下上面的代码,__dict__是用来存储对象属性的一个字典,在A类中我们创建了一个空字典_a并将其传入__dict__,由于字典恰好是可变对象,所以当我们对任何A的实例添加属性进入__dict__时,_a也进行了更新,可以说_a中保存了最新的所有的A对象的属性键值对。将_a赋给实例的__dict__,每个实例也就拥有了同一个__dict__,真是神奇。

于此类似的,利用__new__构建单例模式还有一种方法,如下栗:

class Singleton(object):
    def __new__(cls, *args, **kw):
        if not hasattr(cls, '_instance'):
            orig = super(Singleton, cls)
            cls._instance = orig.__new__(cls, *args, **kw)
        return cls._instance

class MyClass(Singleton):
    a = 1

具体的实现原理就不做赘述啦,相信读到这里你一定可以理解~

©著作权归作者所有,转载或内容合作请联系作者
  • 序言:七十年代末,一起剥皮案震惊了整个滨河市,随后出现的几起案子,更是在滨河造成了极大的恐慌,老刑警刘岩,带你破解...
    沈念sama阅读 214,588评论 6 496
  • 序言:滨河连续发生了三起死亡事件,死亡现场离奇诡异,居然都是意外死亡,警方通过查阅死者的电脑和手机,发现死者居然都...
    沈念sama阅读 91,456评论 3 389
  • 文/潘晓璐 我一进店门,熙熙楼的掌柜王于贵愁眉苦脸地迎上来,“玉大人,你说我怎么就摊上这事。” “怎么了?”我有些...
    开封第一讲书人阅读 160,146评论 0 350
  • 文/不坏的土叔 我叫张陵,是天一观的道长。 经常有香客问我,道长,这世上最难降的妖魔是什么? 我笑而不...
    开封第一讲书人阅读 57,387评论 1 288
  • 正文 为了忘掉前任,我火速办了婚礼,结果婚礼上,老公的妹妹穿的比我还像新娘。我一直安慰自己,他们只是感情好,可当我...
    茶点故事阅读 66,481评论 6 386
  • 文/花漫 我一把揭开白布。 她就那样静静地躺着,像睡着了一般。 火红的嫁衣衬着肌肤如雪。 梳的纹丝不乱的头发上,一...
    开封第一讲书人阅读 50,510评论 1 293
  • 那天,我揣着相机与录音,去河边找鬼。 笑死,一个胖子当着我的面吹牛,可吹牛的内容都是我干的。 我是一名探鬼主播,决...
    沈念sama阅读 39,522评论 3 414
  • 文/苍兰香墨 我猛地睁开眼,长吁一口气:“原来是场噩梦啊……” “哼!你这毒妇竟也来了?” 一声冷哼从身侧响起,我...
    开封第一讲书人阅读 38,296评论 0 270
  • 序言:老挝万荣一对情侣失踪,失踪者是张志新(化名)和其女友刘颖,没想到半个月后,有当地人在树林里发现了一具尸体,经...
    沈念sama阅读 44,745评论 1 307
  • 正文 独居荒郊野岭守林人离奇死亡,尸身上长有42处带血的脓包…… 初始之章·张勋 以下内容为张勋视角 年9月15日...
    茶点故事阅读 37,039评论 2 330
  • 正文 我和宋清朗相恋三年,在试婚纱的时候发现自己被绿了。 大学时的朋友给我发了我未婚夫和他白月光在一起吃饭的照片。...
    茶点故事阅读 39,202评论 1 343
  • 序言:一个原本活蹦乱跳的男人离奇死亡,死状恐怖,灵堂内的尸体忽然破棺而出,到底是诈尸还是另有隐情,我是刑警宁泽,带...
    沈念sama阅读 34,901评论 5 338
  • 正文 年R本政府宣布,位于F岛的核电站,受9级特大地震影响,放射性物质发生泄漏。R本人自食恶果不足惜,却给世界环境...
    茶点故事阅读 40,538评论 3 322
  • 文/蒙蒙 一、第九天 我趴在偏房一处隐蔽的房顶上张望。 院中可真热闹,春花似锦、人声如沸。这庄子的主人今日做“春日...
    开封第一讲书人阅读 31,165评论 0 21
  • 文/苍兰香墨 我抬头看了看天上的太阳。三九已至,却和暖如春,着一层夹袄步出监牢的瞬间,已是汗流浃背。 一阵脚步声响...
    开封第一讲书人阅读 32,415评论 1 268
  • 我被黑心中介骗来泰国打工, 没想到刚下飞机就差点儿被人妖公主榨干…… 1. 我叫王不留,地道东北人。 一个月前我还...
    沈念sama阅读 47,081评论 2 365
  • 正文 我出身青楼,却偏偏与公主长得像,于是被迫代替她去往敌国和亲。 传闻我的和亲对象是个残疾皇子,可洞房花烛夜当晚...
    茶点故事阅读 44,085评论 2 352

推荐阅读更多精彩内容

  • 转至元数据结尾创建: 董潇伟,最新修改于: 十二月 23, 2016 转至元数据起始第一章:isa和Class一....
    40c0490e5268阅读 1,698评论 0 9
  • Python 面向对象Python从设计之初就已经是一门面向对象的语言,正因为如此,在Python中创建一个类和对...
    顺毛阅读 4,212评论 4 16
  • 五一放假前一天,我第二次裸辞 在经历了17个工作日后 之所以这么清楚是17个工作日,是因为辞职那天我用计算器算了下...
    雪小离阅读 268评论 1 0
  • 甘露元年春正月,那时的文皇帝加大都督,奏事不名。夏六月,进封高都公,地方七百里,加之九锡,假斧钺,进号大都督,剑履...
    霍真布鲁兹老爷阅读 466评论 1 4
  • 清晨参加线下晨跑。和大家一起在雅拉河边有说有笑,不知不觉就跑了6公里多,突破了个人的最远距离。还是集体的力量大。
    AndySong_1ee8阅读 260评论 0 0