深度学习:基于Python语言和TensorFlow平台(视频讲解版)
上QQ阅读APP看书,第一时间看更新

2.4 知识背景准备

本节的内容主要是面向不准备学习编程开发而又想了解深度学习的人,或者希望快速学习编写Python简单程序的人。一般来说,理解了本节的知识,至少可以看懂本书后面的所有例子,如果喜欢动手,也可以自己简单编写一些Python程序。当然,还可以运用深度学习的方法解决一些简单问题。

2.4.1 怎样输入Python程序

前面在介绍安装Python的时候,已经介绍过如何用Notepad2-mod这样的文本文件编辑软件来输入和保存Python程序,也介绍了如何用PyCharm这样的专业编程工具来输入和保存Python程序,后面我们还是主要用这两种方法来输入程序。需要特别注意的是,一定要用UTF-8编码来保存Python程序,这在PyCharm中是默认的文件编码,也就是说不需要每次手动设置,而在Notepad2-mod中前面已经介绍过如何把文件的默认编码设置成为UTF-8。另外,注意保存的时候要记得自己保存文件的位置,由于我们后面都要用命令行的形式来执行程序,而命令行中输入带有空格的文件夹名(目录名)不太方便,所以建议保存的文件夹都用不带空格的英文或数字命名。最后,注意所有程序中用到的标点符号如无特殊情况,都是英文的标点,误输了中文标点会导致程序运行错误。

2.4.2 怎样执行Python程序

前面安装Python的介绍中,也介绍了如何启动命令行界面来执行Python程序,后面也会继续用这种方法来执行Python程序。我们一般会说“用命令行执行这个程序”,其实指的是下面几个步骤。

• 用Notepad2-mod或PyCharm保存Python程序文件并记住保存的目录位置。

• 启动命令行界面,无论是Cmder界面还是CMD界面均可。

• 如果需要的话,用“c:”或“d:”命令把工作硬盘切换到C盘或D盘等。

• 用“cd\ml”或“cd\Users\abc\Desktop”等命令把工作目录切换到前面保存Python程序文件的文件夹。

• 可以用“dir”命令来查看文件夹下的文件列表以确认程序文件是否存在。

• 用类似“python test.py”这样的命令来执行Python文件。

那么,我们后面如果说“print(1+2+3)这段代码执行的结果应该是6”,其实指的是下面几个步骤。

• 用Notepad2-mod或PyCharm输入print(1+2+3)这段代码并保存为任意一个由自己命名的程序文件。

• 用命令行方式执行这个Python程序。

• 查看命令行界面中程序的输出结果,与我们预期的结果做对比。

有时候我们要简单测试一两行程序,也可以启动Python的交互式命令行界面,然后直接一行一行输入代码就可以执行。启动Python交互式命令行界面的方法是在命令行界面中,直接执行命令“python”即可。退出Python交互式命令行界面的方法是在其中输入“quit()”命令。所以对于后面简单的例子,我们可以自行选择用保存文件并执行的方式还是用交互式命令行界面中执行的方式来运行。为了方便叙述,我们后面对于一些简单的代码及其运行结果,有时候也会使用类似下面这样直接截取在Python交互式命令行界面中逐行输入代码并获得执行结果的方式来展示。

  >>> a = 16 
  >>> b = 18.9 
  >>> print(a) 
  16 
  >>> print(b) 
  18.9 
  >>>

其中开头带提示符“>>>”的行是我们输入的代码,没有“>>>”开头的行代表是上一条程序执行的结果输出,很好辨认。注意有一些语句执行后是没有输出的,例如a=16这条语句就没有输出。

知道了怎样输入Python程序和怎样执行Python程序并查看结果,我们下面来看看编写Python程序所需要的一些基础知识。

2.4.3 变量

编程中最常用到的概念之一就是变量(variable),由于其存放的内容可以随时改变,所以将其称为“变量”。变量中存放的内容一般有3种:整数、小数和文字。往变量中存内容的过程叫作“给变量赋值”,常见写法如下:

  a = 19

上面这条程序语句(编程中,一般把每一行称为一条语句或一句话,当然也有一行中写多条语句或者一条语句写了几行的情况。注意,自动折行的行还算一行)的意思是,把19这个数字存进a变量中。注意,这里的等号“=”和数学方程式中的等号意思完全不同,数学上的“=”意思是等号两边的数值是相等的,而编程中的“=”则代表把等号右边的数字存到等号左边的变量中,类似于数学证明过程中“令a等于19”这种说法,所以在编程中也经常说“让a等于19”。我们来看看下面的代码与数学意义的区别:

  a = 19 
  a = a + 1 
  print(a)

上述这两行代码执行的结果是:变量a中变成了20这个数。因为我们介绍过变量中存放的内容是可以变的,一开始我们“让a等于19”,然后第二行程序又“让a等于a+1”,这时候其实是把a当时的值加上1再存放到a中,那么这时a中的值实际上就变成了20。在这个例子中,我们也可以看到,给变量赋的值可以是单纯的数字,也可以是一个表达式。如果是表达式,程序执行时会自动把这个表达式计算后的结果赋值给等号左边的变量。

如果我们输入下面代码:

  b = 9.18

那么就表示把9.18这个小数存到命名为b的变量中。刚才介绍过,Python中的变量赋值是区分类型的,主要有整数、小数和文字3种。在这里,我们给b变量赋值时输入了一个小数,Python会自动根据给变量赋的值来确定变量的类型。

如图2.43所示,我们在Python的交互式命令行界面中,输入了上面给a和b赋值的两行代码,又用type(a)来查看变量a的类型,发现其是“int”类型,int即integer(整数)的简写。而用type(b)查看则发现变量b是“float”类型,float就是计算机中对小数的叫法,由于计算机中一般是通过小数的小数点“浮动”来获得更大的数值范围,因此一般叫作“浮点数”。然后我们又定义了一个变量c,让它等于a+b,查看变量c的类型发现也是浮点数,用print函数来输出c的值,看到c中存储的是28.18这个浮点数,也就是a与b的和,与我们的预期相同。

图2.43 赋值语句执行结果

我们再看图2.44中的一段代码。

图2.44 字符串变量赋值执行结果截图

这次我们给变量a赋值的是一段带双引号的文字,这就是Python语言中的文字变量。在编程中,我们一般把文字叫作字符串(string),形象地理解就是“一串字符”,这里我们可以看到用type函数来查看变量a、b、c的类型发现都是“str”类型,也就是string的简写。而字符串也可以用加号来“相加”,但是和数字相加不同,字符串的相加其实是把几个字符串连起来,所以我们用print函数来查看变量c的值时,得到的输出结果是“good”、“morning”和它们之间的一个空格连起来的“good morning”。注意给字符串类型的变量赋值时,都要在字符串前后加上双引号,否则计算机无法判断是变量的名字还是一个字符串。如果字符串中本身含有双引号,要在这个双引号前加上一个反斜杠“\”来避免混淆。

接下来,介绍给变量取名的问题。变量的名称应该都是由英文字母开头的,并且为了避免混淆,建议整个变量名只包含英文字母、数字及下画线符号“_”。变量名中不能含有空格,如果需要表达分隔之意,可以用下画线符号“_”来代替。下面几个变量名的书写是正确的。

  x
  myFirstName
  plan_for_2017

注意其中第二行的写法也是一种常见的变量命名方式,即在一个变量名中含有几个单词时,可以不使用下画线来分隔,而使用英文字母大小写的变化来表示单词间的分界。

下面几个变量名是不正确的。

  12             (变量名不能用数字开头) 
  codeName张三   (变量名不能含有中文) 
  a#6            (变量名不能含有大多数英文字符和数字之外的特殊字符,建议最多只用下画线“_”) 
  this red apple (变量名字中不能有空格)

需要注意的是,系统中对变量名的大小写是“敏感”的,就是说以相同字母命名变量但大小写不同时,Python会认为它们是不同的变量,例如下面几个变量都将被认为是不同的变量。

  theredapple
  TheRedApple
  theRedApple
  THEREDAPPLE

前面3种类型的变量叫作变量的“基本类型”,那么,变量在这几种基本类型上还可组合起来组成“复合类型”。复合类型的变量常用的有数组和字典两种。

• 数组:数组(array),顾名思义就是表示一组数字,如图2.45所示。

图2.45 定义数组

在图2.45所示的命令行中,我们在第一条语句中定义了一个变量a来存放数组,表达数组的方式就是把一组数字用中括号“[]”括起来,数字之间用英文逗号“,”分隔开。执行完语句a=[1,2,3]后,变量a中就保存了一个包含1、2、3这3个数字的数组。

我们想要查看数组中某个具体数字时,要用“a[0]”这种方式,a后面紧跟的中括号内的数字叫作“下标”,代表取第几个数字,计算机中习惯数字从0开始,所以print(a[0])这条语句实际上输出了数组a中的第一个数字1,而print(a[2])这条语句输出了数组a中的最后一个数字,也就是第三个数字3。

如果想要改变数组中某个数字的值,只需直接给带下标的数组变量赋值,例如图2.45中a[2]=5这条语句,就把数组a的第三个数字重新赋值成为5,在接下来的print(a[2])这条语句的输出结果上看,确实已经改变了a[2]的值。

• 字典:数组是通过下标来代表其中某一个数值的,也就是通过数字来索引的;而字典这种变量类型与我们常用的外语字典一样,是通过文字来索引的。下面这个例子,如图2.46所示。

图2.46 字典赋值执行结果

图2.46中,info={"name":"Adam","height":175,"weight":60}这条语句就定义了info这一个字典变量,其中存储了某人的姓名(name)、身高(height)和体重(weight)3个信息。定义字典变量的时候,要用大括号“{}”括起来,所有文字也就是字符串要用双引号括起来,所以其中的name、height、weight这3个文字索引都用双引号括起来了;Adam也是一个字符串,所以也括起来;数字无须括起来;info由于是变量名称,也不需要括起来。索引的字符串和具体的值之间,要用冒号“:”分隔开,两个数值之间再用逗号“,”分开。

引用字典中的某一个值的时候,和数组类似也是用下标的形式,不过这回下标不是数字了,而是改成了字符串,例如图2.46中的print(info["height"])就输出了这个人的身高175。同样的,如果要修改字典中某个值,就用类似于info["height"]=180这样的方法给字典中某个下标赋值。

除了变量以外,编程中实际上还有“常量”的概念,常量可以理解成不变的量。有些计算机语言认为不需要常量,因为用变量就可以代表常量,只是不去改变它就行了。

2.4.4 函数(方法)

我们在前面已经多次用到过print这个函数,函数又叫“方法”(method),但有时候“方法”这个词太不显眼,所以本书中一般还是用“函数”这个说法。函数是我们预先写好的一段完成某个功能的代码,然后给它取个名字来使用它,开发者一般将这种“使用”叫作“调用”。调用print函数的作用,就是把print函数中参数(parameter)的值在命令行界面上输出,例如:

  print(1+2)

这条语句会输出:

  3

函数后面必须带有一个小括号,其中可以有一定数量的“参数”,具体有多少个参数要根据函数需要而定,例如print函数只需要有一个参数就可以完成它的功能;有些函数不需要任何参数,那么只需要有一对空的小括号。

编写函数的目的,一般是因为有一段代码需要经常重复使用,为了避免每次都要重新写一遍的麻烦,就一次性编写好一个函数,以后每次只要“调用”该函数就行了。大多数函数会在执行完毕后输出一个结果,调用这个函数的程序可以用获得的这个结果来做下一步的处理,函数输出结果的过程一般叫作“返回”某个结果,输出的结果一般称为“返回值”,例如前面用到的type(a)这样的函数,就是把其中a这个参数的变量类型输出,一般叫作“返回a的变量类型”;而type函数的返回值是一个字符串类型的变量,代表了传入type函数的参数的变量类型。

Python中已经帮我们编写好了很多类似print这样常用的函数,我们直接用就行了,这些函数叫作Python的“内置函数”。例如,str()就是Python中另一个函数。直接写代码:

  print("我的身高是" + 175)

执行时会出现错误,因为“我的身高是”这是一个字符串,而后面的“175”是一个数字,数字和字符串是无法直接用加号“+”相加的。这时候就可以用函数str()把175这个数字转换成字符串,具体代码:

  print("我的身高是" + str(175))

这样执行语句后就可以得到预期的结果了,如图2.47所示。

图2.47 数字转换为字符串执行结果

图2.47中,第一次执行print("我的身高是"+175)语句时,Python报告出现了错误,而第二次把175作为str函数的参数,str函数就会把175转换成字符串“175”,这样两个字符串相加就是它们相连后的结果了。

我们也可以自行编写函数,本书中因为基本没有用到,所以具体方法暂时不做说明,在最后一章的实例中有自定义函数的示例可供学习。

2.4.5 对象

面向对象(Object Oriented)的编程方法可以说是近几十年来对计算机编程影响最大的方法论。Python语言也受到了这种影响,整个Python 3.x系列版本的语言设计都是基于面向对象的方法的。

所谓“对象”,英语是object,可以理解成一种分类方法,例如对于“人”我们就可以分为“男人”和“女人”两类,那么“男人”就叫作一个“对象类”,“女人”当然也是一个对象类。某一个具体的男人,例如“亚当”,就叫作对象类“男人”的一个“对象实例”,简称“实例”;那么“夏娃”当然就是对象类“女人”的一个实例了。前面的“人”也可以看作是一个对象类,我们一般把它叫作是“男人”和“女人”这两个对象类的“父对象类”,而“男人”和“女人”就是“人”的子对象类。一般来说,子对象类具有父对象类的所有特征,而父对象类不一定有子对象类的所有特征。有时候,我们定义“人”这个对象类只是为了把“男人”和“女人”两个对象类放在一起便于管理,由于“人”对象类包含了“男人”和“女人”两种对象类,所以我们也可以把它叫作一个“包”(package),一个包中可以包含很多个对象类。人们在实际开发中,提到“对象”这个词,有时是指“对象类”,有时是指“对象实例”,需要我们根据上下文来判断。

使用对象来对事物进行分类的目的是:把与事物有关的数据和行为都合并在一个对象类中,这样看起来逻辑更清晰、管理起来更方便。这么说可能比较抽象。实际上我们定义一个对象类,主要是为了把与它有关的变量和函数集中起来管理。我们看一个具体的例子以便更好地理解。在Python交互式环境中输入代码:

  importsys

该代码的作用是导入一个叫作“sys”的对象类,import是“导入”的意思。Python中虽然内置了一些对象(整数、字符串都是对象),但剩余大多数即使是Python安装后自带的对象(或者叫包,一个包有时候可能含有多个对象)也需要在使用前以这种方法来导入。其余第三方提供的包,当然也需要导入。

“sys”是一个包含了与Python语言系统相关的变量和函数的对象类包,以后对这些对象类包都直接称为“对象包”或更简单的“包”。导入sys对象包后,可以如图2.48中这样来使用sys包中已经定义好的变量和函数。

如图2.48所示,我们使用某个对象的变量和函数时,都要在对象名称后面加一个小数点“.”,再写变量或函数的名称,例如sys.version就代表一个变量,里面存放了Python的版本号,可以使用print(sys.version)语句把它输出。而sys.getwindowsversion()则是一个函数,因为其后面有一对空的小括号,说明它是一个不需要任何参数的函数,作用是获取Windows操作系统的内部版本号。最后,我们用了sys对象带有一个参数的函数sys.exit(0),它的作用是从当前程序退出,并把它的参数(这里是0)返回给调用它的上级程序。一般来说,如果我们在命令行界面中执行程序,它的上级程序就是操作系统本身。

图2.48 使用sys包中函数的执行结果

我们一般把对象的变量称为对象的“属性”或“成员变量”,把对象的函数称为对象的“成员方法”或“成员函数”。这也好理解,变量和函数都是这个对象的成员(member)。

最后介绍对象类的成员与对象实例的成员间的区别。前面我们说的成员变量和成员函数其实都是对象类的成员。但有时候,某些成员不是针对对象类的,而是针对对象实例的。下面看看图2.49中的这个例子。

图2.49 对象实例的成员函数

前面说过,数字也是一种对象,那么图2.49中a=1.0这条语句实际上的作用是新建了一个浮点数对象类的实例a,然后把1.0这个数值存放在实例a中,而浮点数的对象实例是可以调用“is_integer()”这个成员函数的,它的作用是判断当前存的数值是否是一个整数(也就是说小数点后面是0),所以我们可以看到图2.49中判断a是一个整数(is_integer函数返回“True”,表示判断成立),而b因为数值是1.2,所以判断它不是一个整数(is_integer函数返回“False”,表示判断不成立)。

在这个例子中可以看到,浮点数作为一个对象类来说,是不会有is_integer()这个成员函数的,因为浮点数是所有的小数的总称,如果不知道具体是哪个数,是无法来判断是否是整数的,也就是无法调用is_integer()这个函数;只有对某个浮点数的实例,也就是说确定了数字是什么,例如上面的a和b都是确定了数值的浮点数的实例,才能调用is_integer()去判断它是否是整数。

另外,由于在Python中所有东西都可以理解成对象,所有类型的变量也都可以理解成存储的对象,所以我们有时候也把具体的某个变量叫作某个对象;当然,变量一般都是对象实例。

如果到这里还能够理解,那么恭喜你已经掌握了面向对象编程的大部分概念。

2.4.6 条件判断与分支

在Python中,我们输入的程序是一行一行的,Python执行程序时,一般也是从上到下一行一行顺序执行的。但也有时候会出现不是这样顺序执行的情况,常见的非顺序执行的情况包括所谓的“条件判断分支”和“循环”。下面我们直接用一个例子来解释什么叫作_x000d_条件判断分支。

  a = 10 
  b = 15 
   
  if a > b: 
        print(a - b) 
  else: 
        print(b - a)

我们输入上面这一段代码时,注意其中有缩进的地方要用一个“Tab”来实现(也就是按一次键盘上的“Tab”键),执行这段代码后得到结果是5,如果让a=15,b=10,得到的结果也是一样的。这就是条件判断分支的作用。代码中从“if”(英语中“如果”的意思)开始是一个条件判断语句,其含义是:如果a>b,那么就执行紧接下来缩进的一段代码,这里是print(a-b),也就是输出a-b的结果数值;否则执行else(英语“否则”的意思)下面缩进的代码,即print(b-a),也就是输出b-a的结果数值。所以整段代码的作用实际上相当于输出了一个a-b的绝对值。注意条件判断语句if和else的结尾,都需要加上一个冒号“:”,这是Python中编写条件判断分支的一个约定。

可以看到,程序从开始顺序执行到“if”开头的语句时,就出现了所谓的“分支”情况,程序会判断一个条件“a是否大于b”,满足条件则走紧接着的程序分支,不满足条件则走“else”下面这个分支,无论走哪个分支,执行完该分支代码后,不会再执行其他分支的程序语句。如果条件判断后还有其他语句,则会继续顺序往下执行。

条件判断分支还有一种情况,看看下面的例子。

  a = 10 
  b = 10 
   
  if a > b: 
        print(a - b) 
  elif a < b: 
        print(b - a) 
  else: 
        print("a=b")

和前一个例子不同的是,多了一个“elif”的判断分支语句,elif是else if的简写,整段条件分支代码的含义是:如果a>b,就执行print(a-b);否则继续进行判断,如果a<b,就执行print(b-a),否则就执行最后这句print("a=b"),也就是当a=b的时候(a既不小于b也不大于b,那就只能等于b)输出“a=b”这一个字符串。

注意,条件分支中可以执行多条语句,多条语句应该在同一个缩进级别上,例如:

  a = 15 
  b = 10 
   
  if a > b: 
        print("a > b") 
        print(a - b) 
  elif a < b: 
        print(b - a) 
  else: 
        print("a=b")

上面这段代码,在满足a>b的条件时,将顺序执行print("a>b")和print(a-b)这两条语句;而这两条语句书写时都要加上一个Tab键控制缩进。

条件判断分支也可以“嵌套”,也就是在某一个分支中,又出现了条件判断的情况,例如刚才的例子可以用嵌套分支的写法,写成这样:

  a = 10 
  b = 10 
  if a > b: 
        print("a > b") 
        print(a - b) 
  else: 
        if a == b: 
              print("a=b") 
      else: 
              print(b - a)

上面这段代码在else这个分支中,又出现了一个条件判断分支的情况,即在前面a不大于b的情况下,再一次判断a是小于b还是等于b,并做出不同的分支处理。这段代码执行的结果和前面的作用是一样的。注意,判断a是否和b相等时,我们使用了“a==b”这样的表达式,也就是说将两个等号连起来用于判断符号左右的两个表达式的值是否相等,这是为了与变量赋值时的单个等号区别开来。如果出现嵌套情况,第二层嵌套的分支语句要多缩进一级,也就是说,像例子中print(b-a)这条语句,前面要加上两个Tab键指令。

另外,Python中规定,代码缩进可以用Tab键,也可以用相同的几个连续空格(一般用两个空格)来代替。但整个程序要统一缩进方式,也就是说,要么全部用Tab键来缩进,要么全部用空格来缩进。我们建议一般用Tab键来缩进会方便一些。

2.4.7 循环

我们在编程中,经常需要重复做一件事情,这时候就要用到“循环”。循环与条件判断分支都会改变程序默认一条一条向下执行的顺序。我们在Python中最常用到的循环就是所谓的“for循环”,例如下面的例子。

  for i in range(5): 
    print(i)

这是一个最简单的for循环。for循环都以“for”这个词开始,range是英语中“范围”的意思,整个这段代码的含义是:“让变量i在0~4这个范围内变化,每变化一次就输出一次变量i的数值”,因为计算机默认从0开始计数,所以range(5)的范围实际上是0到4,而在for循环中默认每次变化的幅度是1,所以在这个循环中,i会分别是0、1、2、3、4这几个数字,也就是说这段代码的实际含义是让print(i)这段缩进的代码重复执行了5次。所以我们以后在看到类似for i in range(5):这样的代码时,没必要想那么多,只需要知道意思是让下面的代码循环执行range函数中参数所表示的数值那么多次就可以了。

这里的变量i一般称为“循环变量”,不成文的惯例是用“i”、“j”、“k”这些字母来命名这些循环变量。

2.4.8 注释

在用Python编程的过程中,可以在任何一行用“#”开始的语句后面写一段注释文字,“#”后面的这些文字不会被程序执行。注释一般用于记录一些信息防止以后遗忘,也常用于向其他人对某条语句或某一段代码做说明。例如:

  h = 175  # h代表身高 
   
  # 下面这句话输出的是标准体重 
  print((h - 80) * 0.7)

上面代码中,“# h代表身高”和“#下面这句话输出的是标准体重”都是正确的注释写法,这些注释均不会被程序执行。经常写一些注释是编写程序中的好习惯。

2.4.9 程序运行时出现错误怎么办

任何编程高手,在写程序的时候也难免出现错误,所以重要的不是避免错误,而是出现错误时如何知道错在哪里,以便进行相应的修改。

下面来看一段只有一句话的程序:

  print(18 + a)

我们执行这个程序会看到图2.50中所示的命令行输出。

图2.50 程序执行出现错误

Python程序在运行时如果发现了错误,就会输出相关的错误信息,其中,一般在最后几行是我们最需要了解的信息。例如这个例子中,我们看到,错误信息显示的是,在“test.py”文件中第1行, print(16+a)这条语句中出现了错误,错误的类型是“NameError”,具体原因是“a”没有被定义过。如果我们是无心之失,马上就可以反应过来。这当然是一种错误,我们只需要对程序做如下修改,就可以让程序顺利运行了。

  a = 6 
  print(18 + a)

2.4.10 本章小结:一段示例代码

至此为止,几乎所有本书中需要用到的编程知识已经介绍完毕了,后面多少会有一些新内容,会在具体讲解实例时逐步引入。下面我们看一段示例代码。虽然是短短几行代码,但已经把这一大节中介绍的所有编程知识包含在其中了,如果能够看懂,说明你已经成功地踏入编程殿堂的大门了。本书中所有主要代码均可以在人邮教育社区(www.ryjiaoyu.com)下载对应的压缩包后获得。

代码2.1 basic.py

  import sys 
   
  Adam_info = {"height": 175, "weight": 60} 
  Eve_info = [165, 50]  #两个数字分别代表夏娃的身高和体重 
   
  if Eve_info[1] > Adam_info["weight"]: 
       print("很遗憾,夏娃的体重现在是" + str(Eve_info[1]) + "公斤。") 
  elif Eve_info[1] == Adam_info["weight"]: 
       print("很遗憾,夏娃的体重和亚当一样。") 
       sys.exit(0) 
  else: 
       print("重要的事儿说3遍!") 
       for i in range(3): 
            print("夏娃没有亚当重,她的体重只有" + str(Eve_info[1]) + "公斤。")