• 周五. 10月 7th, 2022

5G编程聚合网

5G时代下一个聚合的编程学习网

热门标签

元类

admin

11月 28, 2021

元类

一、元类的定义

python中一切皆对象,类也是一个对象,那么,创建类的是什么呢?

class Person:
    pass
p=Person()

Person类也是一个对象,那它一定也是由一个类实例化得到的,那么这个类就叫元类

type是内置的一个元类,所有的类都是由type实例化得到的。

总结:元类就是产生类的类!

二、class底层原理分析

class 类名 : class关键字会把类构造出来。

实际上,内部是由元类(type)实例化产生这个类的。

怎么产生的呢,就是用type()传入一堆参数,实现的。

语法

type()
type(object_or_name, bases, dict)
    object_or_name:类的名字,是个字符串
    bases:是它的所有父类,基类
    dict:名称空间,是一个字典

通过type()创建一个类,不适用class关键字。

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

p=Person('nick')
print(p.name)        # nick
print(p.__dict__)    # {'name': 'nick'}

exec()

exec 执行储存在字符串或文件中的 Python 语句,相比于 eval,exec可以执行更复杂的 Python 代码。

exec(object[, globals[, locals]])
  • object:必选参数,表示需要被指定的Python代码。它必须是字符串或code对象。如果object是一个字符串,该字符串会先被解析为一组Python语句,然后在执行(除非发生语法错误)。如果object是一个code对象,那么它只是被简单的执行。
  • globals:可选参数,表示全局命名空间(存放全局变量),如果被提供,则必须是一个字典对象。
  • locals:可选参数,表示当前局部命名空间(存放局部变量),如果被提供,可以是任何映射对象。如果该参数被忽略,那么它将会取与globals相同的值。

返回值:exec 返回值永远为 None。

通过元类来控制类的产生

自定义元类来控制类的产生,可以控制类名,类的继承关系以及类的名称空间!

type:自定义元类必须继承type,继承了type的类都叫元类。

class Mymeta(type):
    def __init__(self,name,bases,dic):
        # 此时的self为Person这个类(Person类也是对象)
        print(name)
        print(bases)
        print(dic)
        
# metaclass=Mymeta  指定这个类生成的时候,用自己写的Mymeta这个元类
class Person(object,metaclass = Mymeta):
    school='oldboy'
    def __init__(self,name):
        self.name=name
    def score(self):
        print('分数是100')
p = Person('nick')
'''
Person
(<class 'object'>,)
{'__module__': '__main__', '__qualname__': 'Person', '__doc__': '
    注释
    ', 'school': 'oldboy', '__init__': <function Person.__init__ at 0x03C0B4B0>, 'score': <function Person.score at 0x03C0B4F8>}
'''

练习:控制类名必须全大写,控制类必须加注释!

class Mymeta(type):
    def __init__(self,name,bases,dic):
        # 此时的self为Person这个类(Person类也是对象)
        if not name.isupper():
       		raise Exception('类名必须全大写 。')
        doc = self.__dict__['__doc__']
    	if not doc:
            raise Exception('你的类没有加注释')
        
# metaclass=Mymeta  指定这个类生成的时候,用自己写的Mymeta这个元类
class Person(object,metaclass = Mymeta):
    school='oldboy'
    def __init__(self,name):
        self.name=name
    def score(self):
        print('分数是100')
p = Person('nick')

通过元类控制类的调用过程

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

class Mymeta(type):
    # Person('nick') 自动触发init,先触发元类的__call__
    def __call__(self, *args, **kwargs):
        return self.__dict__  # 返回的是People类的对象p,无法点出属性/方法。

class Person(object,metaclass=Mymeta):
    school='oldboy'
    def __init__(self,name):
        self.name=name
    def score(self):
        print('分数是100')

p=Person('nick')
print(p)

练习:把对象中的所有属性设置成私有的

class Mymeta(type):
    def __call__(self, *args, **kwargs):
        # 实例化产生一个Person类的对象,借助__new__来产生,需要把类传过去,才能产生对象
        obj=object.__new__(self)  # obj 是Person类的对象,只不过是空的
        # 对象调用__init__方法完成初始化
        obj.__init__(*args, **kwargs)
		# 修改对象属性为私有属性(拼接_*__*)
        obj.__dict__={ '_%s__%s'%(self.__name__,k):v for k,v in     			                            obj.__dict__.items()}

        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__)  # {'_Person__name': 'nick'}
print(p.name)	   # AttributeError: 'Person' object has no attribute 'name'

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

类的属性查找顺序:类自身–>按mro继承关系顺序去父类找–>自定义元类中找–>type–>报错

对象的查找顺序:先从对象自身找—>类中找—>mro继承关系去父类中找—>报错

发表回复

您的电子邮箱地址不会被公开。