【Python】深度理解Class类、Object类、Type元类的概念和关系


提要:作为普通的Python开发者来讲,深入理解object、type不是必要的,但了解他们确实会有所帮助的。本篇文章的本意是为了元编程打理论基础,但我相信无论是否元编程,本章也将大有裨益。

1.Class类、Object类、Type元类的表面关系

  • object:object类是所有类(class)的父类,包括type类,object类的父类为空
  • type:type类是所有类的类型,即为所有类(class)都可由type实例化而来,包括type类和父类object。
  • class:继承自object,同时,由type进行实例化。其中,type就是我们所讲的元类(metaclass)
print('type的父类是:',type.__base__)

class test:
    pass
print('class的父类是:',test.__base__)

print('object的父类是:',object.__base__)

在这里插入图片描述

2.Class、Object、Type解释

  • class:是典型的面对对象编程的表现形式,为定义对象的属性、行为提供了一个模板。更多的使用场景还是在后台需要个体实例的情况下;一般的model层开发,几乎还是用的函数编程。
  • object:Python3中所有class的顶级父类,在class编程中,隐式的自动继承object,object类为其提供了一些内置函数。在我们Python3的开发中,已经不需要去关注object类。
print(list(object.__dict__.keys()))

在这里插入图片描述

  • type:type是所有对象的顶级类型。在class编程中,隐式的默认声明metaclass=type。

3.关系详解

  • class 与 object的关系:
    在Python3中,已经隐式的自动继承了object方法,所以我们平时编写的class的的默认内置方法其实是由object方法提供的,Python3中任何一个类都默认继承了Object,是不可改变的。并且每个class的顶级父类都是object。
  • class 与 type的关系:
    在Python3中,已经隐式的默认了元类是type类,所以每个class的顶级类型都是type类
  • object、type:本文的核心之一就是阐述object与type的关系。
    上述有一个比较绕的逻辑是object类是type的父类,而object类是type实例化而来,我想了很久怎么去解释和阐述这个逻辑,发现其实从Python的层面来讲已经解释不清了,于是翻到了C的源码,又查了很多资料,翻了很多博客。这里我想结合所有所得总结一些信息:不用去深究object和type的深度关系是什么,除非你想去看完C的PyObject部分,我相信对绝大多数人来讲结果一定是戛然而止的。那么我们只需要知道type的父类是object,object的类型是type便好;也就是我上面说的所有类的顶级类型是type,所有类的顶级父类是object。所以,他们之间与其说是有所关系,不如说是相互协作。下面我会详解他们是如何协作的

4.那么如何看待object、type在Python面对对象概念中的一席之地呢?

这里引用这位博主的一句话。
可以把二者理解为是两个体系的王者,object是站在继承关系顶点的存在,所有的类最后都是继承自object的,object没有父类了,所以这里输出为空(),object是type类型的,也就是说type是站在类型关系顶端的存在,所有的类型都难逃type类型的掌握,所以object和type自己的类型都是type,type的父类是object。
我个人觉得这样的解释也算合理,对普通的开发人员而言已经足够。但如果你想做更加高级的动作,一定要看看下文;皆在我对继承、类型和抽象的理解。

5.那么object、type扮演了什么样的角色呢?他们对class又分别做了什么哪些动作?

object让类有了可以代代相传的通道,可以继承和实例化延续下去,它延续了一个技能,就需要元类,元类里面可以定制类的行为,比如重写__new__或者__init__等方法,就像基因编辑一样,制造出一个按照我们要求工作的娃娃。。
与其说object、type扮演了什么角色,不如讲什么是继承,什么是类型。事实上,Python对此的命名和抽象非常有水平,我想换一种类比去解释这个概念,这里的继承和类型放在中华概念中可以解释为 行为传承和物种生成(通俗点说,就是你向谁学东西,谁创造了你)(在此不得不惊叹guido对Python的审美已经到了极其优雅的程度,让人叹为观止),行为传承让成型的物种有了可以将技能代代相传的通道。那么成型的物种怎么理解呢:既然是物种,那么总有个发育和成型的过程吧。而type就是用来控制这个发育过程的直到它物种成型,然后继承行为,所以从执行顺序上讲object是在type之后。那么type在代码层面又是如何做的呢?我们从下面这个例子看一下:

class MyMeta(type):
 
    def __new__(cls, *args, **kwargs):
        print("元类中的__new__被调用")
        return super(MyMeta, cls).__new__(cls, *args, **kwargs)
    
    def __init__(self, *args, **kwargs):
        print("元类中的__init__被调用")
        return super(MyMeta, self).__init__(*args, **kwargs)
    
    def __call__(self, *args, **kwargs):
        print("元类中的__call__被调用")
        return super(MyMeta, self).__call__(*args, **kwargs)
 
class MyClass(metaclass=MyMeta):
    def __new__(cls, *args, **kwargs):
        print("主类中的__new__被调用")
        return super(MyClass, cls).__new__(cls, *args, **kwargs)
    
    def __init__(self, *args, **kwargs):
        print("主类中的__init__被调用")
        return super(MyClass, self).__init__(*args, **kwargs)
    
    def __call__(self, *args, **kwargs):
        print("主类中的__call__被调用")
        return super(MyClass, self).__call__(*args, **kwargs)
 
print("-------------------------\n创建具体的对象前\n----------------------------------")
m = MyClass()
元类中的__new__被调用
元类中的__init__被调用
-------------------------
创建具体的对象前
----------------------------------
元类中的__call__被调用
主类中的__new__被调用
主类中的__init__被调用

相信上图已经非常明了。在MyClass代码被加载进内存的时候就已经执行了元类的相关函数,这个过程其实就是物种发育成型。所以在我们平时的普通开发中,几乎都是基于物种发育成型后赋予相关技能行为的代码编写。而这种控制物种发育的过程,在Python代码层面叫做元编程。
执行顺序:
元类:new、init、call、 子类new、init、call。注意每个函数的第一个参数都是由上一个函数执行的返回结果,new的第一个参数是外部返回的结果,声明cls,其他函数的第一个参数是内部函数返回的结果声明为self,这种命名是guido做的一种范式,这样的命名设计是合理的。

6.Python在代码层面也提供了一些方法去判断继承和类型

提供了内置函数isinstance去比较类型关系;
提供了内置函数issubclass去比较继承关系;
提供了type()去判断类型;
提供了对象的__base__()方法去获取上一级的父类

总结:从事务看本质

object和type的产生本身就是一种自然抽象。其实从汇编、C、面向过程、面向对象这一路演化都是为了抽象,包括造轮子,语言生态,其实都是抽象的具体表现。那么殊途同归,从硬件到软件一路走来,所有动作都是抽象,它不是创造,而是优化,但某些优化趋近于创造。软件设计思想,其实都是从自然、人文等关系演化类比而来。软件思想就是一方世界,是自然的产生,是真实世界的映射,它和我们一样,在进步、在进化,与我们并肩前行。

  • 10
    点赞
  • 28
    收藏
    觉得还不错? 一键收藏
  • 打赏
    打赏
  • 3
    评论
Python 2.x 和 Python 3.x 是 Python 语言的两个主要版本。它们在语法、函数和标准库等方面有一些不同之处。下面是它们之间的一些主要区别: 1. 打印函数 在 Python 2.x 中,打印函数是 `print`,而在 Python 3.x 中,它被改为了 `print()` 函数。在 Python 2.x 中,您可以像这样使用 `print` 函数: ```python print "Hello, world!" ``` 在 Python 3.x 中,您需要在 `print` 函数中使用圆括号: ```python print("Hello, world!") ``` 2. 整数除法 在 Python 2.x 中,整数除法的结果将四舍五入为最接近的整数。例如,`5/2` 的结果为 `2`。在 Python 3.x 中,整数除法将保留小数部分,结果为浮点数。例如,`5/2` 的结果为 `2.5`。 3. xrange() 函数 在 Python 2.x 中,有一个称为 `xrange()` 的函数,它返回一个迭代器,用于生成整数序列。在 Python 3.x 中,这个函数被删除了,而 `range()` 函数现在返回一个似于 Python 2.x 中 `xrange()` 的迭代器。 4. Unicode 字符串 在 Python 2.x 中,字符串型有两种:ASCII 字符串和 Unicode 字符串。默认情况下,字符串型被定义为 ASCII 字符串。在 Python 3.x 中,字符串型被定义为 Unicode 字符串,这意味着它们可以包含任意的 Unicode 字符。 5. 异常处理语法 在 Python 2.x 中,异常处理语法是这样的: ```python try: # some code that may raise an exception except ExceptionType, e: # handle the exception ``` 在 Python 3.x 中,这个语法被更改为: ```python try: # some code that may raise an exception except ExceptionType as e: # handle the exception ``` 这里,异常型被指定为 `as` 关键字的参数,而不是 `,` 分隔符。 这些是 Python 2.x 和 Python 3.x 之间的一些主要区别。当迁移代码或选择使用哪个版本时,您应该考虑这些差异以及您的项目的需求。
评论 3
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包

打赏作者

会振刀的程序员

你的鼓励将是我创作的最大动力

¥1 ¥2 ¥4 ¥6 ¥10 ¥20
扫码支付:¥1
获取中
扫码支付

您的余额不足,请更换扫码支付或充值

打赏作者

实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

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

余额充值