作为面向对象编程(Object-Oriented Programming: OOP)的语言,C++和Python在很多应用中都很强调类的使用,这篇文章将展开类的讨论,并同时给出两种语言中的实现,以研究细节为主。
类(class)提供了将数据(data)和操作(functionality/operations)打包起来的方法。Python的类是 C++ 和Modula-3 中类机制的一个融合。就像模块(module)一样,Python的类具有动态特性:在运行的时候创建,并且可以在创建之后进行进一步的修改。
和 C++ 不一样,Python 可以将内置类型作为基类(base classes)扩展。和 C++ 一样,大多数的有特殊语法的内置运算符(operators)都可以被重载,供类的实例(instances)使用。
Python 中,同一具体的对象可以有多个名称,这在其他语言中叫别名(aliasing)。别名在涉及可变对象(如列表,字典和大多数其他类型)时可能非常方便。如传递一个对象实际上就是传递了一个指针,函数中如果修改了作为参数传入的对象,调用者可以在原来名字那里看到修改。
{% blockquote Eric Matthes, Python Crash Course %}
Object-oriented programming is one of the most effective approaches to writing soft-ware. In object-oriented programming you write classes that represent real-world things and situations, and you create objects based on these classes. When you write a class, you define the general behavior that a whole category of objects can have.
{% endblockquote %}
名词解释
看这篇文章,理应对类与对象有了基本的了解,但是若是要抛开代码把各个名词和原理将明白,却不是那么容易的事,所以把理论基础再说明白一点。这里我们采用来自Quora上一个解释的非常清楚的答案:
故事从上帝造人开始。上帝需要制造一个新的物种叫做人,也就是一个叫“人”的类(class)。上帝开始了设计,比如说人应该有手,腿,眼睛,鼻子等等,这些叫做类的属性(attributes/properties)。所有的人都应该可以走路,跑步,讲话,吃东西等等,这些就叫做行为和操作,也就是类的方法(methods)。但是现在在现实中还没有人,因为上帝的设计还在纸上,是个蓝图(blue print)。
那么上帝开始造人了,根据蓝图的设计,现实中人就被造了出来,这个过程就是创造类的对象(objects)。人应该在地球上有一块空间来存在,于是分配了这样的一块空间的过程就叫做创建对象的内存(memory)。
现在有个问题:上帝造的人怎么区分呢?上帝要是先让其中一部分去造房子怎么办?那么这就是一个实例化(instantiation) 的过程,上帝给某个人名字叫亚当,那么这样的对象(人) 就有了可供查阅的项(reference) 叫亚当。名叫亚当的这个对象成为了实例(instance) ,可以被 喊名字(refer) 区分了。
所以说,对象是一个统称的概念,它们物理意义上存在但是还没有被区分,实例具有身份了,可以被区别开来。我们所熟悉的整型就是类,整型变量是对象,名字叫 num 的对象就是一个实例了。OOP 语言中,归类带来了很多遍历,也比以基于操作的C语言等好理解。
创建一个类
设计一个类Dog
,狗,具有name
, age
两种属性,sit
, roll_over
两种行为/方法/操作。
C++ :
|
|
Python :
|
|
构造函数
在 C++ 中是和类名同名的无需指明返回类型的函数,而在 Python 中是 __init__()
方法。方法在实例化时自动调用,并且自动传递self
参数(是实例对自身的引用,其实在每个类方法中都要首先写出来),这也就是我们在实际创建实例的时候只需要传入除了self之外的参数的原因。
创建实例与调用方法
区别就是: C++ 先写变量类型(类名),再写变量名(实例名),自动调用构造函数,括号中需要填写传入的参数。而 Python 直接使用类名作为初始化函数,实则调用__init__()
函数,将对象传给有具体名称,产生实例。对于调用成员变量/函数,以及私有/公有属性的区别调用,下文将会讲到。
C++ :
|
|
Python:
|
|
变量类型
在 C++ 和 Python 中,变量可以这样划为三种类型: field (成员变量), parameters (参数), local variables (局部变量)。我们看成员变量field,它是具和class的这个对象一样具有一样的生存周期,当然field也是类中所有方法/函数都可以调用的。而后两者变量都是本地属性(保存在堆栈中),仅在函数执行时存在。三种类型变量具体区别如下:
私有和公有属性
C++ 定义私有/公有属性/函数需要加上关键字private:
, public:
,保护成员则使用关键字protected
。派生类无法访问基类的所有私有成员,但是可以访问基类的公有和保护成员,外部类只能访问当前类的公有成员。但是 Python 只需要在变量名/方法名之前加上单下划线_
或者双下划线__
,而非关键字。在模块中,单下划线开头的常被默认为内部或者保护变量/函数,用from module import *
的方式,这些单下划线开头的变量和函数不会被导入;但是import module
这种方式导入模块的话,可以用module._变量 的形式访问到这些变量和函数。如这样的module.py
:
|
|
采用两种导入方式对比:
双下划线开头的命名形式,通常用于python的类中,用于将变量伪私有化,即用双下划线开头的属性或方法,不能被外部调用,也不会被子类继承;但实际上只是将该变量的名字做了改变,比如__method
变为了 _{class}_method
,通过 {instance}._{class}__method
的方式仍然可以调用。如在module.py
中定义类Module
和其子类subModule
:
|
|
对类成员变量的调用:
|
|
类的继承
在 Python 中,子类初始化函数需要调用super()
来调用父类的初始化函数。即:super().__init__()
。
|
|
在 C++ 中,类的继承比较复杂。单继承基本形式为:
|
|
access_specifier
:访问修饰符,公共/保护或私有,指明继承类型。通常用public继承,而不是protected和private。
当使用不同类型的继承时,遵循以下几个规则:
- 公有继承(public): 当一个类派生自公有基类时,基类的公有成员也是派生类的公有成员,基类的保护成员也是派生类的保护成员,基类的私有成员不能直接被派生类访问,但是可以通过调用基类的公有和保护成员来访问。
- 保护继承(protected): 当一个类派生自保护基类时,基类的公有和保护成员将成为派生类的保护成员。
- 私有继承(private): 当一个类派生自私有基类时,基类的公有和保护成员将成为派生类的私有成员。
多继承形式和但继承基本一致,不过冒号后面的内容要以逗号隔开。
|
|
类的导入
Python 环境下使用from
, import
语句,全部导入就用*
,导入部分就写逗号隔开的需要导入的类。采用单import
则需要每次使用module中的类都需要加上module名的前缀:module.
。
C++ 环境下则include类所在的头文件(含头文件所在位置信息)。
参考
- The Python Tutorial >> Classes
- hat is the difference between object and instance?
- Summary 3 – Variables: Fields vs. Parameters vs. Local variables
- Python中关于私有、公有的属性和方法
- Matthes, Eric. Python crash course: a hands-on, project-based introduction to programming. No Starch Press, 2015. 161-183