2.3 优雅地开始使用 Python
我们假设你现在有机会获得IPython,并根据安装说明顺利安装了软件。
2.3.1 Notebook
一旦我们点击“New Notebook”开始使用Notebook,就会出现一个空的Notebook,如下所示。
这个Notebook是交互式的,这意味着它等待你提出要求,提出要做的事情,然后,Notebook就执行这些命令,并在Notebook中给出答案。然后,再次等待你的下一条指令或问题。这就像一台具有计算天赋、不知疲倦的机器人管家。
如果你想做的事情相对复杂,那么将这个问题分解为几个部分比较合理。通过这种方式,你可以相对容易地组织思想,同时也比较容易找到大工程的哪一部分出了问题。对于IPython而言,我们称这些部分为单元格(cell)。如果上述IPython Notebook有一个初始的空单元格,那么你可以看到闪烁的输入插入符号,等待你输入指令。
让我们指示计算机做一些事情吧!我们要求计算机进行两个数相乘的运算,比如说,2乘以3。我们输入“2 * 3”到单元格中,单击运行单元格按钮,这个按钮看起来像一个音频播放按钮。请记住,不要输入引号。计算机可以快速理解你的意思,在屏幕上返回答案,如下所示。
你可以看到正确显示的答案“6”。我们刚刚使用Python对计算机发布了第一条指令,并成功获得了正确的结果。这是我们的第一个计算机程序!Ipython将你的问题标记为“In [1]”,答案标记为“Out [1]”,不要被这个分散注意力。
这只是Python提示你的提问(输入)和它的回答(输出)的方式。
这些数字是你提问的顺序和它回答的顺序,如果你要在Notebook中上下翻阅、调整和补发指令,这些数字有助于你追踪指令。
2.3.2 简单的Python
我们说Python是一种简单的计算机语言,指的就是这个意思。接下来,在标记为“In [ ]”、准备就绪的单元格中输入下面的代码,并点击运行。我们经常使用“代码”这个单词来表示使用计算机语言编写指令。如果你觉得移动指针点击运行按钮太过麻烦,那么请像我这样做,使用键盘快捷键Ctrl-Enter来代替。
print("Hello World!")
你应该得到一个回应,这个回应就是简单地打印出短语“Hello World!”,如下所示。
你可以发现,发出第2条指令打印的“Hello World!”,并没有移除先前指令的单元格和输出的答案。当你从容不迫地构建包含有几个部分的解决方案时,这是大有裨益的。
现在,让我们观察一下输入下面的代码会发生什么,这引入了一个关键的思想。在新的单元格中,输入这些代码并运行。如果没有新的空单元格,那么请单击看起来是个加号、工具提示为“Insert Cell Below”的按钮。
x = 10 print(x) print(x+5) y = x+7 print(y) print(z)
第一行的“x = 10”看起来像数学声明,告诉我们x 等于10。在Python中,这意味着x 被设置为10,也就是说,10被放在了称为x 的虚拟盒子中。这非常简单,如下图所示。
10就待在这个盒子中,直到有更新的通知。由于之前使用过打印指令,因此我们对“print(x)”也不必太过惊讶。这可以打印出x 的值,也就是“10”。为什么这个指令不打印出字母“x”呢?这是因为Python总是尽可能地计算它所能计算的一切事情,x 可以被评估为10,因此Python就打印出了10。下一行“print (x+5)”就是计算x + 5的结果,也就是10 + 5或15,因此,我们预期程序会打印出“15”。
如果按照Python会计算它所能计算的一切这个想法,那么下一项“y = x +7”,你也不难计算得到。老师告诉我们,这是将值赋给标记为y 的新盒子,但是这个值是多少呢?表达式为x + 7,也就是10 + 7或17,因此y 保存了值17,下一行就应该打印出这个值。
如果我们还未像赋值给x和y那样赋值给z,那么执行“print(z)”这行指令,会发生什么呢?我们会礼貌地得到一个错误信息,告知这种方式有错误,尽可能地帮助我们解决这个问题。我必须说,大多数计算机语言有一些错误消息,这些错误消息尽力帮我们解决问题,但不一定能够成功。
下图显示了上述代码的结果,包括礼貌地用于帮助用户的错误消息,即“name z is not defined”。
这些保存了10和17的值、标签为x和y的盒子,我们称之为变量。
在计算机语言中,我们通常使用变量组成一套通用的指令,就像数学家使用“X”和“Y”这样的表达式做出一般性的陈述。
2.3.3 自动化工作
计算机非常适合多次重复执行类似的任务,它们不介意多次重复执行类似的任务,相比于人类使用计算器,它们的速度非常快!让我们来看看,假设让计算机打印出前10个整数的平方,从0的平方开始,然后是1的平方、2的平方等。我们预期可以观察到输出,如0、1、4、9、16、25等。
我们也可以自己进行计算,然后使用一组指令,如“print (0)”“print (1)”“print (4)”等。虽然这也可以成功,但是并未让计算机为我们执行计算任务。更重要的是,我们错过了,拥有一组通用指令集,打印出任何特定数字的平方的机会。要做到这一点,我们需要学会几个新思路,这样就可以很轻松地达到目标了。
在下一个准备就绪的单元格中输入以下代码,然后运行代码。
list( range(10) )
你会得到10个数字的列表,从0到9。我们让计算机执行任务,创建列表,而不需要亲自做这个工作,这真是太棒了。我们是主人,计算机是仆人!
你也许会感到惊讶,列表是从0到9,而不是从1到10。这是由于许多计算机相关的事情是从0开始,而不是从1开始的。我也曾经认为计算机列表是从1开始而不是从0开始的,这种想法多次绊倒了我。在执行计算或多次应用迭代函数时,创建有序列表来计数是非常有帮助的。
你可能已经注意到,我们遗漏了关键字“print”,当我们打印短语“Hello World!”时,使用了这个关键字,但是,在计算2 * 3时,没有使用这个关键字。由于Python知道我们希望看到所发出指令的结果,因此当我们以交互的方式与Python一起工作时,关键字“print”是可选的。
让计算机重复做事情的一种很常见的方式是使用称为循环的代码结构。循环确实给读者带来了一种印象,就是一件事情潜在无止境地来回运行。我们不会去定义循环,而只是简单地演示循环。在一个新的单元格中输入并运行下列代码。
for n in range(10): print(n) pass print("done")
这里有三样新事物,让我们来理解一下。第一行是“range(10)”,它创建了0到9的数字列表,正如我们先前所见到的那样。
“for n in” 创建了一个循环,在这个例子中,它对在列表中的每个数字都做了一些事情,将当前的值赋予变量n来保持计数。我们先前介绍过变量,这就像是在第一个循环期间将0赋值给n,然后是n=1、n=2,直到列表的最后一项n=9。
下一行“print(n)”就是简单地打印n的值。我们预期要打印列表中的所有数字。但是,在“print(n)”之前,应注意缩进。在Python中,缩进的使用是有意识地显示哪些指令在其他指令的管辖之下,因此缩进很重要,在这个例子中,这就是由“for n in...”所创建的循环。“pass”指令标志循环的结束,下一行就回到正常的缩进,不再是循环的一部分了。这意味着“done”只能打印一次,而不是10次。下图显示出了输出,这正如我们所预期的那样。
现在,我们应该很清楚,通过打印“n*n”,可以打印出数字的平方。事实上,我们可以打印出如“The square of 3 is 9”这样的短语,使得输出更有帮助。下面的代码显示了我们对循环内部重复执行的打印指令做了一点调整。请注意,不在引号内的变量都是要进行计算的变量。
for n in range(10): print("The square of", n, "is", n*n) pass print("done")
其结果如下所示。
这已经相当强大!我们挖掘出了计算机的潜力,只使用一组很短的指令就快速地执行大量的工作。我们可以很容易使用range(50),甚至如果我们喜欢,可以使用range(1000),让循环迭代的数字变得很大。你可以尝试一下!
2.3.4 注释
在我们介绍更多更强大的Python命令之前,先来看看以下简单的代码。
# the following prints out the cube of 2 print(2**3)
第一行的开头是哈希符号#。Python将忽略使用哈希符号#开头的行。
这些语句并不是一无是处,我们可以使用这些行,写上有意义的代码注释,使得其他读者对代码更清晰,甚至在以后我们返回来看代码的时候,这对我们也很有帮助。
对代码进行注释,特别对那些相对复杂或不太明显的代码进行注释,相信我,将来你会对此感激不尽的。我曾经多次试图解码自己的代码,经常自问“当时,我是怎么想得……”
2.3.5 函数
之前,在本书的第1章中,我们花了很多时间探讨数学函数。我们将函数视为机器,接受输入,做一些工作,然后弹出输出。这些函数能够经受住考验,可以反复使用。
许多计算机语言,包括Python在内,都尽量使得创建可重用的计算机指令变得容易。就像数学函数,如果你能够足够明确地定义这些函数,这些可重用的代码片段就能独立存在,并且允许你写出更短、更优雅的代码。为什么较短的代码更好呢?这是因为通过函数名称多次调用函数,比多次写出函数内所有的代码要好得多。
我们说“足够明确的定义”是什么意思呢?在这里,这个词的意思是对函数预期输入很清楚,对函数生成的输出也很清楚。一些函数只接受数字作为输入,你不能提供由字母组成的单词给这些函数。
同样,当希望了解函数的简单思想时,最好的办法是观察一个简单的函数,并使用函数做一些有趣的事情。输入以下代码并运行。
# function that takes 2 numbers as input # and outputs their average def avg(x,y): print("first input is", x) print("second input is", y) a = (x + y) / 2.0 print("average is", a) return a
我们来探讨一下在这里所做的事情。Python忽略了以#开头的前两行,这是我们写的注释。下一行“def avg( x,y )”告诉Python,我们要定义一个新的可重用函数。这就是关键字“def”的意思。“avg”这一项是我们给函数的名字。虽然我们可以称函数为“banana(香蕉)”或“pluto(冥王星)”,但是使用有意义的名字,可以提醒我们函数实际上所做的事情,这更为合理。在括号中的(x,y)这一项,告诉Python这个函数有两个输入参数,这两个输入在后面的函数定义内部称为x和y。
一些计算机语言可能要让你明确这是什么类型的对象,但是Python不会要求你这样做,Python只会在你试图滥用变量时,例如将字符当作数字使用或做其他疯狂的事情时,有礼貌地向你抱怨。
现在,我们已经通知了Python,要定义一个函数。我们需要确实告诉它,这个函数是做什么事情的。函数的定义需要缩进,如上面的代码所示。一些语言使用大量的括号,明确表示哪些指令是属于哪一个程序的一部分,然而,Python的设计者认为,众多括号会让人眼花缭乱,难以一一对应,缩进使得了解程序的结构瞬间变得清晰可见,并变得比较轻松。由于人们很难发觉这些缩进,因此也有分歧意见,但是我喜欢采用缩进这个主意!在有时令人讨厌的计算机编程世界中,这是从中走出的一个最佳的人性化理念!由于avg(x,y)函数使用了我们已经明白的内容,因此它的定义非常容易理解。当调用这个函数时,它会打印出函数获得的第一个和第二个数字。
虽然打印出这些数字对求平均值不是必需的,但是我们这样做,是为了让读者真正清楚函数内部发生的事情。下一项计算(x + y)/ 2.0,并将值赋给名为a的变量。同样,我们再次打印出平均值,帮助读者明白代码做了什么事情。最后一条语句是“return a”,这是函数的结尾,告诉Python函数的输出是什么,就像我们之前讨论的机器
一样。
当我们运行这段代码时,它似乎并没有执行任何事情。没有生成任何数字。这是因为我们只定义了函数,却没有使用函数。实际发生的情况是,Python已经记录了这个函数,并让这个函数准备就绪,以供我们想要调用这个函数时使用。
在接下来的单元格中输入“avg(2,4)”来调用这个函数,输入值为2和4。顺便说一句,在计算机编程世界中,我们称之为调用函数(calling a function)。我们会得到所期望的输出,函数会打印出相关的两个输入值和计算的平均值。由于在交互式的Python会话中调用了这个函数,因此会看到这个函数的结果。下面显示了函数定义以及以avg(2,4)和较大的值avg(200,301)调用函数所得到的结果。
你可以使用自己的输入值,运行代码,进行实验。
你可能已经注意到,计算平均值的函数的代码用两个输入值的和除以2.0,而不是2。这是为什么呢?嗯,这是我不喜欢的Python的一个特点。如果使用2,由于Python认为2为整数,因此它会将结果向下调整到最接近的整数。对于avg(2,4),由于6/2等于3,是一个整数,这还不错。但是对于avg(200,301),平均值为501/2,等于250.5,这会向下调整为250。我认为这一切都非常愚蠢,但是如果你的代码不能够完全正确地执行,这值得你思考一番。除以2.0告诉Python,我们坚持使用具有小数部分的数字,而不希望结果向下调整为整数。
让我们祝贺自己定义了一个可重复使用的函数,无论是在数学领域还是在计算机编程领域,这都是最重要、最强大的元素之一。
当我们编码神经网络时,会使用可重用函数。例如,编写一个可重用的函数,计算S激活函数,这样我们就可以多次调用它,这是非常合理的。
2.3.6 数组
数组只是数值表格,它们非常便于使用。就像表格一样,你可以根据行数和列数来指示特定的单元。如果你想起电子表格,那么你应该知道,我们使用B1或C5这种方式来指示单元,这些单元中的值可以用于计算中,如C3 + D7。
当我们要编码神经网络时,将使用数组来表示输入信号、权重和输出信号的矩阵。并且不只是这些,当神经网络内部的信号前馈或误差在神经网络中反向传播时,我们还将使用数组来表示这些信号和误差。因此,让我们一起来熟悉数组。输入以下代码并运行它们。
import numpy
这条命令做些什么呢?import命令告诉Python,从其他地方借助额外的力量,在它的区域中添加新的工具。有时候,这些额外的工具是Python中的一部分,但是这些工具还未准备就绪供大家使用。为了保持Python的精简,只有你要使用一些额外的工具时,Python才携带这些额外的工具。通常,这些额外的工具不是Python的核心部分,而是由其他人创建的,作为有用的附加功能贡献给大家使用。这里,我们引进了额外的一组工具,这组工具被打包为numpy模块。numpy非常受欢迎,这个模块包含了一些有用的工具(如数组)以及使用这些工具进行计算的能力。
在接下来的单元格中,输入以下代码。
a = numpy.zeros( [3,2] ) print(a)
这段代码使用导入的numpy模块,创建了3乘以2的数组,并且将所有单元的值都设置为0,我们将整个数组赋给了名为a的变量。然后,我们打印a。我们可以观察这个数组,这个数组就像是一个3行2列的表格,其中所有单元都为0。
现在,让我们修改数组的内容,将其中一些0更改为其他值。下面的代码演示了如何指定特定单元,使用新值来覆盖旧值。这就像是指定电子表格的单元格或街道地图的网格一样。
a[0,0] = 1 a[0,1] = 2 a[1,0] = 9 a[2,1] = 12 print(a)
第一行代码将零行和零列的单元的值更新为1,无论此前该单元是什么值,都被覆盖了。其他行代码也进行了类似的更新,并且最后一行“print(a)”,打印出了最终结果。下图显示了改变后数组的样子。
现在,既然我们理解了如何设置数组单元格的值,那如何无需打印出整个数组就可以查找数组单元格的值呢?我们已经在这样做了。可以简单地使用表达式,如[1,2]或[2,1],来指定这些单元格,我们可以打印出这些单元格的内容,或将其赋给其他变量。下列代码显示的就是这种操作。
print(a[0,1]) v = a[1,0] print(v)
从输出中可以观察到,第一条打印指令生成了2.0的值,这是在单元格[0,1]中的值。下一个在单元格[1,0]内的值被赋给了变量v,程序打印出了这个变量,我们得到了预期的9.0。
列和行的编号从0开始而不是从1开始的。左上的单元格是[0,0]不是[1,1]。这也意味着右下单元格是[2,1]而不是[3,2]。我总是忘记,在计算机世界上,许多事情从0开始而不是从1开始,因此这有时会让我抓狂。如果我们试图引用[3,2],会得到一个报错消息,告诉我们试图找的单元格并不存在。如果我们混淆了行和列,会得到相同的报错消息。让我们尝试访问不存在的[0,2],看看到底会报告什么错误消息。
在前馈信号或通过网络反向传播误差时,需要进行大量计算,通过使用数组或矩阵,我们可以简化指令,因此数组或矩阵非常有用。在本书第1章中,我们就已经看到了这一点。
2.3.7 绘制数组
就像大型的数字表格或数字列表一样,即使你非常仔细地观察大型的数组,也得不到任何深入的理解。可视化数组有助于我们快速获取数组的一般意义。绘制二维数字数组的一种方式是将它们视为二维平面,根据数组中单元格的值对单元格进行着色。你可以选择如何将单元格中的某个数值转换为某种色彩。
你可以简单地选择根据颜色标度,将数值转换为种颜色,或者将超过某一阈值的单元格涂上黑色,剩余其他的一切单元格都涂为白色。
试着绘制先前创建的小小的3×2数组。
在绘制数组之前,我们需要扩展Python的能力,使其可以绘制图形。我们通过导入其他人编写的额外的Python代码来做到这一点。你可以将这种行为类比为从朋友处借来食谱,放在自己的书架上,这样你的书架有了额外的内容,和以前相比,你能够准备更多种菜肴了。
下面的代码展示了我们如何导入图形绘制功能。
import matplotlib.pyplot
“matplotlib.pyplot”是我们借用的新“食谱”的名字。你可能会遇到类似“import a module” 或“import a library”这样的短语。这里只需写出要导入的额外Python代码的名字。如果你深入使用Python,可能要经常导入额外的功能,通过复用他人开发的有用代码,使生活工作变得容易一些。你甚至可以自己创建有用的代码,与他人分享!
还有一件事要注意,在IPython中,要坚持在Notebook上绘制图形,不要试图在独立的外部窗口中绘制图形。我们发出了这个明确的指令,如下所示:
%matplotlib inline
现在,我们可以绘制数组了。输入以下代码并运行。
matplotlib.pyplot.imshow(a, interpolation="nearest")
创建绘图的指令是imshow(),第一个参数是我们要绘制的数组。
最后一项“interpolation”是告诉Python,不要为了让绘图看起来更加平滑而混合颜色,这是Python为了帮助我们而进行的缺省设置。让我们来看看输出。
真是太精彩了!我们的第一幅图使用颜色显示了3×2的数组。你可以看到,具有相同值的数组单元颜色也相同。稍后,我们将使用相同的imshow()指令来可视化我们馈送到神经网络中的整个数组的值。
IPython包有着各种各样、丰富的可视化数据的工具集。你应该仔细探索这些工具,感受各种可能的绘图方式,甚至可以使用这些工具试着自己绘图。即使是imshow()指令也具有许多绘图选项值得探索,例如,使用不同的调色板。
2.3.8 对象
我们再来学习一个Python的思想,即对象。由于我们只定义对象一次,却可以多次使用对象,因此对象类似于可重用函数。但是,比起简单的函数,对象可以做的事情要多得多。
要了解对象,最简单的方法就是观察它们的应用,而不是学习一大堆抽象的概念。让我们看看下列代码。
# class for a dog object class Dog: # dogs can bark() def bark(self): print("woof!") pass pass
让我们从熟悉的内容开始。你可以看到,在代码内有一个函数名为bark()。如果我们调用这个函数,就很容易看到这个动作,即在屏幕上打印出了“woof!”。这很容易吧!
现在,让我们来看看这个熟悉的函数定义。你可以看到关键字class、名字“Dog”和一个看起来像函数的结构。这与函数的定义相似,也有一个名字。不同之处在于,函数使用关键字“def”来定义,而这里使用“class”来定义对象。
在深入讨论class(类)与对象有何区别之前,让我们再一次看看一些真实而简单的代码,将这些抽象的思想带到生活中。
sizzles = Dog() sizzles.bark()
可以看到第一行代码创建了一个名为sizzles的变量,这个变量看起来来自一个似是而非的函数调用。事实上,Dog()是一个特殊的函数,这个函数创建了所定义的Dog类的一个实例。现在来看看如何从类的定义中创建出实例。我们称这些实例为对象。我们从Dog类的定义中创建了名为sizzles的对象,可以认为这个对象为一条狗(dog)。
下一行代码在sizzles对象上调用了bark()函数。由于我们明白什么是函数,因此对此并不陌生。有点不太熟悉的是所调用的函数bark(),它好像是sizzles对象的一部分。这是因为所有创建自Dog类的对象都有bark()函数。可以在Dog类的定义中看到这个函数。
让我们使用简单的术语来描述发生了什么。我们创建了一种叫做sizzles的狗(Dog)。 sizzles是一个对象,按照狗(Dog)类的形象创建。对象是类的实例。
下图显示了我们迄今所做的事情,也证实了sizzles.bark()确实输出“woof!”
你可能已经发现了函数定义bark(self)中的“self”。这似乎很奇怪,我对此也感到很奇怪。我非常喜欢Python,但是我并不认为Python是完美的。“self”之所以出现在那里,是为了当Python创建函数时,Python能将函数赋予正确的对象。但是我个人的看法是:由于bark()是在类定义的内部,因此Python应该知道这个函数连接到了哪个对象,这是显而易见的。
让我们来看看所使用的对象和类。仔细观察下面代码。
sizzles = Dog() mutley = Dog() sizzles.bark() mutley.bark()
运行这段代码,看看会发生什么。
真有趣!我们创建了名为sizzles和mutley的两个对象。要意识到,重要的一点是这两个对象都创建自Dog()类的定义。这真是太强大了!我们定义了Dog的形象以及它们的行为,然后我们创建了真正的实例。
这就是类和对象之间的区别,类只是定义,对象是所定义类的真正实例。类是菜谱书中的蛋糕配方,对象是按照配方做出的一个蛋糕。下图生动地显示了从类的配方中,如何制作出对象。
这些从类中创建的对象有什么作用呢?为什么要这么麻烦呢?不需要所有额外的代码,直接打印出单词“woof!”,岂不是更简单!
嗯,当你看到所有这些类型的对象都创建自相同的模板时,就会发现这是多么的有用处了。这种方法完全省去了单独创建每个对象的工作。但是,真正的好处源自于对象已经有了整齐封装在内的数据和函数。这种好处是对于人类的程序员而言的。如果代码片段能有组织地围绕着对象这个中心,并且这些代码是自然属于这个对象的,那么这有利于我们更容易地理解相对复杂的问题。狗吠。单击按钮。扬声器发出的声音。打印机打印,或声明缺纸。在许多计算机系统中,我们将按钮、扬声器和打印机表示为对象,并通过这些对象调用函数。
有时候,你会看到对象函数被称为方法(method)。在上述内容中,我们已经这样做了,我们添加了bark()函数到Dog类,使用Dog类创建的sizzles和mutley对象都具有bark()方法。在例子中,你看到它们都bark(吠)了。神经网络需要接受某些输入,进行一些计算并产生输出。我们也知道可以训练神经网络。你可以看到,这些动作、训练和生成的答案,是神经网络的原生函数,即神经网络对象的函数。你应该记得,神经网络内部有数据,也就是链接权重,这些数据本来就是属于神经网络的。这就是我们把神经网络构建为对象的原因。
为了完整起见,让我们看看如何把数据变量添加到类中,并添加一些方法来观察和改变这些数据。仔细观察以下Dog类的新的定义。这里发生了几件事情,让我们一次讨论一件事情。
# class for a dog object class Dog : # initialisation method with internal data def __init__(self, petname, temp) : self.name = petname; self.temperature = temp; # get status def status(self) : print("dog name is ", self.name) print("dog temperature is ", self.temperature) pass # set temperature def setTemperature(self,temp) : self.temperature = temp; pass # dogs can bark() def bark(self) : print("woof!") pass pass
首先要注意的事情是,我们添加了3个新函数到Dog类中。我们已经有了bark()函数,现在又有了新的函数,名为__init __()、status()和setTemperature()。很容易理解添加新函数。如果愿意,也可以添加名为sneeze()的新函数与bark()匹配。
但是,在函数名内部,似乎存在了新函数的变量名。setTemperature函数实际上是setTemperature(self, temp)。__init__函数实际上是__init__(self, petname, temp)。在括号内,这些额外的项是什么?当函数被调用时,它们是函数所期望的变量,称为参数。记得我们先前所看到的求平均值函数avg(x,y)吗?avg()函数的定义明确需要2个数字。因此,__init__()需要一个petname和一个temp,setTemperature()函数只需要一个temp。
现在来看看这些新函数内部。首先,看看古怪的名为__init __()的函数。为什么给它取一个这么古怪的名字呢?这个名字很特别,当第一次创建对象时,Python会调用这个名为__init __()的函数。这对我们非常方便,在实际使用这个对象前,这个函数就做了准备对象这一工作。在这个神奇的初始化函数中,做了什么呢?我们似乎只创建了2个新变量,分别名为self.name和self.temperature。你可以从传递给函数的petname和temp变量中看到它们的值。“self.”部分意味着这个变量是这个对象本身的一部分,因此它有一个“self”。也就是说,这些变量只属于这个对象,而独立于其他Dog对象和Python中的一般变量。我们不希望混淆这只狗的名字与其他狗的名字!如果这听起来很复杂,不要担心,当实际运行一个真实的例子时,这是很容易明白的。
下一个是status()函数,这个函数非常简单。它不带任何参数,只是打印出Dog的对象名和温度变量。
最后是setTemperature()函数,这个函数确实有一个参数。当调用这个函数时,它将self.temperature设置为所提供的temp参数。这意味着,在创建对象之后,在任何时间内你都可以改变对象的温度。只要你喜欢,可以多次改变对象的温度。
我们避免谈及为何所有这些函数(包括bark()函数),都有一个“self”作为第一个参数。这是Python的特点,我觉得这个特点有点令人厌烦,但是这是Python演变的方式。这个参数的作用就是让Python明白,你要定义的函数属于对象,该对象称为“self”。你也许会认为,我们在类中编写函数,这应该是显而易见的吧。这甚至已经在专家级的Python程序员中引起了争论,对此,你不会惊讶吧,很多人都与你一样迷惑不解。
让我们再运作起来,观察这一切,使得这个概念变得清晰。下图显示了使用这些新函数定义的新Dog类以及名为lassie的新的Dog对象,我们使用参数将其命名为“Lassie”,设置初始温度为37,创建了这个对象。
你可以发现,调用Dog对象lassie的status()函数可以打印出狗的名字和它当前的温度。从lassie被创建后,温度就从未被改变过。
现在,让我们改变温度,并通过请求另一个更新的状态,来检查在对象内部温度是否已经被改变了:
lassie.setTemperature(40) lassie.status()
下面显示了结果。
在 lassie对象上调用setTemperature(40),确实改变了对象内部的温度记录。
我们已经学到了很多对象相关的知识,其中有一些知识是高级主题,这些知识一点都不难,我们真的应该为自己感到高兴!我们已经掌握了足够的Python知识,可以开始制作神经网络了。
如果你对这篇内容有疑问,欢迎到本站社区发帖提问 参与讨论,获取更多帮助,或者扫码二维码加入 Web 技术交流群。
绑定邮箱获取回复消息
由于您还没有绑定你的真实邮箱,如果其他用户或者作者回复了您的评论,将不能在第一时间通知您!
发布评论