面向对象编程简介¶
我们首次对Python基础的探索向我们介绍了基础对象类型:数字,字符串,和列表。相似的,我们对NumPy的讨论也是围绕着N维数组展开的。这些对象类型之间很大的区别和它们绑定的不同函数。绑定(bound)到数组上的函数叫做方法(method)。比如说,字符串有着多个旨在操作其储存的字符序列的方法,而NumPy数组有着很多操作数组内置数字数据的方法。
# 不同类型的对象可以储存不同的方法
>>> string = "hello world"
>>> string.capitalize() # 使用字符串方法 `capitalize`
'Hello world'
>>> import numpy as np
>>> array = np.array([[0, 1, 2],
... [3, 4, 5]])
>>> array.sum() # 使用数组方法 `sum`
15
更加广义来讲,对象可以拥有总结关于对象信息的属性(attribute)。比如说,数组属性 ndim
和 shape
提供了关于数组数字数据索引布局的信息。
# 访问对象的属性
>>> array.ndim
2
>>> array.shape
(2, 3)
在本模组中,我们将学习如何定义我们自己的,可自定义的,有着不同集合的属性和方法的对象类型。通过这么做·,我们将将Python作为一个“面向对象”(object-oriented)编程语言使用;这将大幅提升我们作为Python用户的能力并让我们更加深入地理解Python这门语言。
作为一个预览,让我们创建我们自己的名为 Rectangle
的类(class):
class Rectangle:
""" 一个描述长方形特性的Python对象 """
def __init__(self, width, height, center=(0.0, 0.0)):
""" 设置某个 `Rectangle` 实例的属性。
Parameters
----------
width : float
这个长方形实例的x长度。
height : float
这个长方形实例的y长度。
center : Tuple[float, float], optional (default=(0, 0))
这个长方形的中心的 (x, y) 位置"""
self.width = width
self.height = height
self.center = center
def __repr__(self):
""" 返回在打印某个长方形时使用的字符串。"""
return "Rectangle(width={w}, height={h}, center={c})".format(h=self.height,
w=self.width,
c=self.center)
def compute_area(self):
""" 返回长方形的面积
Returns
-------
float"""
return self.width * self.height
def compute_corners(self):
""" 计算这个长方形的四角的 (x, y) 位置,首先返回“左上”角,然后顺时针继续。
Returns
-------
List[Tuple[float, float], Tuple[float, float], Tuple[float, float], Tuple[float, float]]"""
cx, cy = self.center
dx = self.width / 2.0
dy = self.height / 2.0
return [(cx + x, cy + y) for x,y in ((dx, dy), (dx, -dy), (-dx, -dy), (-dx, dy))]
这个 Rectangle
类的一个实例(instance)是一个属性包括长度,高度,和中心坐标的单个长方形。同时,我们可以使用这个长方形的方法(它为函数的属性)来计算它的面积和四角的坐标。
# 创建一个长度为4,高度为0,中心为 (0, 0) 的长方形
# 这将执行 __init__ 方法并设置它的width/height/center属性
>>> rect1 = Rectangle(4, 10)
# __repr__ 方法定义了长方形实例在命令行中将如何显示
>>> rect1
Rectangle(width=4, height=10, center=(0, 0))
# 计算这个长方形的面积
>>> rect1.compute_area()
40
# 计算这个长方形的四角坐标
>>> rect1.compute_corners()
[(2.0, 5.0), (2.0, -5.0), (-2.0, -5.0), (-2.0, 5.0)]
就像我们遭遇过的任何其它Python对象一样,我们可以将我们的 Rectangle
实例放在字符串中,存储为词典的值,作为参数输入到函数中,使用多个变量引用它们,等等。
流行的科学,数据分析,和机器学习Python模组都很依赖定义Python自定义类的能力。比如说,pandas定义了一个类似表格的 DataFrame
类;PyTorch,MXNet,和TensorFlow都定义了能够自动求导数的张量类;这对训练神经网络极其重要。理解Python的类系统会将大幅提升你使用像以上这些的模组(无耻广告:如果你对自动求导的进行纯Python/NumPy实现的模组感兴趣,请查阅MyGrad模组——译者注:这是作者自己写的模组,很厉害的哦!)。
在本模组,我们将会讨论类定义的基础;这将允许我们定义自己的对象类(class)(也就是类型(type))。然后,我们将学习如何创建某个对象类型的不同的实例并定义方法。这将导致我们初次见到特殊方法;它们将允许我们修改我们对象类型在配合各种Python操作符时的行为。比如说,我们可以定义 +
操作符会如何和我们的对象互动。最后,我们将快速地讨论类继承(class inheritance)的概念。
经验:
本模组旨在理解如何定义和使用我们自己的Python对象类。这将极大地成熟化我们对Python作为一门面向对象语言的理解并提升我们完整使用Python所有功能的能力。
类vs类型:关于术语的重要注释¶
在我们深入讨论之前,我们应该花一些时间来了解术语“类型”(type)和“类”(class)在Python的实际操作中是同义的。到现在为止,我们仅仅使用过术语“类型”来将对象互相区分。比如说,1
术语类型 int
,而 "cat"
术语类型 str
。但是,我们将马上学习用来创造新类型对象的类定义并学习如 issubclass
的函数。虽然如此,请记住,类和类型指的是同一个东西!存在两个不同术语的历史遗留,但是Python
2.2之后类型和类的概念被完全统一了。
在实际操作中,人们一般使用“类型”一词来指内置的类型(如 int
和 str
),而“类”则指用户定义的类型。最后重申一遍,在Pythond的现代版本中,这两次并没有任何实际的区别。
经验:
两个术语“类型”和“类”是同义的;它们都指着对某一类型/类的Python对象的包装定义。虽然它们在Python语言中不能互换——我们将编写类定义,而不是类型定义,而我们将使用 type
而不是 class
来检查对象——但这些区别仅仅是早期Python版本的遗留物而已。