PY4E-CN-Ch14: 面向对象的编程
发布时间:2022-09-23 14:59:48 所属栏目:PHP 来源:
导读: 在本书的开头,我们提出了四个基本的编程模式,我们用它们来构建程序。
在后面的章节中,我们探讨了简单的变量以及集合数据结构,如列表、图元和字典。
当我们构建程序时,我们设计数据结构并编
在后面的章节中,我们探讨了简单的变量以及集合数据结构,如列表、图元和字典。
当我们构建程序时,我们设计数据结构并编
|
在本书的开头,我们提出了四个基本的编程模式,我们用它们来构建程序。 在后面的章节中,我们探讨了简单的变量以及集合数据结构,如列表、图元和字典。 当我们构建程序时,我们设计数据结构并编写代码来操作这些数据结构。写程序有很多方法,到现在,你可能已经写了一些 "不那么优雅 "的程序和其他 "更优雅 "的程序。尽管你的程序可能很小,但你已经开始看到写代码有一点艺术和美感了。 当程序长达数百万行时,写出易于理解的代码变得越来越重要。如果你正在处理一个百万行的程序,你永远不可能在同一时间把整个程序放在脑海中。我们需要有办法将大程序分解成多个小块,这样我们在解决问题、修复错误或增加新功能时,就可以少看一些。 在某种程度上,面向对象的编程是一种安排代码的方式,这样你就可以放大50行代码并理解它,而暂时忽略其他999950行的代码。 开始工作 像编程的许多方面一样,在你能有效地使用它们之前,有必要学习面向对象编程的概念。你应该把本章作为学习一些术语和概念的途径,并通过一些简单的例子来为今后的学习打下基础。 本章的主要成果是对对象的构造和功能有了基本的了解,最重要的是我们如何利用 Python 和 Python 库提供给我们的对象的能力。 使用对象 事实证明,我们在本书中一直都在使用对象。Python 为我们提供了许多内置的对象。下面是一些简单的代码,其中前几行对你来说应该感觉非常简单和自然。 stuff = list() stuff.append('python') stuff.append('Chuck') stuff.sort() print (stuff[0]) print (stuff.__getitem__(0)) print (list.__getitem__(stuff,0)) # 代码: http://www.py4e.com/code3/party1.py 与其关注这些行的成就,不如从面向对象编程的角度来看看真正发生了什么。如果你第一次读下面的段落时没有任何意义,请不要担心,因为我们还没有定义所有这些术语。 第一行构造了一个列表类型的对象,第二和第三行调用 append() 方法,第四行调用sort()方法,第五行检索位置为0的项目。 第六行调用东西列表中的__getitem__()方法,参数为零。 print (stuff.__getitem__(0)) 第七行是一个更加冗长的检索列表中第0项的方法。 print (list.__getitem__(stuff,0)) 在这段代码中,我们调用列表类中的__getitem__方法,并将列表和我们想从列表中获取的项目作为参数传递。 程序的最后三行是等价的,但是简单地使用方括号语法来查询列表中某一特定位置的项目更为方便。 我们可以通过查看dir()函数的输出来看看一个对象的能力。 >>> stuff = list() >>> dir(stuff) ['__add__', '__class__', '__contains__', '__delattr__' 。 '__delitem__', '__dir__', '__doc__', '__eq__', '__format__', '__ge__', '__getattribute__', '__getitem__' 。 '__gt__', '__hash__', '__iadd__', '__imul__', '__init__' 。 '__iter__', '__le__', '__len__', '__lt__', '__mul__', '__ne__', '__new__', '__reduce__', '__reduce_ex__' 。 '__repr__', '__reversed__', '__rmul__', '__setattr__', '__setitem__', '__sizeof__', '__str__', '__subclasshook__', 'append', 'clear', 'copy', 'count', 'extend', 'index', '插入', '弹出', '移除', '反向', '排序'] >>> 本章的其余部分将对上述所有术语进行定义,因此确保在你完成本章后回来重新阅读上述段落以检查你的理解。 从方案开始 一个程序最基本的形式是接受一些输入,做一些处理,并产生一些输出。我们的电梯转换程序展示了一个非常简短但完整的程序,显示了所有这三个步骤。 usf = input('Enter the US Floor Number: ') wf = int(usf) - 1 print('非美国楼层号码是',wf) # 代码:http://www.py4e.com/code3/elev.py 如果我们对这个程序再多想一点,有 "外部世界 "和程序。输入和输出方面是程序与外部世界互动的地方。在程序内部,我们有代码和数据来完成程序所要解决的任务。 php mysql实例教程_php面向对象实例教程_php项目实例教程 A方案 思考面向对象编程的一种方式是,它将我们的程序分离成多个 "区域"。每个区域都包含一些代码和数据(就像一个程序),并与外部世界和程序内的其他区域有明确的互动关系。 如果我们回顾一下我们使用BeautifulSoup库的链接提取应用,我们可以看到一个通过将不同的对象连接在一起来完成任务的程序。 # 要运行这个,请下载BeautifulSoup的压缩文件 # http://www.py4e.com/code3/bs4.zip # 并将其解压到与此文件相同的目录中 import urllib.request, urllib.parse, urllib.error from bs4 import BeautifulSoup import ssl # 忽略SSL证书错误 ctx = ssl.create_default_context() ctx.check_hostname = False ctx.verify_mode = ssl.CERT_NONE url = input('Enter - ') html = urllib.request.urlopen(url, context=ctx).read() soup = BeautifulSoup(html, 'html.parser') # 检索所有的锚点标签 tags = soup('a') for tag in tags: print(tag.get('href', None)) # 代码:http://www.py4e.com/code3/urllinks.py 我们将 URL 读取为一个字符串,然后将其传递给urllib,以从网络上检索数据。urllib库使用套接字库来进行实际的网络连接以检索数据。我们把urllib返回的字符串交给 BeautifulSoup 进行解析。BeautifulSoup 使用对象html.parser并返回一个对象。我们在返回的对象上调用tags()方法,该方法返回一个标签对象的字典。我们循环浏览这些标签,并为每个标签调用get()方法来打印出href属性。 我们可以画出这个程序的图片,以及这些对象是如何一起工作的。 php mysql实例教程_php面向对象实例教程_php项目实例教程 作为对象网络的程序 这里的关键不是要完美地理解这个程序是如何工作的,而是要看到我们是如何建立一个由相互作用的对象组成的网络,并协调对象之间的信息移动来创建一个程序。同样重要的是,当你在前面几章看那个程序时,你可以完全理解程序中发生的事情,甚至没有意识到这个程序是 "协调对象之间的数据移动"。这只是完成工作的几行代码而已。 对问题进行细分 面向对象的方法的优点之一是它可以隐藏复杂性。例如,虽然我们需要知道如何使用urllib和 BeautifulSoup 的代码,但我们不需要知道这些库的内部如何工作。这使我们能够专注于我们需要解决的那部分问题,而忽略了程序的其他部分。 php mysql实例教程_php面向对象实例教程_php项目实例教程 使用对象时忽略细节 这种只关注程序中我们所关心的部分而忽略其他部分的能力,对我们所使用的对象的开发者也很有帮助。例如,开发BeautifulSoup的程序员不需要知道或关心我们如何检索我们的HTML页面,我们想读取哪些部分,或者我们打算如何处理我们从网页上提取的数据。 php mysql实例教程_php面向对象实例教程_php项目实例教程 构建物体时忽略细节我们的第一个Python对象 在基本层面上,一个对象只是一些代码加上数据结构,比整个程序要小。定义一个函数允许我们存储一点代码并给它一个名字,然后以后用函数的名字调用该代码。 一个对象可以包含一些函数(我们称之为方法),以及被这些函数使用的数据。我们把作为对象一部分的数据项称为属性。 我们使用类的关键字来定义构成每个对象的数据和代码。类的关键字包括类的名称,并开始一个缩进的代码块,在这里我们包括属性(数据)和方法(代码)。 class PartyAnimal: x = 0 def party(self) : self.x = self.x + 1 print("到目前为止",self.x) an = PartyAnimal() an.party() an.party() an.party() PartyAnimal.party(an) # 代码:http://www.py4e.com/code3/party2.py 每个方法看起来像一个函数,以def关键字开始,由一个缩进的代码块组成。这个对象有一个属性(x)和一个方法(party)。这些方法有一个特殊的第一个参数,我们按惯例命名为self。 正如def关键字不会导致函数代码被执行一样,class关键字也不会创建一个对象。相反,class关键字定义了一个模板,表明PartyAnimal类型的每个对象中会包含哪些数据和代码。这个类就像一个饼干切割器,用这个类创建的对象就是饼干。.你不会在饼干刀上涂抹糖霜;你会在饼干上涂抹糖霜,而且你可以在每个饼干上涂抹不同的糖霜。 php mysql实例教程_php面向对象实例教程_php项目实例教程 一个类和两个对象 如果我们继续浏览这个示例程序,我们会看到第一行可执行的代码。 an = PartyAnimal() 这是我们指示 Python 构造 (即,创建) 一个对象或PartyAnimal 类的实例的地方。它看起来像对类本身的一个函数调用。Python 用正确的数据和方法构造对象,并返回对象php面向对象实例教程,然后将其分配给变量an。在某种程度上,这与我们一直在使用的下面这句话很相似。 counts = dict() 这里我们指示 Python 使用dict模板 (已经存在于 Python 中) 构造一个对象,返回 dictionary 的实例,并将其分配给变量counts。 当PartyAnimal类被用来构造一个对象时,变量an被用来指向该对象。我们使用an来访问PartyAnimal类的那个特定实例的代码和数据。 每个Partyanimal对象/实例中都包含一个变量x和一个名为party的方法/函数。我们在这一行中调用party方法。 an.party() 当Party方法被调用时,第一个参数(我们习惯上称之为self)指向PartyAnimal对象的特定实例,而Party是由它调用的。在party方法中,我们可以看到这一行。 self.x = self.x + 1 这种使用点运算符的语法是说'自我内部的x'。每次调用party()时,内部的x值都会增加1,并打印出来。 下面这句话是在一个对象中调用party方法的另一种方式。 PartyAnimal.party(an) 在这个变体中,我们从类中访问代码,并明确地将对象指针an作为第一个参数传递(即方法中的self)。你可以把an.party()看作是上面这一行的简写。 当程序执行时,它产生了以下输出。 到目前为止1 到目前为止2 到目前为止3 到目前为止4 对象被构造出来,party方法被调用四次,都是在一个对象中增加和打印x的值。 作为类型的类 正如我们已经看到的,在Python中所有的变量都有一个类型。我们可以使用内置的dir函数来检查一个变量的能力。我们还可以在我们创建的类中使用type和dir。 class PartyAnimal: x = 0 def party(self) : self.x = self.x + 1 print("到目前为止",self.x) an = PartyAnimal() print ("Type", type(an)) print ("Dir", dir(an)) print ("Type", type(an.x)) print ("Type", type(an.party)) # 代码:http://www.py4e.com/code3/party3.py 当这个程序执行时,它产生以下输出。 类型 <类 '__main__.PartyAnimal'> Dir ['__class__', '__delattr__', ... '__sizeof__', '__str__', '__subclasshook__', '__weakref__', 'party', 'x'] 。 类型 <类 'int'> 类型 <类 '方法'> 你可以看到,使用class关键字,我们已经创建了一个新的类型。从dir输出中,你可以看到对象中的x整数属性和party方法都是可用的。 对象生命周期 在前面的例子中,我们定义了一个类(模板),用这个类来创建这个类的一个实例(对象),然后使用这个实例。当程序结束时,所有的变量都被丢弃了。通常情况下,我们不会过多考虑变量的创建和销毁,但往往随着我们的对象变得越来越复杂,我们需要在对象内部采取一些行动,在对象被构建时设置一些东西,并可能在对象被丢弃时清理一些东西。 如果我们想让我们的对象意识到这些构建和破坏的时刻,我们就向我们的对象添加特别命名的方法。 class PartyAnimal: x = 0 def __init__(self): print('I am constructed') def party(self) : self.x = self.x + 1 print('到目前为止',self.x) def __del__(self): print('我被毁灭了', self.x) an = PartyAnimal() an.party() an.party() an = 42 print('an contains',an) # 代码:http://www.py4e.com/code3/party4.py 当这个程序执行时,它产生以下输出。 我正在建造 到目前为止1 到目前为止 2 我被毁灭了 2 一个包含42 当 Python 构造我们的对象时,它调用我们的__init__方法,让我们有机会为对象设置一些默认值或初始值。当 Python 遇到这一行时。 an = 42 它实际上是 "扔掉了我们的对象",所以它可以重新使用an变量来存储42的值。就在我们的an对象被 "销毁 "的那一刻,我们的析构器代码(__del__)被调用。我们不能阻止我们的变量被销毁,但是我们可以在我们的对象不再存在之前做任何必要的清理工作。 在开发对象时,为对象添加一个构造函数以设置对象的初始值是很常见的。相对来说,需要一个对象的析构器是很罕见的。 多个实例 到目前为止,我们已经定义了一个类,构建了一个对象,使用了这个对象,然后把这个对象扔掉。然而,在面向对象编程中,真正的力量发生在我们构建类的多个实例时。 当我们从我们的类中构造多个对象时,我们可能想为每个对象设置不同的初始值。我们可以向构造函数传递数据,给每个对象一个不同的初始值。 class PartyAnimal: x = 0 名称 = '' def __init__(self, nam): self.name = nam print(self.name,'constructed') def party(self) : self.x = self.x + 1 print(self.name,'party count',self.x) s = PartyAnimal('Sally') j = PartyAnimal('Jim') s.party() j.party() s.party() # 代码:http://www.py4e.com/code3/party5.py 构造函数有一个指向对象实例的自参数,以及在构造对象时传入构造函数的附加参数。 s = PartyAnimal('Sally') 在构造函数中,第二行复制了参数(nam),该参数被传递到对象实例的name属性中。 self.name = nam 程序的输出显示,每个对象(s和j)都包含自己独立的x和nam的副本。 面向对象编程的另一个强大功能是通过扩展一个现有的类来创建一个新的类。当扩展一个类时,我们把原来的类称为父类,把新的类称为子类。 在这个例子中,我们将PartyAnimal类移到自己的文件中。然后,我们可以在一个新文件中 "导入 "PartyAnimal类,并扩展它,如下所示。 从party导入PartyAnimal class CricketFan(PartyAnimal): 点 = 0 def six(self): self.points = self.points + 6 self.party() print(self.name, "points",self.points) s = PartyAnimal("Sally") s.party() j = CricketFan("Jim") j.party() j.six() print(dir(j)) # 代码:http://www.py4e.com/code3/party6.py 当我们定义CricketFan类时,我们表明我们正在扩展PartyAnimal类。这意味着PartyAnimal类中所有的变量(x)和方法(party)都被CricketFan类继承了。例如,在CricketFan类的6个方法中,我们调用PartyAnimal类中的party方法。 随着程序的执行,我们创建了s和j作为PartyAnimal和CricketFan的独立实例。j对象具有超越s对象的额外能力。 摘要 这是一个关于面向对象编程的非常快速的介绍,主要集中在术语和定义及使用对象的语法上。让我们快速回顾一下我们在本章开始时看的代码。在这一点上,你应该完全明白发生了什么事。 stuff = list() stuff.append('python') stuff.append('Chuck') stuff.sort() print (stuff[0]) print (stuff.__getitem__(0)) print (list.__getitem__(stuff,0)) # 代码: http://www.py4e.com/code3/party1.py 第一行构造了一个list 对象。当 Python 创建list对象时,它调用构造方法(名为__init__) 来设置将用于存储 list 数据的内部数据属性。我们没有向构造函数传递任何参数。当构造函数返回时,我们使用变量stuff指向返回的list类的实例。 第二行和第三行调用有一个参数的append方法,通过更新stuff 中的属性php面向对象实例教程,在列表的最后添加一个新的项目。然后在第四行,我们调用没有参数的sort方法,对东西对象中的数据进行排序。 然后我们使用方括号打印出列表中的第一个项目,这是调用stuff 中__getitem__方法的一个快捷方式。这相当于在list 类中调用__getitem__方法,并将stuff对象作为第一个参数,将我们要找的位置作为第二个参数。 在程序结束时,东西对象被丢弃,但不是在调用析构器(名为__del__)之前,以便该对象可以在必要时清理任何松散的东西。 这些是面向对象编程的基础知识。关于在开发大型应用程序和库时如何最好地使用面向对象的方法,还有许多额外的细节,超出了本章的范围。 词汇表 归属一个变量,是一个类的一部分。类一个可以用来构建一个对象的模板。定义了构成该对象的属性和方法。儿童班当父类被扩展时创建的一个新类。子类继承了父类的所有属性和方法。构造函数一个可选的特别命名的方法(__init__),在一个类被用来构造一个对象的时候被调用。通常这是用来设置对象的初始值。毁灭者一个可选的特别命名的方法(__del__),在一个对象被销毁前的那一刻被调用。销毁器很少被使用。遗产当我们通过扩展一个现有的类(父类)来创建一个新的类(子类)。子类拥有父类的所有属性和方法,外加子类定义的额外属性和方法。方法包含在一个类中的函数,以及由该类构建的对象。一些面向对象的模式使用 "消息 "而不是 "方法 "来描述这个概念。对象一个类的构造实例。一个对象包含了该类所定义的所有属性和方法。一些面向对象的文档将 "实例 "一词与 "对象 "互换使用。父类正在被扩展以创建一个新的子类的类。父类将其所有的方法和属性贡献给新的子类。 饼干图片版权CC-BY如果你对列表类的定义感到好奇,可以看看(希望URL不会改变)github.com/python/cpython/blob/master/Objects/listobject.c - 列表类是用一种叫做 "C "的语言编写的。如果你看了一下那段源代码,觉得很好奇,你可能想探索一些计算机科学课程。 (编辑:草根网_南昌站长网 ) 【声明】本站内容均来自网络,其相关言论仅代表作者个人观点,不代表本站立场。若无意侵犯到您的权利,请及时与联系站长删除相关内容! |
推荐文章
站长推荐

浙公网安备 33038102330553号