python中的元类

什么是元类

所有的对象都是调用类(实例化)而得来的,调用类的过程叫做类的实例化。

如果一切皆对象,那么类也是一个对象!既然对象都是通过调用类得到的,那么,调用类A得到了一个对象类B,那么类A就是元类!牛逼!

class A:
    pass

a = A()   #调用A得到一个对象a
'''
根据一切皆对象,类A也是一个对象,那么类A从哪来?
A = 某个类()  某个类的实例化为A
那么这个某个类,就叫元类!
'''
print(type(a))  # <class '__main__.A'>
print(type(A))  # <class 'type'>
print(type(type)) # <class 'type'>  元类type的元类为元类type
#打印对象A的类,得出:A的元类为 type 类

元类type——>实例化——>类A——>实例化——>对象a

一个类有三大组成部分:

  • 类名 class_name :A
  • 基类们 class_bases :(object, ) 父类们
  • 类的名称空间 class_dict :执行类体代码得到的

class 这个关键字帮我们做了什么?

  1. 拿到类名 class_name = A
  2. 拿到类的基类 class_bases = (object, )
  3. 执行类体代码,拿到类的名称空间 class_dict =
  4. 调用元类得到类 type(class_name, class_bases, class_dict)

创建类的三要素:类名,基类,类的名称空间

用type创建类:type(class_name, class_bases, class_dict)

自定义元类
class MyType(type):  #自定义元类,继承type的类

    def __init__(self, class_name, class_bases, class_dict):  #类的三要素:类名,基类,名称空间
        super(MyType, self).__init__(class_name, class_bases, class_dict)
        print('self:', self)  #self: <class '__main__.People'>
        print(class_name)  # People
        print(class_bases)  # (<class 'object'>,)
        print(class_dict)  #{'__module__': '__main__', '__qualname__': 'People', '__init__': <function People.__init__ at 0x10c29fb00>, 'fun': <function People.fun at 0x10c358320>}



class People(object, metaclass=MyType):  #等同于:People = type('People', class_bases, class_dict)

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

    def fun(self):
        print('我是一个函数')

# 类在定义的时候就执行了,所以People不用实例化,就能得出自定义元类中的结果
元类的应用:

元类中可以自定义类的产生过程。类的产生过程其实就是元类的调用过程

# 例:实现创建类时必须有注释,类名必须大写,否则创建类会报错。
class MyType(type):
    def __init__(self, class_name, class_bases, class_dict):
        super(MyType, self).__init__(class_name, class_bases, class_dict)

        if not class_name.istitle():
            raise TypeError('类名首字母需要大写')

        if not class_dict.get('__doc__') or not len(class_dict.get('__doc__').strip()):
            raise TypeError('类中必须要有注释,养成一个好习惯好吗弟弟?')


class People(object, metaclass=MyType):

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

    def fun(self):
        print('老子是个函数')

__ call __

要想让类的对象变成可调用的,必须要实现 __ call __ 方法。

class TestCall:

    def __call__(self, *args, **kwargs):
        print('args:',args)
        print('kwargs:',kwargs)


t = TestCall()
t(1,x=2)
# args: (1,)
# kwargs: {'x': 2}

#说明,类也是一个对象,所以在类的类中,也就是元类中肯定也实现了 __call__ 方法

分析一波类的调用:

类() 表示类的调用,要想让 类后面加个() 能用,这时候需要把类当做一个对象,这个对象的类中必然实现了 __ call __,我们知道,类的类时元类,所以元类中肯定实现了call方法,然后这个call返回了一个对象,元类的对象是类,类的对象就是 元类中 call 返回的对象。

__ new __:

我们知道,类在实例化的时候,会第一个调用执行类里面 __ init __ 里面的代码。但是!转折来了,类在实例化的时候,第一个调用的并不是 __ init __ 的方法,而是 __ new __,这个方法会返回一个空对象,然后再调用 init 初始化,将类实例化时后面跟的参数添加进 这个空对象 的名称空间中。

__ new __ 后面跟的参数跟 __ init __ 一样。

__ init __ 是在类实例(对象)创建之后调用的,而 __ new __ 这个方法,正是产生类实例(对象)的方法

注意:__ new __ 只能用于新式类(从object继承的类)

class People():

    def __new__(cls, *args, **kwargs):
        print('new_args', args)  # ('egon', 19) 这个是类实例化的时候后面跟的参数
        print('new_kwargs', kwargs)
        return cls.__new__(cls)  #自己找自己的 __ new __,会无限循环。

    def __init__(self, name, age):
        print('name', name)
        print('age', age)


p = People('egon', 19)

#说明:这个类实例化时,第一步会先调用 __ new __ 方法 ,生成一个空对象,在People类中有一个 new ,这时候创建空对象时,会一直调用自己的 __ new __ 方法,陷入无限循环中。
利用元类自定义类的实例化

自定义类的实例化过程,本质上就是重写元类的 __ call __ 方法。因为类实例化,就是在调用元类。

class MyType(type):
    def __call__(self, *args, **kwargs):

        print('self:', self) # self: <class '__main__.People'>
        print('**args:', args) # **args: ('KbMan', 12)
        print('**kwargs:', kwargs) # **kwargs: {}

        obj = self.__new__(self) # 1、先创建一个空对象,这个self是类 People
        self.__init__(obj, *args, **kwargs) # 2、初始化空对象的属性
        return obj # 3、返回初始化之后的对象


class People(object, metaclass=MyType):
    def __init__(self, name, age):
        self.name = name
        self.age = age


p = People('KbMan', 12)  # 类实例化时,因为类的实例化就是调用元类的过程,所以会调用元类的 __call__方法
print(p.__dict__) # {'name': 'KbMan', 'age': 12}

类的调用即类的实例化,就是元类的调用过程,可以通过自定义元类 MyType 的 __ call __ 控制调用过程。

类实例化分析:

1、类实例化实际上就是调用元类的过程,就是调用元类中 __ call __ 方法的过程

2、利用 __ new __ 生成一个People的空对象

3、为该对象调用 __ init __ 初始化属性值

4、返回该对象

自定义元类后的继承顺序
class MyType(type):

    s = 'MyType元类'


class B:
    # s = 'B类'
    def __init__(self):
        super().__init__()

class A(B):
    # s = 'A类'
    def __init__(self):
        super().__init__()

class People(A, metaclass=MyType):
    # s = 'People类'

    def __init__(self):
        super().__init__()

print(People.s)  # 查找顺序为 People类 ——> A类 ——> B类 ——> MyType类

查找顺序:

  1. 先对象层:People->A->B->object
  2. 然后元类层:MyType->type
自定义元类后 __ new __ 的查找顺序
class MyType(type):

    s = 'MyType元类'
    def __call__(self, *args, **kwargs):

        obj = self.__new__(self)
        print(self.__new__ is object.__new__)


class B:
    # s = 'B类'
    def __init__(self):
        super().__init__()

    # def __new__(cls, *args, **kwargs):
    #     print('B类的__new__')


class A(B):
    # s = 'A类'
    def __init__(self):
        super().__init__()

    # def __new__(cls, *args, **kwargs):
    #     print('A类的__new__')


class People(A, metaclass=MyType):
    # s = 'People类'

    def __init__(self):
        super().__init__()

    # def __new__(cls, *args, **kwargs):
    #     print('People的__new__')


People() # __new__ 的 查找顺序为 People类 ——> A类 ——> B类 ——>object#
#注意:object类中自带 __new__,所以查到object类 肯定会结束
利用元类修改类的属性为私有属性(隐藏属性)
# 要修改的属性为类实例化出的对象的属性。

class MyType(type):
    def __init__(self, class_name, class_bases, class_dict):
        super().__init__(class_name, class_bases, class_dict)

    # 在类实例化时修改对象属性为隐藏属性
    def __call__(self, *args, **kwargs):
        # 1、创建一个空对象
        obj = self.__new__(self)
        # 2、调用 __init__ 初始化对象属性
        self.__init__(obj, *args, **kwargs)
        # 3、修改对象的__dict__
        obj.__dict__ = { '_{0}__{1}'.format(self.__name__, k) : v  for k, v in obj.__dict__.items()}
        return obj


class People(object, metaclass=MyType):
    def __init__(self, name, age):
        self.name = name
        self.age = age


p = People('KbMan',123)
print(p.__dict__)  # {'_People__name': 'KbMan', '_People__age': 123}
posted @ 2019-08-02 17:41  KbMan  阅读(153)  评论(0编辑  收藏  举报