zoukankan      html  css  js  c++  java
  • Python元类

    背景(类也是对象)

    class Foo:
          pass
    
    f1=Foo() #f1是通过Foo类实例化的对象
    

    python中一切皆是对象,类本身也是一个对象,当使用关键字class的时候,python解释器在加载class的时候就会创建一个对象(这里的对象指的是类而非类的实例),

    因而我们可以将类当作一个对象去使用,同样满足第一类对象的概念,可以:

    把类赋值给一个变量

    把类作为函数参数进行传递

    把类作为函数的返回值

    在运行时动态地创建类

    上例可以看出f1是由Foo这个类产生的对象,而Foo本身也是对象,那它又是由哪个类产生的呢?

    #type函数可以查看类型,也可以用来查看对象的类,二者是一样的
    print(type(f1)) # 输出:<class '__main__.Foo'> 表示,obj 对象由Foo类创建
    print(type(Foo)) # 输出:<type 'type'>
    

      

    什么是元类?

    元类是类的类,是类的模板

    元类是用来控制如何创建类的,正如类是创建对象的模板一样,而元类的主要目的是为了控制类的创建行为

    元类的实例化的结果为我们用class定义的类,正如类的实例为对象(f1对象是Foo类的一个实例,Foo类是 type 类的一个实例)

    type是python的一个内建元类,用来直接控制生成类,python中任何class定义的类其实都是type类实例化的对象

    创建类的两种方式

    方式一:使用class关键字

    class Foo(object):
        def __init__(self,name,age):
            self.name=name
            self.age=age
        def talk(self):
            print('%s is talking' %self.name)
    

     方式二:调用元类type(也可以自定义)来产生类

    def fn(self, name='world'): # 先定义函数
        print('Hello, %s.' % name)
    
    Hello = type('Hello', (object,), dict(hello=fn)) # 创建Hello class
    print(Hello.__dict__)
    
    print(Hello)
    print(type(Hello))
    print(isinstance(Hello,type))
    

     我们看到,type 接收三个参数:

    • 第 1 个参数是字符串 ‘Hello’,表示类名
    • 第 2 个参数是元组 (object, ),表示所有的父类
    • 第 3 个参数是字典,表示定义属性和方法

    案例

    基于元类实现单例模式,比如数据库对象,实例化时参数都一样,就没必要重复产生对象,浪费内存

    class Mysql(object):
        __instance=None
    
        def __init__(self,host='127.0.0.1',port=3306):
            self.host = host
            self.port = port
    
        @classmethod
        def singleton(cls,*args, **kwargs):
            if not cls.__instance:
                cls.__instance = cls(*args, **kwargs)
    
    
    m1 = Mysql()
    m2 = Mysql()
    print(m1 is m2)  # False
    
    m3 = Mysql.singleton()
    m4 = Mysql.singleton()
    
    print(m3 is m4) # True
    
    
    #实现方式二:元类的方式
    class Mymeta(type):
        def __init__(self,class_name,class_bases,class_dic):
            if not class_name.istitle():
                raise TypeError('类名的首字母必须大写')
    
            if '__doc__' not in class_dic or not class_dic['__doc__'].strip():
                raise TypeError('必须有注释,且注释不能为空')
    
            super(Mymeta,self).__init__(class_name,class_bases,class_dic)
            self.__instance=None
    
        def __call__(self, *args, **kwargs): #obj=Chinese('egon',age=18)
            if not self.__instance:
                obj=object.__new__(self)
                self.__init__(obj)
                self.__instance=obj
    
            return self.__instance
    
    
    
    class Mysql(object,metaclass=Mymeta):
        '''
        mysql xxx
        '''
        def __init__(self):
            self.host='127.0.0.1'
            self.port=3306
    
    
    
    obj1=Mysql()
    obj2=Mysql()
    
    
    print(obj1 is obj2)
    

     案例:在元类中控制把自定义类的数据属性都变成大写

    1.

    class Mymeta(type):
    
        def __init__(self, class_name,class_bases,class_dic):
            super(Mymeta, self).__init__(class_name, class_bases, class_dic)
    
        def __call__(self, *args, **kwargs):
            obj = object.__new__(self) # 创建对象
            print(type(obj))
            print(kwargs)
            for k, v in kwargs.items():
                obj.__dict__[k.upper()] = v
    
            return obj
    
    
    class Student(metaclass=Mymeta):
        name = 'alex'
        age = '18'
    
        def walk(self):
            print('%s is walking' %self.name)
    
    
    p = Student(name='egon',age='19',score='88')
    print(p.__dict__)
    #-------------输出结果-----------
    <class '__main__.Student'>
    {'name': 'egon', 'age': '19', 'score': '88'}
    {'NAME': 'egon', 'AGE': '19', 'SCORE': '88'}
    

     2.

    class Mymetaclass(type):
        def __new__(cls,name,bases,attrs):
            # 关键在于这,每一次实例化的时候,我们都只会返回这同一个instance对象
            # if not hasattr(cls, 'instance'):
            update_attrs = {}
            for k, v in attrs.items():
                if not callable(v) and not k.startswith('__'):
                    update_attrs[k.upper()] = v
                else:
                    update_attrs[k] = v
            return type.__new__(cls, name, bases, update_attrs)
    
    
    class Chinese(metaclass=Mymetaclass):
        country='China'
        tag='Legend of the Dragon' #龙的传人
        def walk(self):
            print('%s is walking' %self.name)
    
    
    print(Chinese.__dict__)
    

      

     

  • 相关阅读:
    PNG文件格式具体解释
    opencv2对读书笔记——使用均值漂移算法查找物体
    Jackson的Json转换
    Java实现 蓝桥杯VIP 算法训练 装箱问题
    Java实现 蓝桥杯VIP 算法训练 装箱问题
    Java实现 蓝桥杯VIP 算法训练 单词接龙
    Java实现 蓝桥杯VIP 算法训练 单词接龙
    Java实现 蓝桥杯VIP 算法训练 方格取数
    Java实现 蓝桥杯VIP 算法训练 方格取数
    Java实现 蓝桥杯VIP 算法训练 单词接龙
  • 原文地址:https://www.cnblogs.com/xiao-apple36/p/9175634.html
Copyright © 2011-2022 走看看