元类

什么是元类

1.在Python中一切皆对象,类也是一个对象,实例对象由类创建出来的,类是由元类创建出来的。简而言之,用来创建类的类就叫元类(metaclass)。

2.函数type其实就是一个元类,type就是Python在背后用来创建所有类的元类。(type是内置的一个元类,所有的类都是由type实例化得到)

class Person:  #生成一个简单的类
    def __init__(self, name):
        self.name = name
    def score(self):
        print('haha')
        
p = Person('kang')

a = Person
p1 = a('kang')
print(p1.name)
# 打印结果为 kang

#如何找元类
print(type(p))
<class '__main__.Person'> #这里查看的是p的数据类型
#type类是产生所有类的元类
print(type(Person))
print(type(dict))
print(type(list))
#查看类的数据类型,以下类型均为type
<class 'type'>
<class 'type'>
<class 'type'>

class底层原理

#class 类名  会把类构造出来
#实际上是: 元类实例化产生类 这个对象
#类实例化产生对象, 一定是:  类名()
#比如person类, 是由type实例化产生的, 传入一堆参数
# type() 就是调用类的__init__方法
#手动实现
#1. type()
#type(object_name, bases, dict)
#object_name:类的名字,是个字符串
#bases:是它的所有父类,基类
#dict: 名称空间,是一个字典

#通过type来直接产生类, 而不是用class关键字
l = {}

exec('''
school='oldboy'
def __init__(self,name):
    self.name=name
def score(self):
    print('分数是100')
''',{},l) #这里的{}表示全局名称空间, l表示局部名称空间
Person = type('Person, (object,), l')  #按照上面的格式传值,就能用type来直接产生类
#这就实现了通过type直接来产生Person类
#class 底层就是调用type来实例化产生类

#exec()  eval()的区别
#通过exec可以执行动态Python代码,
#而Python中的eval函数可以计算Python表达式,并返回结果;
#(exec不返回结果,print(eval("…"))打印None);

自定义元类控制类的产生

自定义元类;来控制类的产生:可以控制类名,可以控制类的继承父类,控制类的名称空间
#自定义元类必须继承type, 写一个类继承type 这种类就是元类
class Mymate(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') #判断开头是否是'sb'
         #raise Exception('类名没有以sb开头') 抛错
        #练习二: 类必须加注释
        print(self.__dcit__['__doc__'])
#metaclass = Mymeta 指定这个类生成的时候, 用自己写的Mymeta这个元类

class Person(object, metaclass = Mymeta):
    '''
    注释
    '''
    school = 'oldboy'
    def __init__(self, name):
        self,name = name
    def score(self):
        print('40分')
     
# p = Person()

#练习二: 控制类必须加注释
# class Mymeta(type):
#     def __init__(self,name,bases,dic):
#         print(self.__dict__['__doc__'])
#         doc=self.__dict__['__doc__']
#         if not doc:
#             #没有加注释
#             raise Exception('你的类没有加注释')
# class Person(object,metaclass=Mymeta):
#     '''
#     我加了注释
#     '''
#     school='oldboy'
#     def __init__(self,name):
#         self.name=name
#     def score(self):
#         print('分数是100')

        
        

自定义元类控制类的执行过程

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

#__call__
class Mymeta(type):
    def __call__(self, **args,**kwargs):
        print(self) #self是People
        print(args) #args = {'kang'}
        print(kwargs) #kwargs = {'age':18}
        
        #retuen 123
        # 1.先造出一个People的空对象, 申请内存空间
        # __new__ 方法接受的参数虽然也是和__init__一样,但__init__是在类实例创建之后调用,而 __new__方法正是创建这个类实例的方法。
        obj = self.__new__(self) #虽然和上面的同样是People, 但是People没有, 找到的__new是父类的
        #2. 为该空对象初始化独有的属性
        self.__init__(obj, *args, **kwargs)
        return obj
# People = Mymeta() People()则会触发__call__

calss People(object, metaclass=Mymeta):
    country = 'China'
    
    def __init__(self, name, age):
        self.name = name
        self.age = age
        
     def eat(self):
        print(f'{self.name} is eating')
        
        #再调用Mymeta的__call__的时候, 首先找到自己(如下函数)的, 自己的没有才会找父类的
        def __new__(cls, *args, **kwargs):
            # print(cls) # cls是People
            # cls.__new__(cls) #错误, 无限死循环, 自己找自己的, 会无限的递归下去
            obj = super(People, cls).__new__(cls) # 使用父类的, 则是去父类中找__new__
            return obj
        
        
    
    


  • 类的调用,即类实例化就是元类的调用过程,可以通过元类Mymeta的__call__方法控制
  • 分析:调用Pepole的目的
    1. 先造出一个People的空对象
    2. 为该对空对象初始化独有的属性
    3. 返回一个初始化好的对象

属性查找

查找顺序:

  1. 先对象层:OldoyTeacher->Foo->Bar->object

  2. 然后元类层:Mymeta->type

  3. 类的属性查找顺序:先从类本身中找--->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)


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


o=OldboyTeacher('egon',18) #触发OldboyTeacher的类中的__call__方法的执行,进而执行self.__new__开始查找
print(OldboyTeacher.n)
# print(o.n)


作业

class Mymeta(type):
    def __init__(self, name, bases, dic):
        doc = self.__dict__.get('name')
        if not doc:
            raise Exception('你的名称空间中没有name字段')
        print('存在name字段')

class Person(object,metaclass=Mymeta):

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

    def name(self):
        print('you')
class Mymeta(type):
    def __call__(self, *args, **kwargs):
        self.attr = kwargs
        obj = self.__new__(self,*args,**kwargs)
        return obj


class Mydic(dict, metaclass=Mymeta):
    def __init__(self,**kwargs):
        super().__init__(self, **kwargs)

    def __getitem__(self, item):
        return self[item]

    def __setattr__(self, key, value):
        self[key] = value

dic = Mydic(name='nick', age=18)
print(dic.__dict__)
print(dic.attr)


posted @ 2019-09-04 21:20  Feeling_afraid  阅读(107)  评论(0编辑  收藏  举报