尽管 Python 是一种没有大惊小怪或狡辩的面向对象语言,但到目前为止,我们在 Python 教程的前几章中有意避免了对面向对象编程 (OOP) 的处理。我们跳过了 OOP,因为我们确信开始学习 Python 更容易、更有趣,而无需了解面向对象编程的所有细节。
尽管我们避免了面向对象编程,但它始终存在于我们课程的练习和示例中。我们使用了类中的对象和方法,但没有正确解释它们的 OOP 背景。在本章中,我们将补上迄今为止遗漏的内容。我们将介绍面向对象编程的一般原则和 Python 的 OOP 方法的细节。OOP 是 Python 最强大的工具之一,但是您不必使用它,也就是说,即使没有它,您也可以编写强大而高效的程序。
尽管许多计算机科学家和程序员认为 OOP 是一种现代编程范式,但其根源可以追溯到 1960 年代。第一种使用对象的编程语言是 Simula 67。顾名思义,Simula 67 于 1967 年推出。面向对象编程的重大突破来自 1970 年代的编程语言 Smalltalk。
在本教程的下一部分面向对象编程中,您将学习了解面向对象的四大原则以及 Python 处理它们的方式:
- 封装
- 数据抽象
- 多态性
- 遗产
在我们开始关于 OOP 在 Python 中的使用方式的部分之前,我们想给您一个关于面向对象编程的一般概念。为此,我们想提请您注意公共图书馆。让我们想想一个巨大的图书馆,比如伦敦的“大英图书馆”或纽约的“纽约公共图书馆”。如果有帮助,您也可以想象一下巴黎、柏林、渥太华或多伦多*的图书馆。其中每一个都包含有组织的书籍、期刊、报纸、有声读物、电影等集合。
通常,有两种相反的方式将库存保存在图书馆中。您可以使用“封闭式访问”方法,即库存不显示在开放式货架上。在这个系统中,经过培训的工作人员将书籍和其他出版物按需提供给用户。运行图书馆的另一种方式是开放式书架,也称为“开放式书架”。“开放”是指对图书馆的所有用户开放,而不仅仅是受过专门培训的工作人员。在这种情况下,书籍是公开展示的。像 C 这样的命令式语言可以被视为开放存取的搁置库。用户可以做任何事情。由用户来查找书籍并将它们放回正确的书架上。尽管这对用户来说很好,但从长远来看,它可能会导致严重的问题。例如,有些书会放错地方,因此很难再次找到它们。正如您可能已经猜到的那样,“封闭访问”可以与面向对象编程相提并论。类比可以这样看:图书馆提供的书籍和其他出版物就像面向对象程序中的数据。对书籍的访问受到限制,就像在 OOP 中对数据的访问受到限制一样。只能通过工作人员获取或归还书籍。人员功能类似于 OOP 中的方法,控制对数据的访问。因此,此类程序中的数据——通常称为属性——可以被视为被外壳隐藏和保护,并且只能由特殊函数访问,通常在 OOP 上下文中称为方法。将数据放在“shell”后面称为 类比可以这样看:图书馆提供的书籍和其他出版物就像面向对象程序中的数据。对书籍的访问受到限制,就像在 OOP 中对数据的访问受到限制一样。只能通过工作人员获取或归还书籍。人员功能类似于 OOP 中的方法,控制对数据的访问。因此,此类程序中的数据——通常称为属性——可以被视为被外壳隐藏和保护,并且只能由特殊函数访问,通常在 OOP 上下文中称为方法。将数据放在“shell”后面称为 类比可以这样看:图书馆提供的书籍和其他出版物就像面向对象程序中的数据。对书籍的访问受到限制,就像在 OOP 中对数据的访问受到限制一样。只能通过工作人员获取或归还书籍。人员功能类似于 OOP 中的方法,控制对数据的访问。因此,此类程序中的数据——通常称为属性——可以被视为被外壳隐藏和保护,并且只能由特殊函数访问,通常在 OOP 上下文中称为方法。将数据放在“shell”后面称为 只能通过工作人员获取或归还书籍。人员功能类似于 OOP 中的方法,控制对数据的访问。因此,此类程序中的数据——通常称为属性——可以被视为被外壳隐藏和保护,并且只能由特殊函数访问,通常在 OOP 上下文中称为方法。将数据放在“shell”后面称为 只能通过工作人员获取或归还书籍。人员功能类似于 OOP 中的方法,控制对数据的访问。因此,此类程序中的数据——通常称为属性——可以被视为被外壳隐藏和保护,并且只能由特殊函数访问,通常在 OOP 上下文中称为方法。将数据放在“shell”后面称为封装。所以图书馆可以看作是一个类,一本书是这个类的一个实例或一个对象。一般来说,一个对象是由一个类定义的。类是对对象如何设计的正式描述,即它具有哪些属性和方法。这些对象也称为实例。在大多数情况下,这些表达方式是同义词。类不应与对象混淆。
Python 中的面向对象编程
尽管我们在前面的章节中没有讨论类和面向对象,但我们一直在使用类。事实上,在 Python 中一切都是一个类。Guido van Rossum 根据“一流的一切”原则设计了语言。他写道:“我对 Python 的一个目标是让所有对象都是“一流的”。我的意思是我想要所有可以用该语言命名的对象(例如,整数、字符串、函数、类、模块、方法等)具有同等的地位。也就是说,它们可以分配给变量、放在列表中、存储在字典中、作为参数传递等等。” (博客,Python 的历史,2009 年 2 月 27 日)换句话说,“一切”都被同等对待,一切都是一个类:函数和方法是值,就像列表、整数或浮点数一样。这些中的每一个都是其相应类的实例。
x = 42
类型( x )
输出:
整数
y = 4.34
类型( y )
输出:
漂浮
def f ( x ):
返回 x + 1
类型( f )
输出:
功能
导入 数学
类型(math )
输出:
模块
Python 中的众多集成类之一是列表类,我们在练习和示例中经常使用它。list 类提供了丰富的方法来构建列表、访问和更改元素或删除元素:
x = [ 3 , 6 , 9 ]
y = [ 45 , "abc" ]
打印( x [ 1 ] )
输出:
6
x [ 1 ] = 99
x 。追加( 42 )
last = y 。弹出()
打印(最后)
输出:
美国广播公司
上一个例子中的变量 x 和 y 表示列表类的两个实例。简而言之,到目前为止我们已经说过“x 和 y 是列表”。我们将在接下来的章节中同义地使用术语“对象”和“实例”,因为这通常是这样做的**。
前面例子的 pop 和 append 是 list 类的方法。pop 返回列表的顶部(或者您可能认为它是“最右边的”)元素并从列表中删除该元素。我们不会解释 Python 如何在内部实现列表。我们不需要这些信息,因为列表类为我们提供了间接访问数据的所有必要方法。即封装了封装细节。稍后我们将学习封装。
Python 中的最小类
我们将设计并使用 Python 中的机器人类作为示例来演示面向对象最重要的术语和思想。我们将从 Python 中最简单的类开始。
机器人类:
经过
我们可以在 Python 中实现一个类的基本句法结构:一个类由两部分组成:头部和主体。标题通常只包含一行代码。它以关键字“class”开头,后跟一个空格和一个任意的类名称。在我们的例子中,类名是“Robot”。类名后面是其他类名的列表,这些类名是定义的类继承自的类。这些类称为超类、基类或有时称为父类。如果您查看我们的示例,您会发现这个超类列表不是强制性的。您现在不必担心继承和超类。我们稍后会介绍它们。
一个类的主体由一个缩进的语句块组成。在我们的例子中是一个单独的语句,即“pass”语句。
当定义正常保留时,即通过末尾创建类对象。这基本上是对由类定义创建的命名空间内容的包装。
很难相信,尤其是对于 C++ 或 Java 程序员来说,但我们已经定义了一个完整的类,只有三个词和两行代码。我们也可以使用这个类:
类 机器人:
通
如果 __name__ == “__main__” :
X = 机器人()
Ý = 机器人()
Y2 = Ý
打印(Ý == Y2 )
打印(Ý == X )
输出:
真的
错误的
我们在示例中创建了两个不同的机器人 x 和 y。除此之外,我们还创建了一个对 y 的引用 y2,即 y2 是 y 的别名。
属性
那些已经学习过另一种面向对象语言的人一定已经意识到,属性和特性这两个术语通常是同义词。它甚至可以用在属性的定义中,就像维基百科所做的那样:“在计算中,属性是定义对象、元素或文件的属性的规范。它也可以引用或设置特定值给定这样的例子。”
即使在正常的英语用法中,“attribute”和“property”这两个词在某些情况下也可以用作同义词。两者都可以具有“某物或某人的属性、特征、质量或特征”的含义。通常,“属性”用于表示某物或某人具有的特定能力或特征,例如黑头发、无头发或快速感知,或“她对新任务的把握能力”。所以,想一想你的杰出品质。你的“杰出属性”呢?太好了,如果您的强项之一是快速理解和适应新情况的能力!否则,你就不会学习 Python!
让我们回到 Python:稍后我们将了解到属性和属性在 Python 中本质上是不同的东西。我们教程的这一小节是关于 Python 中的属性。到目前为止,我们的机器人还没有任何属性。连名字都没有,就像普通机器人的习惯一样,不是吗?所以,让我们实现一个名称属性。“类型名称”、“建造年份”等也很容易想象为进一步的属性***。
属性是在类定义中创建的,我们很快就会了解到。我们可以为类的现有实例动态创建任意新属性。我们通过将任意名称连接到实例名称来实现这一点,用点“.”分隔。在以下示例中,我们通过为名称和构建年份创建一个属性来演示这一点:
类 机器人:
传递
x = Robot ()
y = Robot ()
x 。名称 = "马文"
x 。build_year = "1979"
y 。name = "Caliban"
y 。build_year = "1993"
打印( x . name )
输出:
马文
打印( y . build_year )
输出:
1993年
正如我们之前所说:这不是正确创建实例属性的方法。我们介绍这个例子,是因为我们认为它可能有助于使下面的解释更容易理解。
如果你想知道内部发生了什么:实例拥有字典__dict__
,它们用来存储它们的属性和它们对应的值:
× 。__dict__
输出:
{'name': 'Marvin', 'build_year': '1979'}
y 。__dict__
输出:
{'name': 'Caliban', 'build_year': '1993'}
属性也可以绑定到类名。在这种情况下,每个实例也将拥有此名称。注意,如果您为实例分配相同的名称会发生什么:
类 机器人(对象):
通过
x = 机器人()
机器人。品牌 = "库卡"
x . 品牌
输出:
'库卡'
× 。品牌 = “泰雷兹”
机器人。品牌
输出:
'库卡'
y = 机器人()
y 。品牌
输出:
'库卡'
机器人。品牌 = "泰雷兹"
y 。品牌
输出: