zoukankan      html  css  js  c++  java
  • 初识python元类(metaclass)

    听过一句话,在python中一切皆对象,而在python3中统一了类和类型。

    我们可以用type方法查看一个变量(对象)的类型,即是查看对象的类:

    x=2
    y='dasd'
    z=[]
    class Student:
        school='jjjj'
        def __init__(self,name):
            self.name=name
    
    
    print(type(x),type(y),type(z),type(Student),)
    
    #结果:<class 'int'> <class 'str'> <class 'list'> <class 'type'>
    当然在了解元类之前,我们需要了解一些姿势点,方便以后运用。
    1:python内置函数exec(解释:独立(在其他语言中不一定独立)执行一段python字符串代码,并留下一些东东)
    exec(a,b,c) 我们一般是这样用。
      a:要执行的字符串代码
      b:a中可能用到的外部变量(字典格式,一般是全局的)
      c:返回执行a后产生的名称空间(不要问我是什么,字典格式)

    name='wx'
    a='''
    k=name
    j='das'
    def pp():
        pass
    '''
    b={'name':name}
    c={}
    exec(a,b,c)
    print(c)
    #结果:{'k': 'wx', 'j': 'das', 'pp': <function pp at 0x0569A228>} nn
    2.__call__方法(类中定义这方法时,实例化出的对象被调用时会触发此方法)
    class Teacher:
        def __call__(self, *args, **kwargs):
            print('__call__ 执行')
            print(args)
            return 444
    
    obj=Teacher()#实例化
    print(obj('llll'))#调用对象,当然也可传参
    
    '''结果:
    __call__ 执行
    ('llll',)
    444
    '''
    3.__new__方法主要是当你继承一些不可变的class时(比如int, str, tuple,可变的也行)
    提供给你一个自定义这些类的实例化过程的途径,还有就是实现自定义的metaclass。还有许多用途。
    这里我们主要是用其创建一个新的对象.
    class UpperStr(str):
        def __new__(cls, s):
            return super().__new__(cls,s.upper())
    
    print(UpperStr('asdasd'))
    #结果:ASDASD
    class MyNew:
        def __init__(self,name,age):
            self.name=name
        def __new__(cls, *args, **kwargs):
           obj=super().__new__(cls)#造一个空对象,也调用了MyNew的__init__方法
           # #为对象加个属性
           obj.new_age=kwargs.get('age')
           return obj
    
    obj=MyNew(name='da',age=18)
    print(obj,vars(obj),)
    #结果:<__main__.MyNew object at 0x057D4070> {'new_age': 18, 'name': 'da'}

    '''
    回到正题,我们查看类type(Student)时看到的Student类的类为type,也就是说Student类是由type类产生的,
    我们把产生类的类称为元类,python中默认的元类是type
    当我我们定义类的时候
    '''
    class Student(object,metaclass=type):
        #其实这里指定了类由哪个元类产生,默认是type
        #即是Student=type(.....) 由type类实例化而来
        pass
    
    
    
     

    '''
    那我们为毛要学习元类:为了掌握一种定义类的方式,在定义类时加一下限定条件。 我们定义类时,一种方式是用关键字class 这里我们来模拟一下class创建类的过程, 首先创建类需要什么? 1:类名 2:基类(父类) 3:类体代码(一堆字符串) ''' #类名 class_name='Student' #类的父类 class_bases=(object,) #类体 class_body=""" school='dh' def __init__(self,name,age): self.name=name self.age=age def study(self): print('%s is studying' %self.name) """ #类体定义的名字都会存放于类的名称空间中,与exec相似 class_dic={} exec(class_body,{},class_dic) print(class_dic) #结果{'school': 'dh', '__init__': <function __init__ at 0x04AEA2B8>, 'study': <function study at 0x04AEA270>}
    那么调用元类type(也可以自定义)来产生类Student
    '''
    那么调用元类type(也可以自定义)来产生类Student
    '''
    Student=type(class_name,class_bases,class_dic)
    print(Student,type(Student),isinstance(Student,type),Student.school)
    #结果:<class '__main__.Student'> <class 'type'> True dh

    通过上面产生的类过程,我们就可以自定义一个元类,即是写一个类,继承type,复写它的一些方法,如下:

    class Mymeta(type):  # 继承默认元类的一堆属性
        def __init__(self, class_name, class_bases, class_dic):
            if not class_name.istitle():
                raise TypeError('类名首字母必须大写')
    
            super(Mymeta, self).__init__(class_name, class_bases, class_dic)
    class student(object,metaclass=Mymeta):
        school = 'dh'
        def __init__(self, name, age):
            self.name = name
            self.age = age
    
        def study(self):
            print('%s is studying' % self.name)
    
    #则在定义阶段 会报错:TypeError: 类名首字母必须大写

    这样就自定义了一个元类,并控制产生类的条件是类名必须是title。

    当然,我们自定义的元类必须继承type,是为了继承默认元类的一堆属性。

    其调用过程如下:

    class Mymeta(type):  # 继承默认元类的一堆属性
        def __init__(self, class_name, class_bases, class_dic):
            if not class_name.istitle():
                raise TypeError('类名首字母必须大写')
    
            super(Mymeta, self).__init__(class_name, class_bases, class_dic)
    
        def __call__(self, *args, **kwargs):
            # self=Student
            print(self, args, kwargs)  # <class '__main__.Student'> ('alan', 18) {}
            # 1、调用self,即Student下的函数__new__,在该函数内完成:1、产生空对象obj 2、初始化 3、返回obj
            # 若没有,则按属性查找方式往上找
            # 给对象加一些属性,即是调用init方法
            obj = self.__new__(self, *args, **kwargs)
            # 2、一定记得返回obj,因为实例化Student(...)取得就是__call__的返回值
            return obj
    
    
    class Student(object,metaclass=Mymeta):
        school = 'dh'
        def __init__(self, name, age):
            self.name = name
            self.age = age
    
        def study(self):
            print('%s is studying' % self.name)
        def __new__(cls, *args, **kwargs):
    
            obj = object.__new__(cls)
            obj.kkk='dasdas'
            cls.__init__(obj,*args, **kwargs)
            return obj
    obj=Student('alan',18)
    print(obj,vars(obj))#<__main__.Student object at 0x05514850> {'kkk': 'dasdas', 'name': 'alan', 'age': 18}

    这样Mymeta就是我们自定义的元类,其中调用过程,Student(。。。)即是触发Student类的类(即是Mymeta)的__call__方法,而在__call__方法中调用了__new__()方法,创建了一个对象,并为对象加了一些属性,这样类Student的实例化过程就完成。

    如此,我们可以在类的定义阶段对类加一些限制。

    如果觉得不错,动动你们发财的小手,求个订阅!!!







  • 相关阅读:
    图的拓扑排序
    线段树+主席树笔记
    树链剖分笔记
    二分图匹配笔记
    最小生成树笔记
    多个storyboard开发应用程序,封装.bundle和.a不用xib使用storyboard!!!
    Storyboard的使用以及使用多个Storyboard的方法
    IOS源码封装成.bundle和.a文件,以及加入xib的具体方法,翻遍网络,仅此一家完美翻译!! IOS7!!(3) 完美结局
    IOS源码封装成.bundle和.a文件,以及加入xib的具体方法,翻遍网络,仅此一家完美翻译!! IOS7!!(2)
    IOS源码封装成.bundle和.a文件,以及加入xib的具体方法,翻遍网络,仅此一家完美翻译!! IOS7!!(1)
  • 原文地址:https://www.cnblogs.com/wh-alan/p/9283138.html
Copyright © 2011-2022 走看看