• 借单例模式掌握python这几个语言要点


    前言

    单例模式是常用的设计模式,在Python里有多种实现方式,这些实现方式灵活地利用了python的各种语言特点,深入研究一下,能帮你大大提升python的语言功力。

    方法一:采用类变量实现单例

    def singleton(class_):
        instances = {}
        def getinstance(*args, **kwargs):
            if class_ not in instances:
                instances[class_] = class_(*args, **kwargs)
            return instances[class_]
        return getinstance
    
    @singleton
    class MyClass(BaseClass):
        pass
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9
    • 10
    • 11
    def test():
    	x = MyClass();
    	y = MyClass();
    	t = type(n)();
    	assert id(x) == id(y)
        assert id(x) != id(t) && id(y) != id(t)
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6

    方法二:用__new__来实现单例

    python的类变量相当于static变量,只存一份,所以可以利用它来实现单例。

    class Singleton(object):
        _instance = None
        def __new__(class_, *args, **kwargs):
            if not isinstance(class_._instance, class_):
                class_._instance = object.__new__(class_, *args, **kwargs)
            return class_._instance
    
    class MyClass(Singleton, BaseClass):
        pass
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9

    分析:重载一个类的__new__方法能接管对象的创建过程。多个实例都会共享同一份_instance。
    注意:Python的类变量并不是线程安全的。

    方法三:采用metaclass的__call__实现单例

    metaclass是什么?其实相当于javascript中的prototype(原型)对象,作用就是你可以扩充一个已存在类的方法,如给标准类string加方法。

    class Singleton(type):
        _instances = {}
        def __call__(cls, *args, **kwargs):
            if cls not in cls._instances:
                cls._instances[cls] = super(Singleton, cls).__call__(*args, **kwargs)
            return cls._instances[cls]
    
    #Python2
    class MyClass(BaseClass):
        __metaclass__ = Singleton
    
    #Python3
    class MyClass(BaseClass, metaclass=Singleton):
        pass
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9
    • 10
    • 11
    • 12
    • 13
    • 14

    __call__该方法的功能类似于在类中重载 () 运算符,使得类实例对象可以像调用普通函数那样,以“对象名()”的形式使用。

    def test_myclass():
        obj1 = MyClass() ## 会调用__call__方法
        obj2 = MyClass() ## 会再次调用__call__方法
        assert id(obj1) == id(obj2)
    
    • 1
    • 2
    • 3
    • 4

    方法四:采用类装饰器实现单例

    def singleton(cls):
        class class_w(cls):
            _instance = None
            def __new__(cls, *args, **kwargs):
                if class_w._instance is None:
                    class_w._instance = super(cls, cls).__new__(cls, *args,**kwargs)
                    class_w._instance._sealed = False
                return class_w._instance
            def __init__(self, *args, **kwargs):
                if self._sealed:
                    return
                super(class_w, self).__init__(*args, **kwargs)
                self._sealed = True
        class_w.__name__ = class_.__name__
        return class_w
    
    @singleton
    class MyClass(BaseClass):
        pass
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9
    • 10
    • 11
    • 12
    • 13
    • 14
    • 15
    • 16
    • 17
    • 18
    • 19

    解释:创建每个对象时, new()被调用,_sealed第一次调用时由False变为True,后续调用不再执行super(class_w, self).init(*args, **kwargs)

    总结一下

    • _init_(),对象实例化后被调用
    • _new_(),对象创建时被调用
    • _del_(), 对象析构时被调用
    • _call_(), 对象以"对象()"形式调用被调用,多用在一些框架中,起到动态调用的作用
    class CoolClass():
        def __init__(self, *args, **kwargs):
            print('CoolClass is initialized.')
            for k,v in kwargs.items():
                print(f"__init__: {k} = {v}")
        #
        def __new__(cls, *args, **kwargs):
            for k,v in kwargs.items():
                print(f"__new__: {k} = {v}")
            return super(CoolClass, cls).__new__(cls)
    
        def __call__(cls, *args, **kwargs):
            for k, v in kwargs.items():
                print(f"__call__: {k} = {v}")
    
    def test_object():
        obj = CoolClass(username='haha', passwd='xixi')
        obj(username='hoho', passwd='hihi')
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9
    • 10
    • 11
    • 12
    • 13
    • 14
    • 15
    • 16
    • 17
    • 18

    结果是先调用__new__(),再调用__init__(),最后调用__call__()。

    __call__在框架中的使用

    一个对象实现了__call__()方法,就变成了一个callable对象。有啥用处呢?就是在一些大型框架中,你可以先把这些callable对象创建好,然后在合适的时机调用它们,这给一些设计模式的实现带来了方便。还原一下就是下面的用法:

    class CallableClass1:
        def __call__(self, *args, **kwargs):
            print('I am class1')
    
    class CallableClass2:
        def __call__(self, *args, **kwargs):
            print('I am class2')
    
    class CallableClass3:
        def __call__(self, *args, **kwargs):
            print('I am class3')
    
    def test_callable():
        objs = []
        objs.append(CallableClass1())
        objs.append(CallableClass2())
        objs.append(CallableClass3())
        for obj in objs:
            if callable(obj):
                obj()
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9
    • 10
    • 11
    • 12
    • 13
    • 14
    • 15
    • 16
    • 17
    • 18
    • 19
    • 20

    实际场景中,CallableClass1~3有多种多样的构造方式,它们在初始化阶段创建好,然后暂时不用,需要的时候再用。

  • 相关阅读:
    Debezium系列之:实现表中指定字段相同的数据始终发往Kafka Topic相同的分区
    npm : 无法将“npm”项识别为 cmdlet、函数、脚本文件或可运行程序的名称。请检查名称的拼写,如果包括路径,请确保路径正确,然后再试一次。
    不知道音频格式转换软件哪个好?打工人都在用的几款你别错过
    GBASE南大通用签约广东省某城市商业银行
    【项目问题】Ionic 的生命周期以及Android “两次返回退出“的实现
    【PID控制技术】
    尚医通(二)
    Cadence Allegro PCB设计88问解析(十七) 之 Allegro中焊盘的全连接和花焊盘
    使用docker compose快速搭建spark集群
    Ab3d.DXEngine 6.0 Crack 2023
  • 原文地址:https://blog.csdn.net/jgku/article/details/128200094