元类

一、元类

1.1 什么是元类

  1. 在python中一切皆对象,类实际上就是一个一个的对象。

  2. 我们用class关键字定义的类本身也是一个对象,负责产生该对象的类称之为元类(元类可以简称为类的类)

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

  4. 如下,Person类也是一个对象,那么他一定是由一个类的实例化得到的,这个类就叫做元类。

    class Person:
        def __init__(self,name):
            self.name = name
        def score(self):
            print('分数是100')
    a = Person  # 类也是一等公民
    p = a('ocean')
    print(p.name)
    

1.2 如何找元类

#利用print进行打印出
#type类是产生所有类的元类
print(type(p))#<class '__main__.Person'>
print(type(Person))#<class 'type'>
print(type(dict))#<class 'type'>
print(type(list))#<class 'type'>
print(type(str))#<class 'type'>
print(type(type))#<class 'type'>

二、class底层原理分析

  1. class 类名,会把类构造出来。实际上实例化产生类,也是一个对象

  2. 类实例化产生对象,一定是:类名()

  3. Person类是由type实例化产生,传入一堆参数

    class Person:
        def __init__(self,name):
            self.name = name
        def score(self):
            print('分数是100')
    a = Person  # 类也是一等公民
    p = a('ocean')
    print(p.name)
  4. type(object_or_name, bases, dict)

    1. object_or_name,类的名字,是一个字符串
    2. bases:是所有的父类(基类)
    3. dict:名称空间是一个字典
  5. class底层就是调用type来实例化产生类(对象)

2.1 通过type来直接生成类

可以不用class关键字

type(object_or_name, bases, dict)

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

方法一

Person = type('Person',(object,),
              {'school': 'hnsf'})
print(Person.__dict__)
#{'school': 'hnsf', '__module__': '__main__', '__dict__': <attribute '__dict__' of 'Person' objects>, '__weakref__': <attribute '__weakref__' of 'Person' objects>, '__doc__': None}

方法二

exec("""
school = 'hnsf'
def __init__(self, name):
    self.name = name
def score(self):
    print('分数100分')
""",{},l)

Person = type('person',(object,),l)
print(Person.__dict__)
#{'school': 'hnsf', '__init__': <function __init__ at 0x000001C0FFD17318>, 'score': <function score at 0x000001C0FFD17168>, '__module__': '__main__', '__dict__': <attribute '__dict__' of 'person' objects>, '__weakref__': <attribute '__weakref__' of 'person' objects>, '__doc__': None}

法三

def __init__(self,name):#魔法方法要先写好
    self.name = name
Person = type('Person',(object,),
              {'school': 'hnsf', '__init__':__init__})
print(Person.__dict__)
#{'school': 'hnsf', '__init__': <function __init__ at 0x00000226235673A8>, '__module__': '__main__', '__dict__': <attribute '__dict__' of 'Person' objects>, '__weakref__': <attribute '__weakref__' of 'Person' objects>, '__doc__': None}
print(Person.__bases__)#(<class 'object'>,)

三、通过元类来控制类的产生

3.1自定义元类

自定义元类来控制类的产生,可以控制类名,可以控制类的继承父类

控制类的名称空间

自定义类必须继承type,写一个类继承type,这种类都叫元类

class Mymeta(type):# 产生的类作为一个对象,传给元类
    def __init__(self,*args,**kwargs):
        print(*args)#Person () {'__module__': '__main__', '__qualname__': 'Person', 'school': 'hnsf', '__init__': <function Person.__init__ at 0x000001C81E7D7558>, 'score': <function Person.score at 0x000001C81E7D75E8>}

        print(**kwargs)

metaclass = Mymeta#指定这个类生成的时候,用自己写的Mymeta这个元类
class Person(metaclass=Mymeta):
    school = 'hnsf'
    def __init__(self,name):
        self.name = name
    def score(self):
        print('ok')
class Mymeta(type):
    def __init__(self, name, bases, dic):
        print(self)
        # print(name)# 类名,Person
        print(self.__name__)  # 类名,Person
        # print(bases)# 类的父类,(<class 'object'>,)
        print(self.__bases__)  # 类的父类,(<class 'object'>,)
        # print(dic)
        print(self.__dict__)  # 类的名称空间,{'__module__': '__main__', '__qualname__': 'Person', 'school': 'hnsf', '__init__': <function Person.__init__ at 0x000002D6F6337558>, 'score': <function Person.score at 0x000002D6F63375E8>}

metaclass = Mymeta  # 指定这个类生成的时候,用自己写的Mymeta这个元类
class Person(object, metaclass=Mymeta):
    school = 'hnsf'
    def __init__(self, name):
        self.name = name
    def score(self):
        print('ok')

练习一:

对产生的类进行限制,控制类必须以sb开头

class Mymeta(type):
    def __init__(self, name, bases, dic):
        if not name.startswith('sb'):
            raise Exception('类名没有以sb开口')

metaclass = Mymeta  # 指定这个类生成的时候,用自己写的Mymeta这个元类
class sbPerson(object, metaclass=Mymeta):
    school = 'hnsf'
    def __init__(self, name):
        self.name = name
    def score(self):
        print('ok')

练习二,类必须加注释

类的名称空间中的key值'__doc__'代表注释

class Mymeta(type):
    def __init__(self, name, bases, dic):
        print(self.__dict__["__doc__"])

metaclass = Mymeta  # 指定这个类生成的时候,用自己写的Mymeta这个元类
class sbPerson(object, metaclass=Mymeta):
    """
    我已经注释
    """
    school = 'hnsf'
    def __init__(self, name):
        self.name = name
    def score(self):
        print('ok')
class Mymeta(type):
    def __init__(self, name, bases, dic):
        doc = self.__dict__["__doc__"]
        if not doc:
            raise Exception('你的类没有加注释')
        else:
            print(doc)

metaclass = Mymeta  # 指定这个类生成的时候,用自己写的Mymeta这个元类
class sbPerson(object, metaclass=Mymeta):
    """
    我已经注释
    """
    school = 'hnsf'
    def __init__(self, name):
        self.name = name
    def score(self):
        print('ok')



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

  • 要想让obj这个对象变成一个可调用的对象,需要在该对象的类中定义一个方法__call__方法,该方法会在调用对象时自动触发

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

    class Mymeta(type):
        def __call__(self, *args, **kwargs):
            print('ocean')
            return 95
    
    class Person(object,metaclass=Mymeta):
        school = 'nasf'
        def __init__(self,name):
            self.name = name
        def score(self):
            print('100')
    
    p = Person('chen')#ocean
    print(p)#ocean 95
    class Person():
        school = 'nasf'
        def __init__(self,name):
            self.name = name
        def score(self):
            print('100')
        def __call__(self, *args, **kwargs):
            print('ocean')
    p = Person('chen')#自动触发__init__的执行
    
    class Person():
        school = 'nasf'
        def __init__(self,name):
            self.name = name
        def score(self):
            print('100')
        def __call__(self, *args, **kwargs):
            print('ocean111')
    p = Person('chen')
    p()# 当元类没有自定义__call__的时候,而且自身定义了__call__的时候,那么对象加()会触发
    #先触发元类的__call__
    # 当自身和自定义的元类都写了__call__方法的时候,先触发元类的__call__,并且不会生成对象
    
    class Mymeta(type):
        def __call__(self, *args, **kwargs):
            print('ocean')
            # return 95
    #
    #
    # p = Person('chen')#ocean
    # print(p)#ocean 95
    class Person(metaclass=Mymeta):
        school = 'nasf'
        def __init__(self,name):
            self.name = name
        def score(self):
            print('100')
        def __call__(self, *args, **kwargs):
            print('ocean111')
    p = Person('chen')#不会生成p对象
    

    练习:给我吧对象中的所有属性都设置成私有的

    分析:

    class Mymeta(type):
        def __call__(self, *args, **kwargs):
            #实例化产生一个Person类的对象,借助__new__来产生,
            # 需要把类传过去,才能产生对象,只不过是空的
            obj = object.__new__(self)
    
    class Person(object,metaclass=Mymeta):
        school = 'hnsf'
        def __init__(self,name):
            self.name = name
    p = Person('name')
    print(p)#None
    class Mymeta(type):
        def __call__(self, *args, **kwargs):
            #实例化产生一个Person类的对象,借助__new__来产生,
            # 需要把类传过去,才能产生对象,只不过是空的
            obj = object.__new__(self)
            #调用__init__方法完成初始化
            #类来调用__init__方法,就是一个普通函数,有几个参数就传几个参数
            # self.__init__(obj,*args,**kwargs)
            #对象来调用__init__方法,对象的绑定方法,会把自身传过去
            obj.__init__(*args,**kwargs)
            print(obj)
            return obj
    
    class Person(object,metaclass=Mymeta):
        school = 'hnsf'
        def __init__(self,name):
            self.name = name
    p = Person('name')
    print(p)
    print(p.name)

    把对象的所有的属性变为私有

    class Mymeta(type):
        def __call__(self, *args, **kwargs):
            obj = object.__new__(self)
            obj.__init__(*args,**kwargs)
            print(obj.__dict__)#{'name': 'ocean'}
    
            obj.__dict__ ={'_%s__%s'%(self.__name__,k):v for k,v in obj.__dict__.items()}
            print(obj.__dict__)#{'_Person__name': 'ocean'}
    
            return obj
    
    
    class Person(object,metaclass=Mymeta):
        school = 'hnsf'
        def __init__(self,name):
            self.name = name
    
    p = Person(name='ocean')
    
    print(p.__dict__)#{'_Person__name': 'ocean'}
    

    五、有了元类之后的属性查找

    1. 类的属性查找顺序:先从类本身中找--->mro继承关系去父类中找---->去自己定义的元类中找--->type中--->报错
    2. 对象的属性查找顺序:先从对象自身找--->类中找--->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)

转载于:https://www.cnblogs.com/SkyOceanchen/p/11454652.html

  • 0
    点赞
  • 1
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值