面向对象高阶-14元类

一丶什么是元类

  • 在python中一切皆对象,那么咱们用class关键字定义的类自己也是一个对象,负责产生该对象的类称之为元类,即元类能够简称为类的类
class Foo:  # Foo=元类()
    pass

二丶为何用元类

元类是负责产生类的,因此咱们学习元类或者自定义元类的目的:是为了控制类的产生过程,还能够控制对象的产生过程

三丶内置函数exec

cmd = """
x=1
print('exec函数运行了')
def func(self):
    pass
"""
class_dic = {}
# 执行cmd中的代码,而后把产生的名字丢入class_dic字典中
exec(cmd, {}, class_dic)

exec函数运行了python

print(class_dic)

{'x': 1, 'func': <function func at 0x10a0bc048>}函数

四丶class建立类

  • 若是说类也是对象,那么用class关键字的去建立类的过程也是一个实例化的过程,该实例化的目的是为了获得一个类,调用的是元类
  • 用class关键字建立一个类,用的默认的元类type,所以之前说不要用type做为类别判断
class People:  # People=type(...)
    country = 'China'

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

    def eat(self):
        print('%s is eating' % self.name)
        
print(type(People)

<class 'type'>学习

4.1type实现

  • 建立类的3个要素:类名,基类,类的名称空间
  • People = type(类名,基类,类的名称空间)
class_name = 'People'  # 类名

class_bases = (object, )  # 基类

# 类的名称空间
class_dic = {}
class_body = """
country='China'
def __init__(self,name,age):
    self.name=name
    self.age=age
def eat(self):
    print('%s is eating' %self.name)
"""

exec(
    class_body,
    {},
    class_dic,
)
print(class_name)

Peoplecode

print(class_bases)

(<class 'object'>,)对象

print(class_dic)  # 类的名称空间

{'country': 'China', '__init__': <function __init__ at 0x10a0bc048>, 'eat': <function eat at 0x10a0bcd08>}继承

  • People = type(类名,基类,类的名称空间)
People1 = type(class_name, class_bases, class_dic)
print(People1)

<class '__main__.People'>递归

obj1 = People1(1, 2)
obj1.eat()

1 is eatingip

  • class建立的类的调用
print(People)

<class '__main__.People'>内存

obj = People1(1, 2)
obj.eat()

1 is eating文档

五丶自定义元类控制类的建立

  • 使用自定义的元类
class Mymeta(type):  # 只有继承了type类才能称之为一个元类,不然就是一个普通的自定义类
    def __init__(self, class_name, class_bases, class_dic):
        print('self:', self)  # 如今是People
        print('class_name:', class_name)
        print('class_bases:', class_bases)
        print('class_dic:', class_dic)
        super(Mymeta, self).__init__(class_name, class_bases,
                                     class_dic)  # 重用父类type的功能

分析用class自定义类的运行原理(而非元类的的运行原理):

  1. 拿到一个字符串格式的类名class_name='People'
  2. 拿到一个类的基类们class_bases=(obejct,)
  3. 执行类体代码,拿到一个类的名称空间class_dic={...}
  4. 调用People=type(class_name,class_bases,class_dic)
class People(object, metaclass=Mymeta):  # People=Mymeta(类名,基类们,类的名称空间)
    country = 'China'

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

    def eat(self):
        print('%s is eating' % self.name)

self: <class '__main__.People'> class_name: People class_bases: (<class 'object'>,) class_dic: {'__module__': '__main__', '__qualname__': 'People', 'country': 'China', '__init__': <function People.__init__ at 0x10a0bcbf8>, 'eat': <function People.eat at 0x10a0bc2f0>}

5.1应用

  • 自定义元类控制类的产生过程,类的产生过程其实就是元类的调用过程
  • 咱们能够控制类必须有文档,可使用以下的方式实现
class Mymeta(type):  # 只有继承了type类才能称之为一个元类,不然就是一个普通的自定义类
    def __init__(self, class_name, class_bases, class_dic):
        if class_dic.get('__doc__') is None or len(
                class_dic.get('__doc__').strip()) == 0:
            raise TypeError('类中必须有文档注释,而且文档注释不能为空')
        if not class_name.istitle():
            raise TypeError('类名首字母必须大写')
        super(Mymeta, self).__init__(class_name, class_bases,
                                     class_dic)  # 重用父类的功能
try:

    class People(object, metaclass=Mymeta
                 ):  #People  = Mymeta('People',(object,),{....})
        #     """这是People类"""
        country = 'China'

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

        def eat(self):
            print('%s is eating' % self.name)
except Exception as e:
    print(e)

类中必须有文档注释,而且文档注释不能为空

六丶call

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

    class Foo:
        def __call__(self, *args, **kwargs):
            print(args)
            print(kwargs)
            print('__call__实现了,实例化对象能够加括号调用了')
    
    
    obj = Foo()
    obj('nash', age=18)

('nash',)
{'age': 18}
__call__实现了,实例化对象能够加括号调用了

七丶new

咱们以前说类实例化第一个调用的是__init__,但__init__其实不是实例化一个类的时候第一个被调用 的方法。当使用 Persion(name, age) 这样的表达式来实例化一个类时,最早被调用的方法 实际上是 new 方法。

__new__方法接受的参数虽然也是和__init__同样,但__init__是在类实例建立以后调用,而 __new__方法正是建立这个类实例的方法。

注意:new() 函数只能用于从object继承的新式类。建立类能够理解为init+new

class A:
    pass


class B(A):
    def __new__(cls):
        print("__new__方法被执行")
        return cls.__new__(cls)

    def __init__(self):
        print("__init__方法被执行")


b = B()

八丶自定义元类控制类的实例化

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

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

    def eat(self):
        print('%s is eating' % self.name)


#     在调用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. 返回一个初始化好的对象
    obj = People('nash', age=18)

<class '__main__.People'> ('nash',) {'age': 18}

print(obj.__dict__)

{'name': 'nash', 'age': 18}

九丶自定义元类后累的继承顺序

其实咱们用class自定义的类也全都是对象(包括object类自己也是元类type的 一个实例,能够用type(object)查看),根据继承的实现原理,若是把类当成对象去看,将下述继承应该说成是:对象OldboyTeacher继承对象Foo,对象Foo继承对象Bar,对象Bar继承对象object

class Mymeta(type):  # 只有继承了type类才能称之为一个元类,不然就是一个普通的自定义类
    n = 444

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


class Bar(object):
    n = 333


class Foo(Bar):
    n = 222


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)


print(
    OldboyTeacher.n
)  # 自下而上依次注释各个类中的n=xxx,而后从新运行程序,发现n的查找顺序为OldboyTeacher->Foo->Bar->object->Mymeta->type

111

print(OldboyTeacher.n)

111

  • 查找顺序:
    1. 先对象层:OldoyTeacher->Foo->Bar->object
    2. 而后元类层:Mymeta->type

依据上述总结,咱们来分析下元类Mymeta中__call__里的self.__new__的查找

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


class Bar(object):
    n = 333

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


class Foo(Bar):
    n = 222

    # 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__')


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

总结,Mymeta下的__call__里的self.__new__在OldboyTeacher、Foo、Bar里都没有找到__new__的状况下,会去找object里的__new__,而object下默认就有一个__new__,因此即使是以前的类均未实现__new__,也必定会在object中找到一个,根本不会、也根本不必再去找元类Mymeta->type中查找__new__

相关文章
相关标签/搜索