初识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的实例化过程就完成。

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

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







posted @ 2018-07-09 11:55  孤月灵声  阅读(245)  评论(0编辑  收藏  举报