• Python元类简单了解和单例模式


    class 关键字定义类是python提供的语法,python解释器读取到定义类的语法时会调用type(类名,符类列表,类名称空间字典)来创造类,我们称type为所有类默认的元类。有了元类这个特性我们可以基于元类来自定义一些类的操作

    1.不使用class创造类的过程

    # 类名
    class_name='StanfordTeacher'
    # 父类
    class_bases=(object,)
    #名称空间
    class_dic = {}
    s = """
    def __init__(self,name,age):
        self.name=name
        self.age=age
    
    def say(self):
        print('%s says welcome to the Stanford to learn Python' %self.name)
    """
    
    exec(s,{},class_dic)
    
    class_obj = type(class_name,class_bases,class_dic )
    
    # class_obj ==》<class '__main__.StanfordTeacher'>

    2.自定义元类,并且加入一些控制

    class Mymeta(type): #只有继承了type类才能称之为一个元类,否则就是一个普通的自定义类
        def __init__(self,class_name,class_bases,class_dic):
            # print(self) #<class '__main__.StanfordTeacher'>
            # print(class_bases) #(<class 'object'>,)
            # print(class_dic) #{'__module__': '__main__', '__qualname__': 'StanfordTeacher', 'school': 'Stanford', '__init__': <function StanfordTeacher.__init__ at 0x102b95ae8>, 'say': <function StanfordTeacher.say at 0x10621c6a8>}
            super(Mymeta, self).__init__(class_name, class_bases, class_dic)  # 重用父类的功能
    
            if class_name.islower():
                raise TypeError('类名%s请修改为驼峰体' %class_name)
    
            if '__doc__' not in class_dic or len(class_dic['__doc__'].strip(' 
    ')) == 0:
                raise TypeError('类中必须有文档注释,并且文档注释不能为空')
    
    # StanfordTeacher=Mymeta('StanfordTeacher',(object),{...})
    class StanfordTeacher(object,metaclass=Mymeta): 
        """
        类StanfordTeacher的文档注释
        """
        school='Stanford'
    
        def __init__(self,name,age):
            self.name=name
            self.age=age
    
        def say(self):
            print('%s says welcome to the Stanford to learn Python' %self.name)

    3.重写元类中的__call__方法实现控制类的实例化

    class Mymeta(type): #只有继承了type类才能称之为一个元类,否则就是一个普通的自定义类
        def __call__(self, *args, **kwargs):
            print(self) #<class '__main__.StanfordTeacher'>
            print(args) #('lili', 18)
            print(kwargs) #{}
            return 123
    
    class StanfordTeacher(object,metaclass=Mymeta):
        school='Stanford'
    
        def __init__(self,name,age):
            self.name=name
            self.age=age
    
        def say(self):
            print('%s says welcome to the Stanford to learn Python' %self.name)
    
    
    
    # 调用StanfordTeacher就是在调用StanfordTeacher类中的__call__方法
    # 然后将StanfordTeacher传给self,溢出的位置参数传给*,溢出的关键字参数传给**
    # 调用StanfordTeacher的返回值就是调用__call__的返回值
    t1=StanfordTeacher('lili',18)
    print(t1) #123

    4.单例模式

    #步骤五:基于元类实现单例模式
    # 单例:即单个实例,指的是同一个类实例化多次的结果指向同一个对象,用于节省内存空间
    # 如果我们从配置文件中读取配置来进行实例化,在配置相同的情况下,就没必要重复产生对象浪费内存了
    #settings.py文件内容如下
    HOST='1.1.1.1'
    PORT=3306
    
    #方式一:定义一个类方法实现单例模式
    import settings
    
    class Mysql:
        __instance=None
        def __init__(self,host,port):
            self.host=host
            self.port=port
    
        @classmethod
        def singleton(cls):
            if not cls.__instance:
                cls.__instance=cls(settings.HOST,settings.PORT)
            return cls.__instance
    
    obj1=Mysql('1.1.1.2',3306)
    obj2=Mysql('1.1.1.3',3307)
    print(obj1 is obj2) #False
    
    obj3=Mysql.singleton()
    obj4=Mysql.singleton()
    print(obj3 is obj4) #True
    
    
    #方式二:定制元类实现单例模式
    import settings
    
    class Mymeta(type):
        def __init__(self,name,bases,dic): #定义类Mysql时就触发
            # 事先先从配置文件中取配置来造一个Mysql的实例出来
            self.__instance = object.__new__(self)  # 产生对象
            self.__init__(self.__instance, settings.HOST, settings.PORT)  # 初始化对象
            # 上述两步可以合成下面一步
            # self.__instance=super().__call__(*args,**kwargs)
            super().__init__(name,bases,dic)
    
        def __call__(self, *args, **kwargs): #Mysql(...)时触发
            if args or kwargs: # args或kwargs内有值
                obj=object.__new__(self)
                self.__init__(obj,*args,**kwargs)
                return obj
            return self.__instance
    
    class Mysql(metaclass=Mymeta):
        def __init__(self,host,port):
            self.host=host
            self.port=port
    
    obj1=Mysql() # 没有传值则默认从配置文件中读配置来实例化,所有的实例应该指向一个内存地址
    obj2=Mysql()
    obj3=Mysql()
    print(obj1 is obj2 is obj3)
    obj4=Mysql('1.1.1.4',3307)
    
    
    #方式三:定义一个装饰器实现单例模式
    import settings
    
    def singleton(cls): #cls=Mysql
        _instance=cls(settings.HOST,settings.PORT)
    
        def wrapper(*args,**kwargs):
            if args or kwargs:
                obj=cls(*args,**kwargs)
                return obj
            return _instance
        return wrapper
    
    
    @singleton # Mysql=singleton(Mysql)
    class Mysql:
        def __init__(self,host,port):
            self.host=host
            self.port=port
    
    obj1=Mysql()
    obj2=Mysql()
    obj3=Mysql()
    print(obj1 is obj2 is obj3) #True
    
    obj4=Mysql('1.1.1.3',3307)
    obj5=Mysql('1.1.1.4',3308)
    print(obj3 is obj4) #False
  • 相关阅读:
    Redis高可用集群构架原理及高并发
    zabbix 6.0 使用 fping 批量监控 ip 地址 海口
    Loki收集Nginx日志并以grafana展示 海口
    python实现jupyter代码的运行
    How to fix 'sudo: no tty present and no askpass program specified' error?
    go实现jupyter代码的运行
    go实现正则匹配
    gogin使用websocket
    Excel
    对worklogworklogTask表的日期进行计算
  • 原文地址:https://www.cnblogs.com/wtil/p/13535568.html
Copyright © 2020-2023  润新知