面向对象之元类

面向对象之元类

一、什么是元类

  • 产生已知类的类就叫做元类,type
class Person(object):
    ...


def func():
    ...


people = Person()
# 产生对象的是类
print(type(people))  # <class '__main__.Person'>
print(type(func))  # <class 'function'>
# 产生类的就是元类
print(type(object))  # <class 'type'>
print(type(Person))  # <class 'type'>
  • 通过查看每一种数据的数据类型,我们会发现都共同拥有一个类
  • 这个类就是 type 我们也称之为元类

二、产生类的两种方法

[1]使用关键字声明

  • 这是我们最常用的
  • 语法
    • class 类名(继承的父类)
class Person(object):
    ...


print(Person)  # <class '__main__.Person'>
print(type(Person))  # <class 'type'>

[2]通过type创建

  • 语法
    • 类名 = type('类的名字',(类继承的父类,使用元组),{当前类内置的数据属性和函数属性})
Person = type('Person', (object,), {'name': 'Xanadu', 'run': run})

people = Person()
print(Person)  # <class '__main__.Person'>
print(Person.__dict__)
# 在创建类时添加的函数数据与使用关键字定义的类内定义的
# {'name': 'Xanadu', 'run': <function run at 0x000002B17C9628C0>, ....'__doc__': None}
print(people)  # <__main__.Person object at 0x000001846987B760>
print(people.name)  # Xanadu
print(people.__dir__)  # ['__class__', .....'__weakref__', 'name']

[3]为什么要使用元类

  • 元类可以控制类的创建,也就意味着我们可以高度定制类的具体行为
    • 比如,当我们掌控了食品的生产过程,我们就可以在里面随便动手脚

[4]元类的基本使用

  • 尝试实现功能:让所有类名都是大写

  • 先要知道怎么样产生的类,再通过定义类名是进行修改

class MyType(type):
    # 其实我们自建的这个元类只是一个从type里派生方法的中间平台
    def __init__(cls, class_name, class_bases, class_dict):
        print('这是 MyType 里的 init 方法')
        # 在使用我们的元类定义类时就调用了这个函数,先于call方法
        # 这里使用type里的__init__来创建一个空的类,方便创建类时进行类名,继承父类,和名称空间的定义
        super().__init__(class_name, class_bases, class_dict)

    def __call__(cls, *args, **kwargs):
        # 在使用类名加括号是会触发这个方法,这个实现实例化的最核心的方法
        # 其产生的obj就是我们实例化所得到的对象
        # 这个使用type里的__call__方法产生了一个对象并返回了出来
        obj = super().__call__(*args, **kwargs)
        print('这是 MyType 里的 call 方法')
        # 所以我们也要返回出去
        return obj


# 通过上面我们自己定义的元类来产生类
# 这个过程与继承相似又不同

class MyClass(object, metaclass=MyType):
    def __init__(self):
        print('这是MyClass里的init方法')

    # 对象加括号调用了这个方法
    def __call__(self, *args, **kwargs):
        print('这是MyClass里面的call方法')


stu = MyClass()
stu()
# 这是 MyType 里的 init 方法
# 这是MyClass里的init方法
# 这是 MyType 里的 call 方法
# 这是MyClass里面的call方法
  • 使用关键字创建类时在类中直接定义的方法与使用type创建类时在名称空间中直接添加的方法(函数)是不同的
class Person1(object):
    def run_1(self):
        ...

    @classmethod
    def run_2(cls):
        ...

    @staticmethod
    def run_3():
        ...


def run_4():
    ...


Person2 = type("Person", (object,), {'run': run})

print(Person1.__dict__)
# {'__module__': '__main__', 
# 'run_1': <function Person1.run_1 at 0x000002048B8E36D0>,
# 'run_2': <classmethod(<function Person1.run_2 at 0x000002048BB51AB0>)>, 
# 'run_3': <staticmethod(<function Person1.run_3 at 0x000002048BB51B40>)>,
# ...'__doc__': None}
print(Person2.__dict__)
# {'run': <function run at 0x000002048B3E3EB0>,
# ...'__doc__': None}

[5]自定义类

  • 尝试实现功能:让所有类名都是大写
# 会自动更正,将类名转化成大写
class MyType(type):
    def __init__(cls, class_name, class_bases, class_dict):
        if not class_name.isupper():
            # raise TypeError('请输入全大写类名')
            class_name.upper()
        else:
            ...
        super().__init__(class_name, class_bases, class_dict)

    def __call__(cls, *args, **kwargs):
        obj = super().__call__(*args, **kwargs)
        return obj


class MyClass(object, metaclass=MyType):
    def __init__(self):
        ...

    # 对象加括号调用了这个方法
    def __call__(self, *args, **kwargs):
        ...


stu = MyClass()
# 查看类名已经转换大写
print(type(stu))  # <class '__main__.MyClass'>




# 强制输入群大写类名,不然报错
class MyType(type):
    def __init__(cls, class_name, class_bases, class_dict):
        if not class_name.isupper():
            raise TypeError('请输入全大写类名')
            # class_name.upper()
        else:
            ...
        super().__init__(class_name, class_bases, class_dict)

    def __call__(cls, *args, **kwargs):
        obj = super().__call__(*args, **kwargs)
        return obj


class MyClass(object, metaclass=MyType):
    def __init__(self):
        ...

    # 对象加括号调用了这个方法
    def __call__(self, *args, **kwargs):
        ...


stu = MyClass()
print(type(stu))  
# 使用了不符合全大写的类名就报错了
# TypeError: 请输入全大写类名

[6]总结

  • 元类属于面向对象中比较高阶的用法,无特殊需求和特殊情况外不建议使用
  • 如果你想高度定制类的产生过程
    • 那么编写元类里面的__init__方法
  • 如果你想高度定制对象的产生过程
    • 那么编写元类里面的__call__方法

补充、__new__方法

  • 元类中直接调用__new__
class MyType(type):
    # 其实我们自建的这个元类只是一个从type里派生方法的中间平台
    def __init__(cls, class_name, class_bases, class_dict):
        print('这是 MyType 里的 init 方法')
        # 在使用我们的元类定义类时就调用了这个函数先于call方法
        # 这里使用type里的__init__来创建一个空的类,方便创建类时进行类名,继承父类,和名称空间的定义
        super().__init__(class_name, class_bases, class_dict)

    def __call__(cls, *args, **kwargs):
        # 在使用类名加括号是会触发这个方法
        # 这个使用type里的__call__方法产生了一个对象并返回了出来
        obj = super().__call__(*args, **kwargs)
        print('这是 MyType 里的 call 方法')
        # 所以我们也要返回出去
        return obj

    def __new__(cls, *args, **kwargs):
        print('这是 MyType 中的 new 方法')
        # obj = super().__new__(cls, *args, **kwargs)
        obj = type.__new__(cls, *args, **kwargs)
        # ('MyClass', (<class 'object'>,), {'__module__': '__main__', '__qualname__': 'MyClass',
        # '__init__': <function MyClass.__init__ at 0x000001F9BFB21D80>,
        # '__call__': <function MyClass.__call__ at 0x000001F9BFB21E10>})
        return obj


# 通过上面我们自己定义的元类来产生类
# 这个过程与继承相似又不同

class MyClass(object, metaclass=MyType):
    def __init__(self):
        print('这是 MyClass 里的 init 方法')

    # 对象加括号调用了这个方法
    def __call__(self, *args, **kwargs):
        print('这是 MyClass 中的 call方法')

    def __new__(cls, *args, **kwargs):
        print('这是 MyClass 中的 new 方法')
        obj = super().__new__(cls)
        return obj
stu = MyClass()
stu()
'''
这是 MyType 中的 new 方法
这是 MyType 里的 init 方法
这是 MyClass 中的 new 方法
这是 MyClass 里的 init 方法
这是 MyType 里的 call 方法
这是 MyClass 中的 call方法
'''
  • 使用__call__间接调用
posted @   桃源氏  阅读(6)  评论(0编辑  收藏  举报
相关博文:
阅读排行:
· Manus重磅发布:全球首款通用AI代理技术深度解析与实战指南
· 被坑几百块钱后,我竟然真的恢复了删除的微信聊天记录!
· 没有Manus邀请码?试试免邀请码的MGX或者开源的OpenManus吧
· 【自荐】一款简洁、开源的在线白板工具 Drawnix
· 园子的第一款AI主题卫衣上架——"HELLO! HOW CAN I ASSIST YOU TODAY
点击右上角即可分享
微信分享提示