欢迎您访问程序员文章站本站旨在为大家提供分享程序员计算机编程知识!
您现在的位置是: 首页  >  IT编程

初识python元类(metaclass)

程序员文章站 2022-10-20 08:26:44
听过一句话,在python中一切皆对象,而在python3中统一了类和类型。 我们可以用type方法查看一个变量(对象)的类型,即是查看对象的类: 通过上面产生的类过程,我们就可以自定义一个元类,即是写一个类,继承type,复写它的一些方法,如下: 这样就自定义了一个元类,并控制产生类的条件是类名必 ......

听过一句话,在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>}
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的实例化过程就完成。

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

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