你也能看得懂的Python算法书
上QQ阅读APP看本书,新人免费读10天
设备和账号都新为新人

1.1 变量

变量在计算机语言中,可以用于存储不同的计算结果和不同类型的数据,或表示一个数值。变量是可变的,也就是说,它可以被重复赋值。

在认识变量之前,我们先来了解输入和输出,这样才能对程序输入数据或者看到运行后的结果。

1.1.1 输出和输入

我们以非常经典的一项任务入手,在屏幕上输出一行文字:Hello World!

在Python 3中,实现这个任务只需要一行代码(Python中“#”后方的文字为注释,不影响程序运行,详细信息见1.1.5节最后)。

01  print("Hello World!")       #注意:编写程序时一定要用英文输入法!

运行程序,输出结果为:

Hello World!

在这一行程序中,print()是用于输出的函数,“Hello World!”是输出的数据。其中双引号代表数据是一个字符串。

我们再来看如何输入数据。如果想输入一个数字,把它除以2然后打印出来,该怎么做呢?下面的程序就实现了这个功能。

01  num=int(input("Enter a number:"))  #输入一个数字,用变量num存储

02  num=num/2              #把num除以2

03  print(num)               #输出num的值

运行程序,程序输出如下:

Enter a number:

此时可以输入任意整数。我们输入6,程序输出3后结束。

在程序的第一行中,新建了变量num来存储输入的数据。因为这个变量是用于存储一个数的,所以把它命名为num,这样比较直观易懂。只要遵守变量的命名法则(参见本小节末尾),那么可以随意为变量取名。

在第一行中,input()函数用于输入数据,括号内的字符串是提示语,就是在输入数据之前输出的那一段字符串。如果括号内没有内容(input()),则不输出任何文字,用户直接输入数据即可。而input()函数外面套着的int()函数则是用于把数据转换为int类型的函数。Input()函数返回的值默认为字符串类型(string),字符串不可以进行数学运算。所以,我们使用 int()函数先把字符串转换为整型(int),再进行赋值、运算并输出。数据类型的知识会在下一小节介绍。

程序中的“=”用于给变量赋值,不是相等的意思。“=”左边必须是一个可以被赋值的变量,“=”右边可以是一个常量、变量或函数的返回值等。赋值语句运行结束后,“=”左边的变量的值更新为“=”右边的值。与其他编程语言不同,由于 Python 中变量的类型不固定,所以被赋值的变量的数据类型同样可以被更新,如下面的程序所示。

运行这段程序,输出结果为abcd。

在这一小节中我们了解了输入和输出,也对变量的概念有了初步认识。为了避免程序出错,以下是变量命名的几项法则:

变量名中只能出现字母、数字和下画线,且第一个字符不能是数字。比如,可以将变量命名为name_6,但不能将变量命名为6_name。

不要把在Python中有特殊用途的单词作为变量名,如函数名。比如,目前我们接触过的print函数,如果把print作为一个变量名称,运行程序时又用到print函数,编译器就会报错。

下面就是一个例子:

01  print=0

02  print(print)       #print函数无法被调用,print被系统认为是变量名称了

一个变量名对应一个变量,不会有两个变量有相同的变量名。变量的名字最好和变量的功能相对应,比如一个存储平均数的变量,将它命名为average比命名为abcd要直观,这样不容易记错变量名称而导致程序出错。

1.1.2 简单变量类型

Python语言中有5种标准变量类型:数字、字符串、列表、元组和字典,其中数字和字符串是比较简单的,本小节主要介绍这两种变量类型。

首先,我们来仔细了解一下变量的概念。每个变量都有一个独特的变量名,我们通过这个独特的变量名来调用这个变量的值。就像图1.1中的气球和牌子一样,人够不着气球,但是可以通过扯动牌子来拿到气球。每个牌子都对应一个单独的气球。牌子就相当于变量名,而气球就相当于变量存储的值。

现在我们知道了变量的概念,再来分类型介绍变量。

数字类型的变量是最常用的一种。数字类型还可以再细分,其中主要的两种类型为整型(int)和浮点型(float)。整型变量只能存储整数,浮点型变量则可以存储小数。

图1.1 变量与变量名

例如:

01 num1=10

02 num2=10.0

在这两行程序中,num1表示的值是整数10,num2表示的值是小数10.0。尽管这两个变量所表示的数的大小是相等的,但它们并不是同一种类型的变量。不过,它们的类型是可以相互转换的。

01 num1=10

02 num2=10.99

03 num1=float(num1)

04 num2=int(num2)

05 print(num1,num2)

运行程序,输出结果为:

10.0 10

其中,10.0是输出时num1的值,10是输出时num2的值。在程序中,float函数的作用是把整型变量转换为浮点型变量,而int函数的作用是把浮点型变量转换为整型变量。如果你足够细心,会发现原本值为10.99的num2在转换为整型之后变为了10,它的值改变了。这是因为,在Python中,不存在四舍五入,取整指的是丢弃原值小数点后的部分。

这段程序中还涉及了关于print函数的新语法。我们使用print(num1,num2),在同一行输出两个值,中间用空格隔开。

这就好比用一个盒子搬运东西,本来盒子中有多个格子,每个格子都可以装一件物品。使用print(num1)加上print(num2)就是每次只用其中的一个格子来搬运东西,所以需要分成两次搬运;print(num1,num2)就是同时用两个格子装物品,一次搬运就可以完成。print函数就是一个有无数格子的箱子,只要需要就可以使用,如图1.2所示。

图1.2 print函数与盒子

讲完了新语法,我们再来看看用旧语法输出两个变量时的情况。如果采用分两次print的方法来输出,结果会是两个数分别在两行:

......

05 print(num1)

06 print(num2)

运行程序,输出结果为:

10.0

10

第二种常用的数据类型是字符串,字符串就是一串字符。在Python中,两个单引号或者两个双引号之间的一串字符就是字符串。需要注意的是,如果你使用单引号来表示字符串,那么字符串中不能包含撇号。

01 text1="I'm a programmer."

02 text2='I'm a programmer.'

以上两种字符串定义方式中,第二种是不可行的。系统会认为前单引号和撇号中间的文本I为需要定义的字符串,后面字符则会被判定为非法字符,从而导致程序出错。

如果换一个思路,按照下面的方法来定义,就是可行的了。

01 text1="I'm a programmer."

02 text2='I am a programmer.'

1.1.3 数学计算

在Python中,可以对数字进行加减乘除运算。其中,加对应“+”,减对应“-”,乘对应“*”,除对应“/”。

以下是Python 3中整数运算的程序:

01 print(5+2)

02 print(5-2)

03 print(5*2)

04 print(5/2)

运行程序,输出结果为:

7

3

10

2.5

值得注意的是,在Python 3中,如果整数相除的结果不是整数,Python会把运算的结果存储为浮点型的变量。

我们来看一看浮点数的运算:

01 print(0.3+0.2)

02 print(0.3-0.2)

03 print(0.3*0.2)

04 print(0.3/0.2)

运行程序,输出结果为:

0.5

0.09999999999999998

0.06

1.49999999999999998

在浮点数的运算中有时会出现第二个和第四个运算结果这样的情况。计算机内部表示数字的方式使得有时表示运算结果出现困难,最后输出一个十分近似真实结果的值。所有语言都存在这样的问题,不必担心。

除了简单的加减乘除运算,Python中还有许多内置函数,可以对数字进行更复杂的运算。调用这些函数比自己写出实现这些功能的代码要快得多,从而可以大大提高程序的效率,也可以提高程序的运行速度。下面我们来了解几个常用的数学函数。

pow函数是十分常见的一个函数,通常用于求一个数的n次方。

01 print(pow(4,2))

02 print(pow(4,2,3))

运行这段程序,输出结果为:

16

1

在这段程序中,我们见到了pow函数的两种使用方法。pow函数的使用格式为pow(x,y,z),如果括号内没有填写z,也就是只输入了两个数的话,pow函数的返回值为x的y次方。如果输入了z,那么pow的返回值就是x的y次方除以z的余数,即x**y%z。在Python中,符号“%”是取余的意思,“a%b”就是a除以b的余数。

在这段程序中,第一行输出的是4的2次方,也就是16;第二行输出的是4的2次方除以3的余数,也就是16/3的余数1。

接下来要了解的函数是abs函数和sqrt函数。

01 import math

02 num=-4

03 num=abs(num)

04 print(math.sqrt(num))

运行程序,输出结果为:

2.0

这段程序中使用了用于求绝对值的abs函数和用于求平方根的sqrt函数。我们知道,负数是没有实数根的,所以在开方之前先用abs函数把num变成非负数,再把abs函数返回的值赋给num,从而达到更新num的值的效果。第3行运行结束时num的值为4。

第4行使用sqrt函数来求num的平方根。由于sqrt函数不处于默认模块中,所以要先导入包含sqrt的math模块,使用import语句来导入。调用sqrt函数时,函数名前面要加上“math.”,表示它是math模块内的函数,这样计算机才能找到它。sqrt函数的返回值是浮点型。

Python中还有许多内置函数,在以后的学习和使用中你会逐渐接触到。

1.1.4 位运算

位运算的符号是把变量当作二进制数来进行运算的。

在了解位运算之前,我们先来看一看二进制和我们平时使用的十进制有什么相似和不同之处,为熟练使用位运算打好基础。如果已经对二进制很熟悉,可以跳过这一段,直接进入讲解位运算的部分。

我们先以一个十进制数为例。

十进制数62,它的值由6*10+2*1组成。我们知道,在十进制数中,个位代表10的0次方,十位代表10的1次方,百位代表10的2次方,千位代表10的3次方,依此类推。从右到左,第n个数位上的数字代表着有多少个10的n-1次方相加。对于二进制数来说,这个规律也同样适用,只不过数字从满10进一位变成了满2进一位。从右到左,一个二进制数的第一位代表2的0次方,第二位代表2的1次方,第三位代表2的2次方,第四位代表2的3次方……但是,由于一个数位上的数只要满2就必须进位,所以二进制数只由1和0组成。

以十进制数62为例(如图1.3所示),把它化成一个二进制的数。

图1.3 用十进制表示62

先把运算过程用图1.4表示出来:

图1.4 用二进制表示62

为了方便理解,把得到的二进制的数每4个数位为一组,中间以空格隔开。

62=2*2*2*2*2+2*2*2*2+2*2*2+2*2+2

62=0011 1110

我们看到,62被拆分成多个2的不同次幂的和。每一个幂对应着二进制数的数位上的一个1,而那些没有出现的幂(比如2的0次方),它们的数位上则是0。

所以,二进制数中的1和0也可以被看作“是”和“否”,就像一个开关一样。像图1.5中一样,一个格子就是一盏灯,白色的格子代表灯亮起,亮起的灯就是“是”,代表这个数位上是一个1,也代表计算这个数的值时要加上这个数位。

图1.5 用灯的亮灭表示二进制数

现在,把十进制数转换为二进制数之后,再试着把它转换回原来的十进制数,验证一下是否正确。

明白了二进制数的概念之后,再来了解位运算的各个符号。

第一个符号是按位与运算符&。参与运算的两个值中,只有对应数位上都是1时,那个数位的运算结果才是1,否则运算结果的对应数位是0。

用62和21来做一次与运算,从而更直观地理解按位与运算符。

01  a=62      #a=0011 1110

02  b=21      #b=0001 0101

03  c=a&b     #c=0001 0100

04  print(c)

运行程序,输出结果为:

20

在Python的语法中,我们不能直接定义二进制数,只能定义十进制数,但是进行位运算时,计算机会把十进制数转换为二进制数进行计算,再把二进制的结果转换为十进制数输出。

下面来分析一下这次位运算的过程。

首先,把a和b这两个数换算成二进制数,再把每一个数位对齐。

a : 0011 1110

b : 0001 0101

c : 0001 0100

进行按位与运算后,只有(从右到左的)第三位和第五位上,a和b的值都是1,其他位上要么只有一个1,要么就都是0。所以,结果c中只有第三位和第五位上是1,其他位都是0。把运算结果c换算成十进制数,就是20。

在介绍完位运算具体实现的过程之后,就可以继续了解其他的位运算符号了。

第二个位运算符号是按位或运算符|。它的运算方法和按位与类似,也是对换成二进制的两个数的对应数位进行运算。不同的是,两个数的数位中只要有一个是1,结果就是1。

我们来用运算过程来说明按位或运算符的使用方法:

01  a=62      #a=0011 1110

02  b=21      #b=0001 0101

03  c=a|b     #c=0011 1111

04  print(c)

运行程序,输出结果为:

63

同样把三个数的数位对齐,来看看按位或运算符的操作过程:

a : 0011 1110

b : 0001 0101

c : 0001 1111

只有前两个数位上,两个数都没有1存在,所以最终答案里只有前两位为0。最后,把答案c换算成十进制数,是63。

第三个运算符是按位异或运算符^。这次的运算规则是这样的:对应数位上的数不同时,结果中的数位为1;对应数位上的数相同时,结果中的数位为0。

直接来看一个应用按位异或运算符的例子:

01  a=62      #a=0011 1110

02  b=21      #b=0001 0101

03  c=a^b     #ans=0010 1011

04  print(c)

同样的,还是把三个数的每个数位对齐来观察按位异或运算的规律:

a : 0011 1110

b : 0001 0101

c : 0010 1011

从右到左,第一个、第二个、第四个、第六个数位上,要么是a的数位的值是1、b的数位的值是0,要么是a的数位的值是0,b的数位的值是1。就这样,最终答案就出现了。

最后两个运算符是左移和右移位运算符。左移运算符是<<,右移运算符是>>。这两个运算符的作用,是把一个变量的值化成二进制之后,再把它的整体数位向左移或向右移指定的位数。比如,“a<<2”就是把a的二进制数的数位整体往左移动两位之后再转换回十进制数。如果是往左移,那么被覆盖的高位就消失了,而空出来的低位用0补上。如果是往右移则刚好相反,被覆盖的低位消失了,空出来的高位则变成了0。

还是用图来展示这个过程吧,参见图1.6。

图1.6 二进制数向左移动两位

在图1.6中,上面是原来的二进制数,下面是向左移动两位后的二进制数。可以看到,向左移动两位后,原来是2的5次方到2的1次方这些数位上是1,现在变成了2的7次方到2的3次方这些数位上是1。原本2的7次方和6次方数位上的数被覆盖而消失了。

同时,我们发现,原本在2的0次方数位上的数移动到了2的2次方的数位上。此时,2的1次方和2的0次方就空了出来。用0填补上这些空位。这样,就完成了一次移动。

看完了图1.6之后,再来看一看在程序中该如何实现二进制数的左移和右移。通过下面的例子,你会更直观地了解这两个运算符的作用。

01  a=62      #a=0011 1110

02  b=21      #b=0001 0101

03  print(a<<2)   #a<<2=1111 1000

04  print(b>>1)   #b>>1=0000 1010

运行程序,输出结果为:

248

12

从程序的注释中可以看到a、b以及运算结果转换为二进制数的效果。左移和右移位运算符是可以自行定义不同的移动位数的,所以你也可以尝试设两个数来验证自己对这两个运算符的理解。

1.1.5 使用字符串

可以使用字符串在编程时进行文本处理和判断。本节主要讲解字符串的一些使用和处理方法,涉及一些常用的字符串内建函数。

首先是字符串的拼接。在 Python 中,字符串可以使用“+”进行拼接,就像做数学题的加法一样。很多时候都需要拼接字符串。比如,如果想把日期和星期几拼接起来,就要用到这个方法。下面展示了解决这个问题的一段程序:

01 date="2018.2.16"

02 day="Friday"

03 text=date+" "+day

04 print(text)

运行程序,输出结果为:

2018.2.16 Friday

在程序的第3行中,新建了一个字符串变量text,用于把字符串date和字符串day拼在一起。为了美观,在中间加上一个空格。由此可以直观地得出,字符串的拼接是按从左往右的顺序进行的。

01 date="2018.2.16"

02 day="Friday"

03 print(date+" "+day)

这段程序的输出结果和上一段程序是相同的。

需要注意的是,如果你想在字符串中添加数字,这些数字也必须是字符串类型的变量。

我们来看一个典型的错误例子:

01 age=94

02 text="Age:"

03 combine=text+age

04 print(combine)

其中,age 是一个整型变量,并不是字符串变量,所以运行到第3行拼接字符串的时候程序会出错。正确的做法是把age转换为字符串类型后再拼接:

01 age=94

02 text="Age:"

03 combine=text+str(age)

04 print(combine)

当然,如果换一种思路,不把age转换为字符串也是可以的:

01 age=94

02 text="Age:"

03 print(text,age)

这种方法有一点小缺陷:print函数的括号内用逗号隔开的变量,在输出时中间会有空格。

在字符串的处理中,大小写的转换也是十分实用的一项技术,这是我们需要了解的第二个方面。

首先来了解lower和upper这两个字符串内建函数:

01 name="Adam Smith"

02 print(name.lower())

03 print(name.upper())

运行这段程序,输出结果为:

adam smith

ADAM SMITH

观察这段程序可以发现,lower函数的作用是把字符串中的英文字符全部转换为小写形式,而upper函数的作用刚好相反,把所有英文字母都转换为大写形式。

我们仔细地来研究一下这两个函数,函数有时也称作方法。upper与lower相似,我们只以lower为例,它的格式为:name.lower(),其中,name是字符串的变量名,方法lower()与name中间用一个句点连接。方法是 Python 可以对数据进行的操作。句点使 Python 对句点前的变量执行句点后的方法。一般来讲,方法的执行需要额外的信息,在方法名后面的括号内提供。lower和upper方法不需要额外的信息,所以括号内为空,但是必须保留括号。字符串名+句点+(数据),这是大多数内建函数的格式。

接下来讲解capitalize和title这两个内建函数。来看下面的程序:

01 name="adam smith"

02 print(name.capitalize())

03 print(name.title())

运行程序,输出结果为:

Adam smith

Adam Smith

根据程序的运行结果可以推断出,capitalize的功能是把字符串的第一个字母字符变为大写(如果第一个字符不是字母则不变),title的功能是把字符串内每一个单词的首字母都变为大写,(单词之间由不是字母的字符隔开时,如“good9day”,用title方法操作后变为“Good9Day”)。这两个内建函数的点结构和上面所讲的lower和upper相似。

第三点要讲的是转义字符。为了输出时的美观,常常需要把一串字符分隔开来在不同行输出,或者在不同的字符前添加缩进。如何才能在同一个字符串内实现这些功能而不用多次print呢?

请看下面的程序:

01 text="Python\nC\nJava"

02 print(text)

运行程序,输出结果为:

Python

C

Java

在字符串text中,\n叫作换行符,是Python中转义字符的一种。这代表Python在输出这些字符时,不会输出它们原本的组成字符,而是会输出它们对应的功能,比如\n对应换行。同样,要想在文本中添加缩进也可以使用类似的方法:

01 text="Programming Language:\n\tPython\n\tC\n\tJava"

02 print(text)

运行程序,输出结果为:

Programming Language:

Python

C

Java

其中,往字符串中添加\t就是添加横向制表符,从而达到缩进的效果。

除了上面主要讲述的两种转义字符,Python中还有许多转义字符,如表1.1所示。

表1.1 转义字符表

第四个技巧是如何重复输出字符串。比如,想要重复输出“Python”这个单词三次,每次输出后都换一行。这时可以使用如下方法:

01 text="Python\n"

02 print(text*3)

运行程序,输出结果为:

Python

Python

Python

重复输出字符串的方法就像在做数学中的乘法一样。在合适的时候使用它可以大大提高编写程序的效率。

第五点是编写注释。注释是指存在于程序中但对程序运行效果不产生影响的文字,在程序运行过程中它会被计算机忽略。在编写程序时,如果程序较长或者它的逻辑结构十分复杂,添加注释可以有效地帮助我们看懂程序的结构和作用。妙用注释对于程序员来说有巨大的帮助。

在一行字符前面加上#就可以让这一行出现在#后面的字符全部变成注释。如果需要多行注释,则在每行文字前都加上#即可。

01  n=1+2+3

02  print(n)     #print the value of n

需要注意的是,Python中的注释最好用英文和数字来写。如果在注释中使用中文,则程序在运行过程中有可能出错(即使注释并不是程序要运行的一部分),这取决于编译器的类型和使用的语言。本书中为使大家更好地明白书中要讲解的内容,大部分使用中文编写注释。