首页 > 其他分享 >面向对象之元类

面向对象之元类

时间:2024-01-29 14:57:33浏览次数:24  
标签:__ 面向对象 init call print type class 之元类

面向对象之元类

一、什么是元类

  • 产生已知类的类就叫做元类,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__间接调用

标签:__,面向对象,init,call,print,type,class,之元类
From: https://www.cnblogs.com/taoyuanshi/p/17994498

相关文章

  • 面向对象之内置方法
    面向对象之内置方法​Python的Class机制内置了很多特殊的方法来帮助使用者高度定制自己的类这些内置方法都是以双下划线开头和结尾的,会在满足某种条件时自动触发__init__ :初始化类时触发__del__ :删除类时触发__new__ :构造类时触发__str__ :str函数或者print函数触发__......
  • 面向对象之派生和组合
    面向对象之派生和组合派生派生是指,子类继承父类,派生出自己的属性与方法,并且重用父类的属性与方法一、派生的方法子类是必须要派生出自己的数据属性不然就无数据属性可用子类无法使用父类中__init__定义的属性classPerson(object):def__init__(self,name,age......
  • 面向对象基础 成员变量、成员方法、构造方法、this关键字、静态字段、静态方法..
    成员变量与局部变量的区别:  1.作用域:成员变量作用于整个类,局部变量只作用于它所属的范围(函数、语句)  2.生命周期&位置:成员变量存储在堆内存中,是属于对象的,随着对象存在消失。局部变量存储在栈内存中,是属于他所属的范围的,使用完自动释放。  3.初始值:成员变量有默认初始......
  • day01-面向对象高级
    day01——面向对象高级各位同学,接下来的三天课程中,我们继续学习面向对象的相关课程。面向对象是写Java程序的核心套路,如何你不懂面向对象,那就相当于Java你白学了。所以在接下来的三天时间里,各位同学也需要克服重重困难好好学习。前面我们说过面向对象最核心的套路是:设计对象来处......
  • C# 面向对象编程进阶:构造函数详解与访问修饰符应用
    C#构造函数构造函数是一种特殊的方法,用于初始化对象。构造函数的优势在于,在创建类的对象时调用它。它可以用于为字段设置初始值:示例获取您自己的C#服务器创建一个构造函数://创建一个Car类classCar{publicstringmodel;//创建一个字段//为Car类创建一个......
  • C# 面向对象编程进阶:构造函数详解与访问修饰符应用
    C#构造函数构造函数是一种特殊的方法,用于初始化对象。构造函数的优势在于,在创建类的对象时调用它。它可以用于为字段设置初始值:示例获取您自己的C#服务器创建一个构造函数://创建一个Car类classCar{publicstringmodel;//创建一个字段//为Car类创建一......
  • 理论篇:面向对象程序设计指导
    》本文来自看过的相关知识的摘录整理,太久了,忘了主要出自哪儿里了。软件架构架构(Architecture)是指一个系统或软件的总体设计和组织结构,包括其各个组件、模块、接口和数据流等。架构设计的目的是确保系统或软件具有可扩展性、可维护性、可靠性和安全性等特性,并且能够满足业务需......
  • 《设计模式:可复用面向对象软件的基础》PDF
    内容简介本书结合设计实例从面向对象的设计中精选出23个设计模式,总结了面向对象设计中*有价值的经验,并且用简洁可复用的形式表达出来。本书分类描述了一组设计良好、表达清楚的软件设计模式,这些模式在实用环境下特别有用。本书适合大学计算机专业的学生、研究生及相关人......
  • 面向对象设计原则
    一、面向对象#OOP实际包含OOA(面向对象分析),OOD(面向对象设计),OOP(面向对象编程)三部分#封装:隐藏信息、保护数据;属性暴露get方法,修改通过方法定义暴露get;privateset;#抽象:隐藏方法的具体实现,调用者只需要关心方法提供了哪些功能#继承:代码复用;两个类有一些相同的属性和方法,......
  • python 面向对象专题(23):基础(14)类对象、实例对象、类属性、实例属性、类方法、实例方法
    1简易理解(快速理解)类对象:定义的类就是类对象实例对象:类对象实例化后就是实例对象类属性:定义在init外部的变量实例属性:定义在__init__内部的带有self.的变量类方法:定义在类对象中且被@classmethod装饰的方法就是类方法实例方法:定义在类对象中,且......