上期Python专栏,为大家介绍了Python对象是如何被构造的,以及类与对象的本质
今天来给大家讲讲新的硬核知识,经常看Python中文教程的朋友一定会发现,我们讲的新内容其实是现有Python中文教程里不常见的哦。
如何手动制造一个对象
基于上期的分析,类和对象的本质已经初见端倪——类和对象本质上也是一种映射结构,这一结构中存值的那一部分位于 dict ,而存储业务逻辑的部分则是各个函数,它们在 dir(t) 中均可以找到名称,并且可以通过 getattr 进行访问(实际上在Python中,函数也同样是一个对象)。
因此,我们可以基于上述的原理,尝试构造一个简易的对象出来。例如下面的例子:

首先在第6行,我们模仿 new 方法的思路,手动创建一个空对象(注意不能直接用 object ,而需要继承一层,具体原因详见官方文档中的Note部分);
接下来分别对对象的属性进行赋值,包括数值 x 和 y ,以及一个会基于 t.x 和 t.y 进行运算处理的函数 plus (一般我们更习惯于称之为方法);
最后就是使用这一手动创建的对象,可以看到 t.x和t.y均可正常使用,并且方法t.plus(z)也可以被正常调用。经过这一系列操作,一个手工创建的对象就产生了,而且从使用者的角度来看,也和正常实例化的对象并无差异。
如何手动制造一个类
不仅对象,类也是可以手动制造出来的。话不多说,我们先看看来自官方文档的构造 type 类说明

看起来挺长,不过后续附了一个最为简明扼要的例子。

所以其实依然不难理解,简单来说就是三个基本参数:
名称( name )——字面意思,表示构造的类名
基类( bases )——字面意思,表示所需要继承的基类
字典( dict )——即需要赋予对象的属性
因此基于以上的原理,我们可以构造出来一个自己的类,就像这样:

不难发现,从这样的视角来看,一个类的装配也大致分为三步:
“初始化阶段”——此阶段会创建一个指定名称的类对象
“继承阶段”——此阶段会尝试在类对象上建立与已有类的继承关系
“装配阶段”——次阶段会将类所需的各个属性,装配至类对象上
至此,经过了三个阶段后,一个类对象创建完毕,并且在使用上和正常定义的类并无差别。
私有字段的本质
对于了解Python面向对象或学习过Java、C++等其他语言的读者,应该对私有字段这个东西并不陌生(如果还不够了解的话可以看看Python3 面向对象 - 类的私有属性)。
Python3面向对象-类的私有属性参考地址:
(https://www.runoob.com/python3/python3-class.html)
在Python中,我们所熟知的私有字段大致是如下的形态:

简单来说就是:
私有字段,仅可以被类内部访问,以双下划线开头
保护字段,可以被当前类及其子类访问,以单下划线开头
公有字段,可以被自由访问,以字符开头
因此对上面的例子中,实际访问效果如下:

保护字段和公有字段是可以被访问到的,但是一般情况下,保护字段并不推荐在当前类或子类以外的地方进行访问(实际上当你这么做的时候,不少IDE都会报出明确的warning),而私有字段则无法访问,直接访问会导致报错。看起来似乎一切很正常,但是让我们来看看上面例子中变量 t 内部都有什么:

其中 public 和 protected 是意料之内的,但是除此之外还包含一个T__private,并且其值正是在构造函数中所赋予的值。基于这一点,我们再来做个实验。

发现私有字段居然也可以被访问。至此,我们可以得出一个结论——在Python中,并不存在严格意义上的私有字段,我们所知道的私有字段本质上更像一种语法糖效果,而保护字段则干脆是被摆在明面上的。
从这个角度来看不难发现,在Python中这些字段之所以还能起到私有字段或保护字段应有的效果,本质上靠的是开发者意义上的约束,而非语言系统本身的强制力。这一点和Java等静态语言存在本质上的差异,在Java中定义的私有字段一般无法通过正常途径进行访问,即便通过反射机制强制读取,也需要绕开一系列机制。
下期预告
本文重点针对类的特性,从原理角度进行了分析。在本系列的下一篇中,会重点针对类的方法和属性进行讲解,以及treevalue第三弹也将会在不久后推出,敬请期待。
欢迎大家体验OpenDILab开源的项目
作者小哥的开源项目(部分仍在开发中)
命令行工具
对象转可执行代码
好用的工具库


扫码即可了解更多开源信息~