自定义元类控制类的实例化行为

对象的三要素:id, type , value

在进行一些优化时,可能会遇到这种情况,多个对象具有相同的值(value),可是却有多个id,对于同一个我们想用到的值,

程序却向系统申请多个内存空间

a = [1, 2, 3, 523241, 31, 5215, 63, 634]
b = [1, 2, 3, 523241, 31, 5215, 63, 634]
print(a == b)
print(a is b)

结果如下

True
False

这不是我们想要的,我们希望这些对象使用同一片内存空间

那么,运用单例模式就能很好的优化这个问题

引用单例模式的概念:

单例模式是一种常用的软件设计模式。在它的核心结构中只包含一个被称为单例类的特殊类。通过单例模式可以保证系统中一个类只有一个实例而且该实例易于外界访问,从而方便对实例个数的控制并节约系统资源。如果希望在系统中某个类的对象只能存在一个,单例模式是最好的解决方案。显然单例模式的要点有三个:一是某个类只能有一个实例;二是它必须自行创建这个实例;三是它必须自行向整个系统提供这个实例。

okay,还是针对这个列表,我再写一遍:

a = [1, 2, 3, 523241, 31, 5215, 63, 634]
b = [1, 2, 3, 523241, 31, 5215, 63, 634]

现在问题是问题:怎么优化使其变成一个单例模式,即使id(a) == id(b) / a is b

我们知道,a和b都是列表这个类实例化的对象

a = [1, 2, 3, 523241, 31, 5215, 63, 634]

a = list([1, 2, 3, 523241, 31, 5215, 63, 634])

我们也知道元类可以通过__Init__方法控制类的创建行为

同样,元类也可以通过__call__方法控制类的实例化行为,在此例中,元类可以控制a这个列表对象的创建

由于默认a is b -->False,我们有理由相信list的元类控制list对象的创建过程中存在这么个逻辑:当list实例化时,每实例化一次都会为变量申请一个内存空间,

那么搞清楚这个逻辑,实现单例模式就很容易了:我们要使list实例化时,不要直接申请内存空间,而是,先判断是否存在一个要创建的对象(a)值相同的对象(b),如果有,那么

不要去申请内存空间了,我就直接把b赋值给a,那么保证a指向b的地址。

>>> a = [1, 2, 3, 523241, 31, 5215, 63, 634]
>>> b =a
>>> b
[1, 2, 3, 523241, 31, 5215, 63, 634]
>>> a
[1, 2, 3, 523241, 31, 5215, 63, 634]
>>> id(a)
48912392
>>> id(b)
48912392
>>> 

问题转化为:怎么使用__call__方法,使得类在实例化对象时会判断这个对象是否存在,如果不存在则创建新对象,如果存在,则不要创建了,直接引用
代码奉上:

class Mylist(type):

    def __init__(self, class_name, class_bases, class_dic):
        # 这里self.__instance存放Mylis实例化的对象,方便后面检测是否已经存在这个对象
        self.__instance = []
    
    # 这里的self指的是Mylis这个类(类也是对象,明白这个就知道为什么这里用的是self而不是cls了)
    def __call__(self, *args, **kwargs):
        # 类的实例化三步骤
        # 1.创建一个空对象
        obj = list.__new__(self)
        # 2.初始化对象
        self.__init__(obj, *args, **kwargs)
        # 3.返回对象
        if not obj in self.__instance:
            # 如果不存在和自己相同值的对象的对象,那么在实例化列表中增加这个对象,并实例化这个新对象
            self.__instance.append(obj)
            return obj
        else:
            # 如果和自己值相同的对象已经被实例化过,那么引用和自己对象值相同的那个对象
            return self.__instance[self.__instance.index(obj)]

# 我们需要自己定制一个list,这里名为Mylis,让它继承list类,以后就可以用Mylis了。
class Mylis(list,metaclass=Mylist):
    pass

检验一下:

a = Mylis([1, 2, 3, 523241, 31, 5215, 63, 634])
b = Mylis([1, 2, 3, 523241, 31, 5215, 63, 634])
print(a == b)
print(a is b)

结果:

True
True

这里,我们通过单例模式的实现掌握了定义元类控制类的实例化行为的方法。

 

posted on 2018-04-27 13:27  Tarantino  阅读(156)  评论(0编辑  收藏  举报

导航