元类及其原理

什么是元类

在python中,一切都是对象,那么类肯定也是一个一个对象

如果类是对象的话,那他一定是由一个实例化得到,这个类就叫做元类。也就是说产生类的类,叫做元类

class Person:
    def __init__(self,name):
        self.name=name
    def score(self):
        print('分数是100')

p=Person('nick')

# a=Person
# p1=a('nick')
# print(p1.name)

#如何找元类?
# print(type(p))
#同理:type类是产生所有类的元类
# print(type(Person))
print(type(dict))
print(type(list))
print(type(str))
print(type(object))

print(type(type))

上述结构都为<class 'type'>,也就是说,在一般情况下,元类为type。type是一个内置的元类,所有的类都是有type实例化得到

class底层原理分析

class+类名,会把类构造出来,但是,实际上则是元类实例化产生类这个对象。

我们也可以用type来直接产生类,而不用class关键字

type() 掉用类的__init__方法
type(object_or_name, bases, dict)
object_or_name:类的名字,是个字符串
bases:是它的所有父类,基类
dict:名称空间,

那么我们就可以定义一个简单的Person类

def __init__(self,name):
    self.name=name
Person=type('Person',(object,),{'school':'oldboy','__init__':__init__})

我们也可以用exec()方法来实现最后字典的输入

l={}
exec('''
school='school'
def __init__(self,name):
    self.name=name
def score(self):
    print('分数是100')
''',{},l)
Person=type('Person',(object,),l)

通过元类来控制类的产生

自定义元类,来控制类的产生,可以控制类名,可以控制类的继承父类,控制类的名称空间自定义元类必须继承type,再由自定义类产生类,这样的自定义类都叫元类

class Mymeta(type):
    # def __init__(self,*args,**kwargs):
    def __init__(self,name,bases,dic):
        # self 就是Person类
        # print(name)
        # print(bases)
        # print(dic)
        #练习一:加限制 控制类名必须以sb开头
        if not name.startswith('sb'):
            raise Exception('类名没有以sb开头')
class sb_Person(object,metaclass=Mymeta):

    school='雄英高中'
    def __init__(self,name):
        self.name=name
    def score(self):
        print('分数是100')

如果上述代码类名不以sb开头,则会抛出'类名没有以sb开头'异常

同样我们也可以办到让类必须添加注释

查询注释的方法为类名._doc_

class Mymeta(type):
    def __init__(self,name,bases,dic):
        print(self.__dict__['__doc__'])
        doc=self.__dict__['__doc__']
        if not doc:
            # 没有加注释
            raise Exception('你的类没有加注释')
#metaclass=Mymeta  指定这个类生成的时候,用自己写的Mymeta这个元类
class Person(object,metaclass=Mymeta):
    '''
    注释
    '''
    school='音乃木坂学院'
    def __init__(self,name):
        self.name=name
    def score(self):
        print('分数是100')

控制类的调用过程

控制类的调用过程,实际上是在控制对象的产生

我们在使用__call__的时候,如果采用对象加()的话,就会调用类里__call__方法

那么如果我们采用类加()的时候呢?很明显我们会调用元类里的__call__方法

我们可以因此而不改变类内的东西,只用元类来使该类里的属性隐藏

class Mymeta(type):
    def __call__(self, *args, **kwargs):
        obj=object.__new__(self)
        # self.__init__(obj,*args, **kwargs)	# 也可以用
        obj.__init__(*args, **kwargs)
        # print(obj.__dict__)
        obj.__dict__={ '_%s__%s'%(self.__name__,k):v for k,v in obj.__dict__.items()}
        # print(obj.__dict__)
        return obj

class Person(object, metaclass=Mymeta):
    school = 'oldboy'
    def __init__(self, name):
        self.name = name
    def score(self):
        print('分数是100')
p = Person(name='nick')
print(p.__dict__)
# print(p.name)	# 会报错,因为已经被隐藏

有了元类之后的属性查找顺序

类的属性查找顺序:先从类本身中找--->mro继承关系去父类中找---->去自己定义的元类中找--->type中--->报错
对象的属性查找顺序:先从对象自身找--->类中找--->mro继承关系去父类中找--->报错

class Mymeta(type):
    n=444

    def __call__(self, *args, **kwargs): #self=<class '__main__.OldboyTeacher'>
        obj=self.__new__(self)
        # print(self.__new__ is object.__new__) #True
        obj.__init__(*args, **kwargs)
        return obj


class Bar(object):
    # n=333
    pass

    # def __new__(cls, *args, **kwargs):
    #     print('Bar.__new__')

class Foo(Bar):
    # n=222
    pass

    # def __new__(cls, *args, **kwargs):
    #     print('Foo.__new__')

class OldboyTeacher(Foo,metaclass=Mymeta):
    # n=111

    school='oldboy'

    def __init__(self,name,age):
        self.name=name
        self.age=age

    def say(self):
        print('%s says welcome to the oldboy to learn Python' %self.name)
posted @ 2019-09-03 17:31  黑井白子  阅读(142)  评论(0编辑  收藏  举报
Live2D