面向对象编程简介

我们首次对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)。比如说,数组属性 ndimshape 提供了关于数组数字数据索引布局的信息。

# 访问对象的属性
>>> 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 类;PyTorchMXNet,和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之后类型和类的概念被完全统一了。

在实际操作中,人们一般使用“类型”一词来指内置的类型(如 intstr),而“类”则指用户定义的类型。最后重申一遍,在Pythond的现代版本中,这两次并没有任何实际的区别。

经验

两个术语“类型”和“类”是同义的;它们都指着对某一类型/类的Python对象的包装定义。虽然它们在Python语言中不能互换——我们将编写类定义,而不是类型定义,而我们将使用 type 而不是 class 来检查对象——但这些区别仅仅是早期Python版本的遗留物而已。

官方说明文档链接