类在python中以class定义。
从有道翻译可以看到,class中的阶级,班级或者等级也好,实则都是指一个群体。所以class用到python的语法中来就被称为‘类’,指一类事物的总称。
下面关于类的介绍可能不是很浅显,但尽量用易懂的话描述。
在python中,类(class)中封装了一组相关数据(一般是一些方法和属性)。使之成为一个整体。
在用户(使用类完成一定功能的人)看来,类就是一个封装好了直接可用的工具,‘类’就好比是一辆汽车,用户只要知道怎么用油门、刹车、方向盘驾驶就好而不必去理会汽车中的零部件是如何工作的。
类与函数的异同(此处讲的函数与方法不是一个概念,不要混为一谈):
函数和类都是一种小粒度复用单位,但类的行为特征更复杂。
函数具有单一入口和出口,可以完成一次计算过程。而类从构造开始就面临多个方法,这些方法在不同的调用次序下会产生不同的结果。这也印证了类的行为特征更复杂。
类擅长对有持续状态、有生命周期、有遗传特性的物体进行抽象模拟。在保留物体共性的前提下,每个个体又会有自己的特征。如此一来,类关注数据本身(面向对象)。
类存在两种关系:1、继承自某个(某几个)族类;2、组合了哪些部件(方法、属性)
类与模块的异同:
类和生成多个实例;
类可被继承和扩展;
类实例的生命周期可控;
类支持运算符,可按需重载。
模块的粒度大,其可用来提供游戏场景级别的解决方案,而类则是该场景下特定家族和演员(模块>=类)
一、类的创建
以上的两种方式略有不同,这里加不加括号在python3中已无影响,因为python3已做优化。
实则都是继承于object的。
关键字class是运行期的指令。完成对类型对象的创建。并且类可以在函数内部定义。而非我们常见的在类中定义‘函数’,这里类中的函数我更愿意称其为‘方法’。
类在函数test中被定义。
反汇编查看具体创建过程
反汇编:在执行python代码的过程中是python代码先被编译为字节码后,再由python虚拟机执行字节码。Python的字节码其实是一种类似于汇编指令的中间语言,一条python语句会对应若干条字节码指令。然后虚拟机一条一条执行字节码指令,从而完成程序执行。
而dis模块支持对python代码进行反汇编,成为字节码,从而能够清楚python代码背后的执行过程。
由上述执行结果为例
第一列的数字表示对应源代码的行数(这里第一列的数字是2)。
第二列的数字是字节码的索引,指令LOAD_BUILD_CLASS在0的位置。
第三列是指令执行的功能描述,也是我们可读的
第四列是指令的参数
第五列是计算后的实际参数
从反汇编结果来看,先创建X函数,然后是属性(data)设置和方法(get())创建.随后这个X函数被当作参数传递给builtins.__build_class__调用.在__build_class__调用X时.会提供一个字典作为堆栈帧的名字空间.用于保存类型成员,完成后将字典连同名字和基类一起传递给元类,最终生成目标类型的对象.(元类用于创建类型对象)
Python在编码过程中会生成codeObject(如上述第一张反汇编图中的<code object X>), CodeObject是在虚拟机中的抽象表示。
二、类与实例
类在模块中定义,类的生命周期与模块等同。如果其被放在函数内,那么每次都是新建。即使每次都是相同的名字和内容,也属于不用的类了。
若在函数内定义类,那么在所有实例死亡后,会被回收。
类型对象除了用来创建实例外,也为所有实例定义了操作接口,类型负责管理整个家族的可共享数据和行为模板。实例仅保留私有特征,其以内部引用从 所属的类型和祖先类型 中查找到需要的方法。用来展现个体的面貌。
类型创建的众多实例中,实例之间并无直接关系,单个实例从诞生到消亡整个过程都不影响类型或者其他实例。
三、类的名字空间
类型有自己的名字空间,里面存储的是当前类型定义的方法和字段。类的祖先类型的成员并不在此列,祖先的类型成员还是由引用关联。
class A:
a =10 # 类字段
def __init__(self, x): # 实例初始化方法
self.x = x # 实力字段
def get_x(self): # 实例方法
return self.x
class B(A): # 继承A
b = "python"
def __init__(self, x, y):
super().__init__(x) # 引用父类A的初始化方法
self.y = y
def get_y(self):
return self.y
>>> ad = B(1,2) # 实例化
实例会存储所有继承层次的实例字段(上面的ad会继承A、B的实例字段),因为这些都是属于ad自己的的私有数据(类共有的数据都存放在__init__( )方法中)
>>> A.__dict__
mappingproxy({'__module__': '__main__', 'a': 10, '__init__': <function A.__init__ at 0x00000000005E1EA0>, 'get_x': <function A.get_x at 0x0000000003136840>, '__dict__': <attribute '__dict__' of 'A' objects>, '__weakref__': <attribute '__weakref__' of 'A' objects>, '__doc__': None})
>>> B.__dict__
mappingproxy({'__module__': '__main__', 'b': 'python', '__init__': <function B.__init__ at 0x00000000031368C8>, 'get_y': <function B.get_y at 0x0000000003136950>, '__doc__': None})
类型的名字空间返回 mappingproxy只读视图 不允许直接修改
>>> ad = B(1,2)
>>> ad.__dict__
{'x': 1, 'y': 2}
>>> dir(ad)
['__class__', '__delattr__', '__dict__', '__dir__', '__doc__', '__eq__', '__format__', '__ge__', '__getattribute__', '__gt__', '__hash__', '__init__', '__init_subclass__', '__le__', '__lt__', '__module__', '__ne__', '__new__', '__reduce__', '__reduce_ex__', '__repr__', '__setattr__', '__sizeof__', '__str__', '__subclasshook__', '__weakref__', 'a', 'b', 'get_x', 'get_y', 'x', 'y']
>>> ad = B(1,2)
>>> ad.__dict__
{'x': 1, 'y': 2}
>>> dir(ad)
['__class__', '__delattr__', '__dict__', '__dir__', '__doc__', '__eq__', '__format__',
'__ge__', '__getattribute__', '__gt__', '__hash__', '__init__', '__init_subclass__', '__le__',
'__lt__', '__module__', '__ne__', '__new__', '__reduce__', '__reduce_ex__', '__repr__',
'__setattr__', '__sizeof__', '__str__', '__subclasshook__', '__weakref__', 'a', 'b', 'get_x',
'get_y', 'x', 'y']
而实例的名字空间是普通字典,可以直接修改。函数dir返回所有可访问的成员名字。