Python语法快速入门-运维必备

一、Python介绍

Python语法简洁高效,入门门槛低,且应用广泛。Web、爬虫、人工智能大数据、机器学习、测试运维、数据分析等工作,都需要Python基础。通过这篇博客,可以了解Python基本语法,为后面工作打下一点基础。

1、Python简介

(1)简要介绍

  • 面向对象,解释型的编程语言
  • 吉多 范罗苏姆于1989年发明
  • 使用缩进作为逻辑层次

(2)优点

  • 简单明了、容易上手、功能强大
  • 社区活跃、用户基数大、开发效率高
  • 入门难度低

我觉得他是一门优雅明确简单的语言

那么,有没有缺点呢,当然有啦

(3)缺点

  • 运行效率低
语言类型运行速度代码量
C/C++编译成机器码非常快非常多
Java编译成字节码
Python解释执行

好在现在的CPU性能比较高,显得python的这个缺点不是那么明显

2、Python的发展历史

3、Python的现状

目前python最新的版本应该是3.12,编程语言排行榜如下

再来看看22年的编程语言占比和往年python的趋势

看到这里,那就把python学起来吧,再介绍一下python涉及的领域

python涉及场景
云基础设施DevOps数据分析
后台开发网络爬虫金融行业
运营运维数据挖掘人工智能
...

那么,有哪些网站是使用python来进行开发的呢,比如:豆瓣,知乎,还有著名的云计算平台openstack,还有著名的视频平台YouTube

不擅长的领域

  • 底层应用(驱动等)
  • 移动开发(Android、iOS)
  • 游戏开发

二、Python环境

1、下载安装Python3.8标准环境

我们来安装3.8的标准环境,首先安装运行环境,编辑环境后面通过安装编辑器完成

进入python官网:Welcome to Python.org

点击downloads就可以下载安装了

安装的时候如果不想自己手动配置环境变量,可以勾选自动配置:

安装好后,按下windows+R,输入cmd

输入python,显示版本号就代表安装成功

2、第一个Python程序

把Python环境搭建完成之后,就可以尝试运行Python程序了。运行Python程序有两种方式,第一种是直接通过命令行编写代码运行,第二种是通过编辑器编写代码运行。

对于程序员来说,学习一门新的语言,第一步是通过这门语言向Python问好:“Hello Python”,那么我们也向Python问好吧。

(1)使用终端运行Python程序

  1. 打开命令行窗口(通过快捷键Win+R打开运行窗口,输入cmd即可进入命令行窗口)
  2. 输入python进入python运行环境(正确的python运行环境会有>>>的提示)
  3. 输入你的第一行Python代码print('Hello Python'),回车即可运行

(2)注意点

第一次运行,有几个需要注意的地方:

  • 注意在Hello World前后都有单引号'
  • print>>>不可以有空格
  • 注意(、)'')均为英文字符

你也可以尝试修改单引号里面的内容,输出你喜欢的内容。

(3)使用编辑器运行Python程序

随着Python热门起来,现在越来越多的编辑器支持Python代码的编写了。常见的编辑器包括Sublime Text3Visual CodePyCharm等。

三、Python变量和数据类型

1、Python基础数据类型

计算机顾名思义就是可以做数学计算的机器,因此,计算机程序理所当然地可以处理各种数值。但是,计算机能处理的远不止数值,还可以处理文本图形音频视频网页等各种各样的数据,不同的数据,需要定义不同的数据类型。

学习编程,认识不同编程中的数据类型是必要的,在Python中,能够直接处理的数据类型有以下几种:

(1)整数

整数和我们生活中定义的整数是一个概念,例如:1,2,3,10,100,1000,-1000 等等,都是整数,Python可以处理任意大小的整数。

对于整数,除了生活中常用的十进制以外,计算机由于使用二进制,所以,在计算机中也常用二进制或者十六进制表示整数。相比十进制数,在Python中使用以下方式表示二进制数和十六进制数。

二进制数只使用数字0、1表示,在Python中,二进制整数使用前缀0b表示,比如:0b01100b1100

十六进制数除了0~9十个数字以外,还使用a、b、c、d、e、f,在Python中,十六进制使用前缀0x,比如:0x12ef0xde2431af

十进制数、二进制数和十六进制数之间是可以相互转换的,如果感兴趣,可以百度学习一下。

(2)浮点数

浮点数对应我们生活的是小数,例如:0.1,0.512.343.1415926等,都是浮点数。

为什么小数在计算机领域被称为浮点数呢?这是因为按照科学记数法表示时,一个浮点数的小数点位置是可变的(浮动的),比如,1.23x10^912.3x10^8是相等的,因此称为浮点数。

浮点数可以用数学写法,如1.233.14-9.01。但是对于很大或很小的浮点数,就必须用科学计数法表示,在Python中,把10用e替代,比如:1.23x10^9就是1.23e9,或者12.3e80.000012可以写成1.2e-5。

这里有必要提醒一点就是,整数和浮点数在计算机内部存储的方式是不同的,整数运算永远是精确的,而浮点数运算则可能会有四舍五入的误差,如何检验,可以在Python终端中输入以下内容0.1 + 0.2,看看可以得到你期望的结果0.3吗?自己验证一下。

(3)字符串

字符串对应生活中的就是非数字类型的内容,比如一个句子,一段话,在Python中就是字符串,在Python中,字符串是以''或""括起来的任意文本,比如'abc',"xyz"等等。注意,''或""本身只是一种表示方式,不是字符串的一部分,因此,字符串'abc'只有a,b,c这3个字符。

比如之前练习过的第一个Python程序:print('Hello Python'),其中的Hello Python就是字符串。

(4)布尔值

布尔值对应于生活中的就是,在计算机的世界里,大部分判断都是非错则对的,布尔值和布尔代数的表示完全一致,一个布尔值只有TrueFalse两种值,要么是True,要么是False,在Python中,可以直接用True、False表示布尔值(注意大小写,不需要使用字符串符号括起来),也可以通过布尔运算计算出来。

布尔值可以用andornot运算(注意and,or,not都是Python语言本身的关键字)。

and运算是与运算,只有所有都为 True,and运算结果才是 True。

or运算是或运算,只要其中有一个为 True,or 运算结果就是 True。

not运算是非运算,它是一个单目运算符,把 True 变成 False,False 变成 True。

(5)空值

空值是Python里一个特殊的值,用None表示。

注意,None和0是不一样的,None不能理解为0,因为0是有意义的,比如我有0个苹果表示我没有苹果,0表达的是数值0的意思,而不能表示为我有None个苹果,None是一个特殊的空值。

此外,Python还提供了列表字典等多种数据类型,还允许创建自定义数据类型。

2、Python定义变量的方法

变量不仅可以是数字,还可以是任意数据类型(字符串、布尔值、空值、字典等)。在Python中,定义一个变量需要遵循一定的约束,否则,Python可能识别不出它是一个变量。

  • 变量名由大小写英文字母、数字和下划线_组成
  • 变量不能用数字开头
  • 变量尽量不要和Python关键字重合(比如前面学习过的:and、or、not,否则可能导致Python原有关键字发挥不出作用)

以下这些变量的定义都是合法的。

num, count, _none, min_value

他们都满足上面的三个条件。

以下这些变量的定义都是不合法的。

1num, 666, 1_cd, and

(1)定义变量

定义变量的方式很简单,通过变量名 = 数据,即可定义一个变量。
举个例子:

a = 1

在这个例子里面,a就是变量,它的值是一个整数1。

hello = 'Hello'

在这个例子里面,hello就是变量,它的值是一个字符串'Hello'。
在Python里面,一个变量可以先后存储多种不同类型的数据。

a = 1 # 这个时候a存储的是整数类型
print(a)
a = 'ABC' # 这个时候a存储的是字符串类型
print(a)

这是Python这类语言特有的特性,我们称之为动态语言,与之对应的是静态语言,Python、Javascript等等都是动态语言,Java、C、C++等等属于静态语言。

(2)例子

以下是声明多个变量并打印:

hello = 'Hello'
space = ' '
word = 'Python'
print(hello, space, word)

3、Python的整数于浮点数

在Python中,整数和浮点数虽然属于不同的数值类型,但是在运算上是可以一起运算的,这从生活经验出发,也是可以理解的。

(1)四则运算

整数、浮点数可以直接进行四则运算。

# 加法
num1 = 10
num2 = 0.5
result = num1 + num2
print(result) # ==> 10.5
# 减法
result = num1 - num2
print(result) # ==> 9.5
# 乘法
result = num1 * num2
print(result) # ==> 5.0
# 除法
result = num1 / num2
print(result) # ==>20.0

从上面可以发现一个规律,整数和浮点数运算后 ,得到的结果不管小数点后是否有值,结果都变成浮点数了,这是合理的,浮点数可以表达整数的结果,但是整数不能表达浮点数的结果
注意:在Python2使用除法可能和Python3得到不一样的结果

# python2
num1 = 10
num2 = 3
result = num1 / num2
print(result) # ==> 3
# python3
num1 = 10
num2 = 3
result = num1 / num2
print(result) # ==> 3.3333333333333335

可以看到在python2,得到的是一个整数的结果,这是因为除数和被除数都是整数时,得到的结果也默认保存为整数了,这是非常不科学的,因此在python3,改进了这一点。

(2)取模运算

Python数字支持取模运算,使用百分号%表示取模。

print(3 % 2) # ==> 1
print(33 % 10) # ==> 3
print(99 % 30) # ==> 9

恰当使用取模运算,可以判断一个数是否为偶数,当一个数对2取模结果为0时,则这个数为偶数,否则为奇数。

print(3 % 2) # ==> 1 因此3为奇数
print(33 % 2) # ==> 1 因此33为奇数
print(100 % 2) # ==> 0 因此100为偶数

(3)地板除

Python除了普通除法以外,还有一个特殊的除法被称为地板除,对于地板除,得到的结果会忽略纯小数的部分,得到整数的部分,地板除使用//进行。

10//4 # ==> 2
10//2.5 # ==> 4.0
10//3 # ==> 3

(4)小数点位数

使用Python计算小数的时候,经常需要保留小数点后若干位,可以使用round()函数来处理,这里先了解round的调用方式,使用两个参数,第一个是需要保留小数点位数的数值,第二个是保留的位数。

num = 10 / 3
print(num) # ==> 3.3333333333333335
# 使用round保留两位小数
round(num, 2) # ==> 3.33

(5)例子

一个长方形的长为3.68cm,宽为1.23cm,计算这个长方形的面积,保留小数点后两位。

length = 3.68
width = 1.23
result = round(length*width,2)
print(result)

4、Python的布尔类型

前面我们了解到,布尔类型是Python的基础数据类型,布尔类型只有True和False两种值,来,我们学习布尔类型的集中运算。

(1)与运算

只有两个布尔值都为 True 时,计算结果才为 True。

True and True # ==> True
True and False # ==> False
False and True # ==> False
False and False # ==> False

(2)或运算

只要有一个布尔值为 True,计算结果就是 True。

True or True # ==> True
True or False # ==> True
False or True # ==> True
False or False # ==> False

(3)非运算

把True变为False,或者把False变为True:

not True # ==> False
not False # ==> True

这些运算有什么用呢?计算机程序是由无数的逻辑分支组成的,通过布尔运算,可以在计算机中实现条件判断,根据计算结果为True或者False,计算机可以自动执行不同的后续代码,因此学习布尔运算也是非常有必要的。

在Python中,布尔类型还可以与其他数据类型(字符串,数字等)做 and、or和not运算,看下面的代码:

a = True
print(a and 0 or 99) # ==> 99

得到的计算结果不是布尔类型,而是数字99,这是为什么呢?

因为Python把0、空字符串和None看成False,其他数值和非空字符串都看成True,所以:

True and 0计算结果是0 
继续计算0 or 99计算结果是 99 
因此,结果是99
需要注意的是,not计算的优先级是高于and和or的

True and not False # ==> True

在上述布尔计算中,先计算not False = True,然后再计算True and True,因此得到True的结果。

(4)短路计算

  • 在计算a and b时,如果 a 是 False,则根据与运算法则,整个结果必定为 False,因此返回 a;如果 a 是 True,则整个计算结果必定取决与 b,因此返回 b。
  • 在计算a or b时,如果 a 是 True,则根据或运算法则,整个计算结果必定为 True,因此返回 a;如果 a 是 False,则整个计算结果必定取决于 b,因此返回 b。

所以Python解释器在做布尔运算时,只要能提前确定计算结果,它就不会往后算了,直接返回结果。

(5)例子

运行如下代码,并解释打印的结果

a = 'python'
print('hello,', a or 'world')
b = ''
print('hello,', b or 'world')

运行结果:

('hello,', 'python')
('hello,', 'world')

解释:布尔类型只有两个结果“True”和“False”,其运算过程就是逻辑运算里面的与或非运算,对于或运算,前后值只要有一个值为“True”,运算值就是“True”。需要注意的是在Python中0、None和空字符串均看作是“False,其他值在布尔运算中其值为“Ture”,但是输出的结果是按照原结果输出

5、Python的字符串

(1)字符串表示

字符串可以用' '或者" "括起来表示。

如果字符串本身包含'怎么办?比如我们要表示字符串 I'm OK这时,可以用" "括起来表示:

"I'm OK"

类似的,如果字符串包含",我们就可以用' '括起来表示:

'Learn "Python" at here'

但是,如果字符串既包含'又包含"怎么办?

这个时候,就需要对字符串中的某些特殊字符进行“转义”,Python字符串用\进行转义。

要表示字符串Huhu said "I'm OK"
由于'"会引起歧义,因此,我们在它前面插入一个\表示这是一个普通字符,不代表字符串的起始,因此,这个字符串又可以表示为

'Huhu said \"I\'m OK\".'

注意:转义字符 \不计入字符串的内容中。

常用的转义字符还有:

\n表示换行
\t 表示一个制表符
\\表示 \ 字符本身

(2)例子

在Python中输出以下字符串special string: ', ", \, \\, \n, \t

s = 'special string: \',\",\\,\\\,\\n,\\t'
print(s)

6、Python中raw字符串与多行字符串

(1)raw字符串与多行字符串

如果一个字符串包含很多需要转义的字符,对每一个字符都进行转义会很麻烦。为了避免这种情况,我们可以在字符串前面加个前缀r,表示这是一个 raw 字符串,里面的字符就不需要转义了。例如:

r'\(~_~)/ \(~_~)/'

但是r'...'表示法不能表示多行字符串,也不能表示包含'和 "的字符串。

如果要表示多行字符串,可以用'''...'''表示:

'''Line 1
Line 2
Line 3'''

上面这个字符串的表示方法和下面的是完全一样的:

'Line 1\nLine 2\nLine 3'

还可以在多行字符串前面添加r,把这个多行字符串也变成一个raw字符串:

r'''Python is created by "Guido".
It is free and easy to learn.
Let's start learn Python!'''

(2)例子

把下面的字符串用r'''...'''的形式改写,并用print打印出来:

'\"To be, or not to be\": that is the question.\nWhether it\'s nobler in the mind to suffer.'

 改写:

result = r'''"To be,or not to be":that is the question.
Whether it's nobler in the mind to suffer.'''
print(result)

7、Python的字符串format

(1)format介绍

字符串是Python程序重要的数据类型,到目前为止,我们输出的字符串的内容都是固定的,但有时候通过字符串输出的内容不是固定的,这个时候需要使用format来处理字符串,输出不固定的内容
字符串format由两个部分组成,字符串模板模板数据内容组成,通过大括号{},就可以把模板数据内容嵌到字符串模板对应的位置。

# 字符串模板
template = 'Hello {}'
# 模板数据内容
world = 'World'
result = template.format(world)
print(result) # ==> Hello World

如果模板中{}比较多,则容易错乱,那么在format的时候也可以指定模板数据内容的顺序。

# 指定顺序
template = 'Hello {0}, Hello {1}, Hello {2}, Hello {3}.'
result = template.format('World', 'China', 'Beijing', 'huhu')
print(result) # ==> Hello World, Hello China, Hello Beijing, Hello huhu.
# 调整顺序
template = 'Hello {3}, Hello {2}, Hello {1}, Hello {0}.'
result = template.format('World', 'China', 'Beijing', 'huhu')
print(result) # ==> Hello huhu, Hello Beijing, Hello China, Hello World.

除了使用顺序,还可以指定对应的名字,使得在format过程更加清晰。

# 指定{}的名字w,c,b,i
template = 'Hello {w}, Hello {c}, Hello {b}, Hello {i}.'
world = 'World'
china = 'China'
beijing = 'Beijing'
huhu= 'huhu'
# 指定名字对应的模板数据内容
result = template.format(w = world, c = china, b = beijing, i = huhu)
print(result) # ==> Hello World, Hello China, Hello Beijing, Hello huhu.

(2)例子

使用两种format的方式打印字符串Life is short, you need Python
 

line = 'Life is short, you need Python'
format1 = '{}'
format2 = '{l}'
result1 = format1.format(line)
result2 = format2.format(l = line)
print(result1)
print(result2)

8、Python的字符串编码

在python2中,字符串的编码问题是使用python2必备的知识,到了python3,python3从编程语言的层面就减少了很多编码的问题,但是学习字符串编码还是很有必要的

(1)为什么有编码问题

计算机只能处理数字,如果要处理文本,需要先把文本转换为数字才能处理。最早的计算机在设计时采用8个比特(bit)作为一个字节(byte),所以,一个字节能表示的最大的整数就是255(二进制11111111=十进制255),0 - 255被用来表示大小写英文字母、数字和一些符号,这种编码方式被称为ASCII编码,比如大写字母 A 的编码是65,小写字母 z 的编码是122。
如果要表示中文,显然一个字节是不够的,至少需要两个字节,而且还不能和ASCII编码冲突,所以,中国制定了GB2312编码,用来把中文编进去。
类似的,日文和韩文等其他语言也有这个问题。为了统一所有文字的编码,Unicode应运而生。Unicode把所有语言都统一到一套编码里,这样就不会再有乱码问题了。
Unicode通常用两个字节表示一个字符,原有的英文编码从单字节变成双字节,只需要把高字节全部填为0就可以。

(2)python3的编码

在python3中,默认使用UTF-8 Unicode来进行编码,因此我们可以在python中输入任意形式的Unicode字符串,都不会遇到像python2中遇到的问题(在python2中,需要显式指明该字符串是Unicode字符串),如果没有了解过python2,就不用管了,python3提供了更加简单易懂的编码方式。

s1 = '这是中文字符串'
s2 = 'this is an English string'
print(s1)
print(s2)

在python3中,中文字符串和英文字符串无异

(3)例子

定义并打印中英文混合的字符串 "这是一句中英文混合的Python字符串:Hello World!"

s = '这是一句中英文混合的Python字符串:Hello World!'
print(s)

9、Python的字符串切片

(1)切片介绍

字符串由一个个字符组成,每一个字符都有一个唯一的位置。比如字符串'ABC',第一个字符是A,第二个字符是B,第三个字符是C
因此我们可以使用位置的方式取出字符串中特定位置的字符,按照位置取字符串的方式使用中括号[]访问,这个时候可以把字符串看作是一个列表,不过需要注意的是,在程序的世界中,计数是从0开始的,使用0来表示第一个。

s = 'ABC'
a = s[0] # 第一个
b = s[1] # 第二个
c = s[2] # 第三个
print(a) # ==> A
print(b) # ==> B
print(c) # ==> C

有时候,我们会想获取字符串的一部分(子串),这个时候我们采取切片的方式获取,切片需要在中括号[]中填入两个数字,中间用冒号分开,表示子串的开始位置和结束位置,并且这是半闭半开区间,不包括最后的位置。

ab = s[0:2] # 取字符串s中的第一个字符到第三个字符,不包括第三个字符
print(ab) # ==> AB

我们定义一个更长的字符串,了解切片更多的细节。

s = 'ABCDEFGHIJK'
abcd = s[0:4] # 取字符串s中的第一个字符到第五个字符,不包括第五个字符
print(abcd) # ==> ABCD
cdef = s[2:6] # 取字符串s中的第三个字符到第七个字符,不包括第七个字符
print(cdef) # ==> CDEF

(2)例子

从字符串'AABCDEFGHHIJ'中,使用切片的方式取出'ABCDEFGH'。

aabcdefghhij = 'AABCDEFGHHIG'
abcdefgh = aabcdefghhij[1:9]
print(abcdefgh)

四、Python语言的控制流程

1、if语句

(1)介绍

计算机之所以能做很多自动化的任务,因为它可以自己做条件判断,通过条件判断,选择做什么样的逻辑(当然,逻辑是需要我们提前写好的),我们称之为条件分支判断。
举个例子,在100分试卷的考试中,小于60分我们认为是不及格的,因此,我们可以使用程序自动判断考试是否及格。

score = 59
if score < 60:
    print('抱歉,考试不及格')
# ==> 抱歉,考试不及格

这里,因为score = 59 < 60,所以if的判断是True,因此就会执行print('抱歉,考试不及格')
这里有几个需要注意的地方:

  1. 可以看到print('抱歉,考试不及格')这行代码明显比上一行代码缩进了,这是因为这行代码是if判断的一个子分支,因此需要缩进,在Python规范中,一般使用4个空格作为缩进
  2. 在if语句的最后,有一个冒号:,这是条件分支判断的格式,在最后加入冒号:,表示接下来是分支代码块

(2)例子

如果年龄达到18岁,则是成年人,胡胡的年龄是25岁,使用if语句判断胡胡是否成年,如果成年,输出'adult',并把胡胡的年龄打印出来。

age = 25
if age >= 18:
    print('your age = {}'.format(age))
    print('adult')

2、if-else语句

(1)介绍

使用if判断,可以在当if条件为True时,执行if的子逻辑分支,但有时候,也想在if条件不为True时,执行别的子逻辑分支。
比如:在上个例子中,我希望当分数小于60分时输出,抱歉,考试不及格,否则,输出恭喜你,考试及格,这个时候可以使用if-else语句。

score = 59
if score < 60:
    print('抱歉,考试不及格')
else:
    print('恭喜你,考试及格')

这个时候,程序就会根据score的值,走不同的子逻辑分支,你可以改变score的值试一试。
在这里,同样需要注意两个地方,第一个是冒号:,在else中,同样需要冒号;其次是缩进,在else的子逻辑分支,同样需要缩进。

(2)例子

如果年龄达到18岁,则是成年人,使用if-else语句实现以下逻辑,如果成年,输出'adult',否则,输出'teenager'。

age = 16
if age >= 18:
    print('adult')
else:
    print('teenager')

3、if-elif-else语句

(1)介绍

在100分的考试当中,分数达到或超过90分是顶尖的,达到或超过80分是优秀的,如果想对达到或者超过60分的学生进行不同的分类,使用if-else进行判断,代码如下:

score = 59
if score < 60:
    print('抱歉,考试不及格')
else:
    if score >= 90:
        print('恭喜你,拿到卓越的成绩')
    else:
        if score >= 80:
            print('恭喜你,拿到优秀的成绩')
        else:
            print('恭喜你,考试及格')

在这个程序里,我们做了多次分类,第一次,我们把低于60分和大于等于60分的分开,第二次,我们把大于等于90分和小于90分的分开,第三次,我们把大于等于80分和小于80分的分开。
这样写,我们得到一个两层嵌套的if-else语句,这样的实现可以满足我们的要求,但是如果继续增加条件,比如继续判断高于70分和低于70分的话,那么这个代码将会缩进越来越多,代码逻辑看起来也不够清晰。
我们可以使用if-elif-else语句来简化以上的逻辑。其中elif就是else if的意思。

score = 59
if score < 60:
    print('抱歉,考试不及格')
elif score >= 90:
    print('恭喜你,拿到卓越的成绩')
elif score >= 80:
    print('恭喜你,拿到优秀的成绩')
else:
    print('恭喜你,考试及格')

这样,我们就可以写出代码结构非常清晰的一系列条件判断了。
特别注意: 这一系列条件判断会从上到下依次判断,如果某个判断为 True,执行完对应的代码块,后面的条件判断就直接忽略,不再执行了。
思考以下代码将会输出什么?为什么不输出恭喜你,拿到卓越的成绩

score = 95
if score < 60:
    print('抱歉,考试不及格')
elif score >= 80:
    print('恭喜你,拿到优秀的成绩')
elif score >= 90:
    print('恭喜你,拿到卓越的成绩')
else:
    print('恭喜你,考试及格')

 很显然,这里走到第二个条件就为True了

(2)例子

如果年龄达到18岁,则是成年人,如果年龄6岁到18岁,则是青少年,如果年龄3岁到6岁,则是小孩子,如果年龄在3岁以下,则是婴儿,使用if-elif-else语句实现逻辑,如果成年,输出'adult',如果是青少年,输出'teenager',如果是小孩子,输出kid,如果是婴儿,输出baby。

age = 1
if age >= 18:
    print('adult')
elif age >= 6:
    print('teenager')
elif age >= 3:
    print('kid')
else:
    print('baby')

4、for循环

(1)介绍

for循环在Python语言的流程控制中是非常重要的,在编程中有很多场景需要使用到for循环。举个例子,当我们需要把字符串中的每一个字符打印出来的时候,我们怎么实现呢?
我们当然可以使用之前学习的指定位置的方式让每个字符打印出来。

s = 'ABCD'
print(s[0])
print(s[1])
print(s[2])
print(s[3])

但是,这是字符串s比较短的情况下可以实现的,当s拥有成千上万个字符的时候,难道我们要为此书写上万行代码吗?这个时候for循环就可以派上用场了。

s = 'ABCD'
for ch in s:
    print(ch) # 注意缩进

在上述代码中,ch是在for循环中定义的,意思是把字符串s中的每一个元素依次赋值给ch,然后再把ch打印出来,直到打印出字符串s的最后一个字符为止。

(2)例子

班里考试后,老师要统计几位同学的平均成绩,已知5位同学的成绩用list表示如下:
L = [75, 92, 59, 68, 99]
利用for循环计算出平均成绩。

L = [75, 92, 59, 68, 99]
sum = 0.0
average = 0
for scode in L:
    sum = sum + scode
num = sum/5
print(num)

5、while循环

(1)介绍

和 for 循环不同的另一种循环是 while 循环,while循环可以继续进行下去的条件更加简单,只需要判断while循环的条件是否为True即可,当条件为True时,即继续运行下去。
比如:

while True:
    print(1)

在上述代码中,由于while的条件一直为True,所以这个代码将会无限的输出1,你可不要轻易尝试,这是个死循环。
我们可以把while循环的条件设置得复杂一些,在运行一定次数后,条件可以自动变为False从而跳出while循环。
比如计算1~100的和。

num = 1
sum = 0
while num <= 100:
    sum = sum + num # 注意缩进
    num = num + 1 # 注意缩进
print(sum) # ==> 5050

在上述代码中,while的判断条件是num <= 100,当num小于或者等于100时,循环会继续下去,但当num > 100时,将跳出循环;因为在while循环里面num不断加1,所以num最终会大于100,因此while循环不会无限进行下去。

(2)例子

求出1~10的乘积。

方法一

result = 1
i = 1
while i < 10:
    result = result*(i+1)
    i = i + 1
print(result)

 方法二

num = 1
sum = 1
while num <= 10:
    sum = sum * num
    num = num + 1
print(sum)

 显然方法一更符合情景

6、break跳出循环

(1)介绍

用 for 循环或者 while 循环时,如果要在循环体内直接退出循环,可以使用 break 语句。
比如在前面的无限循环里面,只要在恰当的时机,我们使用break跳出循环,也可以求出1~100的和。

num = 1
sum = 0
while True:
    if num > 100:
        break
    sum = sum + num
    num = num + 1
print(sum)

同样的,对于字符串s = 'ABCDEFGHIJKLMNOPQRSTUVWXYZ',假如希望输出s的前20个字符,而不是所有字符,我们也可以使用break。

s = 'ABCDEFGHIJKLMNOPQRSTUVWXYZ'
num = 1
for ch in s:
    if num > 20:
        break
    print(ch)
    num = num + 1

(2)例子

使用while True和break,计算0~1000以内,所有偶数的和。

num = 0
sum = 0.0
while True:
    if num > 1000:
        break
    if num % 2 == 0:
        sum = sum + num
    num = num + 1
print(sum)

7、continue继续循环

(1)介绍

使用continue,我们可以控制循环继续下去,并跳过continue后面的逻辑,比如,对于字符串s = 'ABCDEFGHIJKLMNOPQRSTUVWXYZ',假如希望输出字符串s中第10个以后的字符,而不是所有字符,这个时候, 我们可以使用continue跳过前面的9个字符。

s = 'ABCDEFGHIJKLMNOPQRSTUVWXYZ'
num = 1
for ch in s:
    if num < 10:
        num = num + 1
        continue # 当num < 10时,跳过后续循环代码,继续下一次循环
    print(ch)
    num = num + 1

(2)例子

综合使用while和continue,计算0~1000以内,所有偶数的和。

num = 0
sum = 0
while num  < 1000:
    num = num + 1
    if num % 2 != 0:
        continue
    sum = sum + num
print(sum)

8、嵌套循环

(1)介绍

就像多层if-else嵌套一样,python的循环也支持嵌套。
我们使用两层嵌套循环输出字符串'ABC'中每个字符和字符串'123'每个字符的排列。

s1 = 'ABC'
s2 = '123'
for x in s1:
    for y in s2:
        print(x + y)

在上述代码中,对于外层循环,外层每循环1次,内层就会循环3次,因此,我们将会得到如下结果:

A1
A2
A3
B1
B2
B3
C1
C2
C3

(2)例子

字符串s1='ABC',字符串s2='123',字符串s3='xyz',输出s1、s2、s3中所有字符的排列。

s1 = 'ABC'
s2 = '123'
s3 = 'xz'
for ch1 in s1:
    for ch2 in s2:
        for ch3 in s3:
            print(ch1 + ch2 +ch3)

五、Python的List容器

1、什么是容器,什么是list

(1)容器介绍

生活中,容器指的是可以容纳物品的收纳器,在程序中,容器是一种可以把多个元素放在一起的数据结构,容器中的元素可以逐个地迭代获取,可以用in, not in等关键字判断某个元素是否包含在容器中。
在Python中,包括列表(list)元组(tuple)字典(dict)集合(set)等,他们都可以放入多个元素,因此都可以算作是容器,这些容器是Python编程中非常重要的数据结构,我们接下来重点学习这些数据结构。

(2)list

列表(list)是一种有序的容器,放入list中的元素,将会按照一定顺序排列。构造list的方法非常简单,使用中括号[]把需要放在容器里面的元素括起来,就定义了一个列表。
比如列出所有同学们的成绩:

scores = [45, 60, 75, 86, 49, 100]

列出所有同学们的名字:

names = ['Alice', 'Bob', 'David', 'Ellena'] # 注意,字符串元素仍需要引号

正如我们看到的,list可以放入数字、字符串等数据类型,list不对放入其中的类型进行判断,也就是说,list可以同时放入任意类型的数据,这是Python这门语言决定的,因为Python是动态语言。

L = ['Alice', 66, 'Bob', True, 'False', 100]

对于list,我们可以直接把list的内容打印出来。

L = ['Alice', 66, 'Bob', True, 'False', 100]
print(L)

(3)例子

Davy同学某次考试语文(Chinese)、数学(Math)、英语(English)三科的成绩分别是92、75、99,使用list保存这些数据。
注意:科目和成绩属于不同的数据类型。

L = ['Alice', 'Chinese', 92, 'Math', 75, 'English', 99]
print(L)

2、按顺序访问list

(1)介绍

列表是有序的,因此我们可以按顺序访问列表中的元素。

L = ['Alice', 66, 'Bob', True, 'False', 100]
for item in L:
    print(item)

回想一下,在前面,我们学习过字符串,这里使用for循环访问列表中的每一个元素和使用for循环访问字符串中的每一个字符是非常类似的。
事实上字符串也可以看作是一种特殊的列表,它只能按顺序存放多个字符。通过for循环访问列表每一个元素的方式,我们称为迭代
而对于一个空的列表,使用for循环访问,将不会打印任何东西。

L = []
for item in L:
    print(item)

(2)例子

使用迭代的方式按顺序输出列表 L = ['Alice', 66, 'Bob', True, 'False', 100] 的偶数位置的元素。

num = 0
L = ['Alice',66,'Bob',True,'False',100]
for item in L:
    num = num + 1
    if num % 2 != 0:
        continue
    print(item)

3、按索引访问list

(1)介绍

由于列表list是一个有序的容器,所以在list里面,每一个元素都有一个唯一的位置,我们称之为索引(index),这和字符串是类似的,因此我们也可以通过位置的方式获取list中的元素,回顾一下前面学习的,我们通过中括号[]来按位置访问对应的元素。
注意,这里要注意区分,列表的定义也是通过中括号[]来定义的,但这和通过索引访问列表里面的元素并不冲突

names = ['Alice', 'Bob', 'David', 'Ellena']
print(names[0])
print(names[1])
print(names[2])
print(names[3])

由于names只有四个元素,所以我们最多只能通过索引3访问到最后一个元素。试想一下,如果我们print(names[4]),会怎么样?

names = ['Alice', 'Bob', 'David', 'Ellena']
print(names[4])

这会引起Python运行的错误,提示索引访问超出范围

Traceback (most recent call last):
  File "<stdin>", line 1, in <module>
IndexError: list index out of range

因此,我们在使用索引的方式访问列表时,一定要特别注意不要越界。
同时,列表和字符串一样,也支持切片,通过切片的方式,获取到列表的子列表。

names = ['Alice', 'Bob', 'David', 'Ellena']
sub_names = names[0:2]
print(sub_names)

这里需要注意一下,如果我们越界切片的话,不会出现Python运行错误,但是按照这样的下标去切片,获取不到任何元素

names = ['Alice', 'Bob', 'David', 'Ellena']
sub_names = names[5:10]
print(sub_names) # ==> []

(2)例子

五名同学的成绩可以用一个list表示:L = [95.5, 85, 59, 66, 72],按照索引的方式分别打印出第一名、第二名、第三名。

L = [95.5,85,59,66,72]
print(L[0])
print(L[1])
print(L[4])

4、倒叙访问list

(1)介绍

Python的列表,除了支持正向顺序索引获取列表中的每一个元素以外,也支持倒序访问list中的每一个元素。

names = ['Alice', 'Bob', 'David', 'Ellena']

对于names列表,Ellena的名字排在最后,也就是我们所说的倒数第一个,在Python中,可以使用-1来表示最后一个元素

names = ['Alice', 'Bob', 'David', 'Ellena']
print(names[-1]) # ==> Ellena

同样的道理,我们可以通过-2,打印出David的名字,通过-3,打印出Bob的位置。

print(names[-2]) # ==> David
print(names[-3]) # ==> Bob

注意,如果我们使用-5的话,因为不存在倒数第五个名字,因此这也是一种越界,同样会报错。

names = ['Alice', 'Bob', 'David', 'Ellena']
print(names[-5])
Traceback (most recent call last):
  File "<stdin>", line 1, in <module>
IndexError: list index out of range

(2)例子

三名同学的成绩可以用一个list表示:L = [95.5, 85, 59, 66, 72],按照倒序索引的方式分别打印出第一名、第二名、第三名。

L = [95.5, 85, 59, 66, 72]
print(L[-5])
print(L[-4])
print(L[-1])

5、向list添加新的元素

(1)append和insert

现在班里面有4名同学:

names = ['Alice', 'Bob', 'David', 'Ellena']

今天,班里转来一名新同学Candy,如何把新同学添加到现有的列表当中呢?
在Python中,list提供了一系列的方法可以让我们操作list中的元素,其中也包含了添加元素的方法。
第一个办法是用append()方法,把新同学追加到列表的末尾:

names = ['Alice', 'Bob', 'David', 'Ellena']
names.append('Candy')
print(names) # ==> ['Alice', 'Bob', 'David', 'Ellena', 'Candy']

注意,append()方法总是将元素添加到list的尾部 
如果上面的列表需要按照首字母排序的话,那么Candy应该是排在第三的位置的,这怎么办呢?
这就需要使用list的insert()方法,insert()方法和append()方法不一样,insert()方法需要两个参数,分别是需要插入的位置,以及需要插入的元素。

names = ['Alice', 'Bob', 'David', 'Ellena']
names.insert(2, 'Candy')
print(names) # ==> ['Alice', 'Bob', 'Candy', 'David', 'Ellena']

注意,将Candy插入到第三的位置之后,原来的名字,都将自动往后移动一位,这个时候再使用相同的索引获取后面的元素,将会得到不一样的结果

names = ['Alice', 'Bob', 'David', 'Ellena']
print(names[2]) # ==> David
names.insert(2, 'Candy')
print(names[2]) # ==>Candy

(2)例子

班上已有同学['Alice', 'Bob', 'Candy', 'David', 'Ellena'],新来报到3名同学分别是'Zero', 'Phoebe', 'Gen',综合利用append()方法,insert()方法,把三个同学的名字按首字母顺序插入到列表里去。

L = ['Alice', 'Bob', 'Candy', 'David', 'Ellena']
L.append('Zero')
L.insert(5,'Phoebe')
L.insert(5,'Gen')
print(L)

6、从list删除元素

(1)pop

如果Ellena因为家庭原因需要转学,那么我们如何把Ellena从已有的列表里面删除呢?
这个时候我们可以使用列表的pop()方法,pop()方法默认删除列表的最后一个元素,并返回。

L = ['Alice', 'Bob', 'Candy', 'David', 'Ellena']
name = L.pop()
print(name) # ==> Ellena
print(L) # ==> L = ['Alice', 'Bob', 'Candy', 'David']

对于Ellena,由于Ellena恰好位于列表的最后,所以可以直接使用pop()方法把Ellena从列表的最后删除,假如需要转学的不是Ellena,而是Candy,这个时候应该怎么办呢?
pop()方法,除了可以删除最后一个元素以外,pop()还可以接收一个参数,指定需要删除的元素的位置。

L = ['Alice', 'Bob', 'Candy', 'David', 'Ellena']
name = L.pop(2)
print(name) # ==> Candy
print(L) # ==> ['Alice', 'Bob', 'David', 'Ellena']

(2)例子

L = ['Alice', 'Bob', 'Candy', 'David', 'Ellena'],由于Candy,David依次转学,某同学写出以下代码,判断以下代码是否可以正常运行?如果不可以,为什么?帮忙修正。

L = ['Alice', 'Bob', 'Candy', 'David', 'Ellena']
L.pop(2)
L.pop(3)
print(L)

 答案是不能,因为删除元素后,后面的元素会自动补充,正确的代码是:

L = ['Alice', 'Bob', 'Candy', 'David', 'Ellena']
L.pop(2)
L.pop(2)
print(L)

7、替换list中的元素

(1)替换方法

对于列表,除了可以向列表添加元素,删除列表元素以外,列表已有的元素,也是可以修改的,通过索引指定位置,并赋值新的元素,即可替换列表中原有的元素。
假如班上同学Candy需要转走了,同时有一个新的同学Canlina转入,那么按照字母排序,Canlina的位置恰好是Candy的位置。

L = ['Alice', 'Bob', 'Candy', 'David', 'Ellena']
L[2] = 'Canlina'
print(L)

我们也可以使用倒序索引来完成同样的功能。

L = ['Alice', 'Bob', 'Candy', 'David', 'Ellena']
L[-3] = 'Canlina'
print(L)

注意,如果替换一个不存在的下标,则同样会引起Python运行错误。

L = ['Alice', 'Bob', 'Candy', 'David', 'Ellena']
L[6] = 'Canlina'
Traceback (most recent call last):
  File "<stdin>", line 1, in <module>
IndexError: list assignment index out of range

(2)例子

班上某次考试,['Alice', 'Bob', 'Candy', 'David', 'Ellena'] 的成绩分别是 89, 72, 88, 79, 99,按照成绩高低,重新排列list中同学名字的顺序。

L = ['Alice', 'Bob', 'Candy', 'David', 'Ellena']
L[0] = 'Ellena'
L[1] = 'Alice'
L[2] = 'Candy'
L[3] = 'David'
L[4] = 'Bob'
print(L)

8、二维list

(1)什么是二维

有时候,一维list并不能满足所有的要求(上述所有list均为一维list),这个时候需要二维list甚至更高维的list。
比如:
Alice最近的三次成绩分别是[100, 89, 92]
Bob最近的三次成绩分别是[70, 65, 81]
Candy最近的三次成绩分别是[88, 72, 77]
如果需要用一个列表存放三个同学的成绩,则需要这样:

alice_scores = [100, 89, 92]
bob_scores = [70, 65, 81]
candy_scores = [88, 72, 77]
all_scores = [alice_scores, bob_scores, candy_scores]
print(all_scores) # ==> [[100, 89, 92], [70, 65, 81], [88, 72, 77]]

这个时候得到的就是一个二维list,对于二维list,列表里面的每一个元素仍然是一个列表。这个时候,如果需要从二维list all_scores获取Bob最近第三次考试的成绩,可以这样写

alice_scores = [100,89,92]
bob_scores = [70,65,81]
candy_scores = [88,72,77]
all_scores = [alice_scores, bob_scores, candy_scores]
score = all_scores[1][2] # ==> 81

其中all_scores[1]得到Bob的最近三次成绩的列表,再通过下标[2],则可以得到Bob第三次的成绩。

(2)例子

有三个长方体,他们的长宽高分别是[1, 2, 3], [5, 3, 2], [7, 3, 2],定义在数组L中,L = [[1, 2, 3], [5, 3, 2], [7, 3, 2]],分别求出三个长方体的表面积。

L = [[1,2,3], [5, 3, 2], [7,3,2]]
for cube in L:
    length = cube[0]
    width = cube[1]
    height = cube[2]
    result = length * width * 2 + width * height * 2 + length * height * 2
    print(result)

六、tuple容器

1、tuple介绍

(1)元组介绍

元组(tuple)和list一样,也是一个有序容器,在元组中,同样可以包含0个或者多个元素,并且也支持索引访问切片等操作。
定义元组的方式是使用小括号()将元组内的元素括起来。

T = ('Alice', 'Bob', 'Candy', 'David', 'Ellena')
# 通过下标的方式访问元素
print(T[0]) # ==> Alice
print(T[4]) # ==> Ellena
# 切片
print(T[1:3]) # ==> ('Bob', 'Candy')

元组数据类型可以把不是元组的容器转换为元组,比如将列表转换成元组。

L = ['Alice', 'Bob', 'Candy', 'David', 'Ellena']
print(L) # ==> ['Alice', 'Bob', 'Candy', 'David', 'Ellena']
T = tuple(L)
print(T) # ==> ('Alice', 'Bob', 'Candy', 'David', 'Ellena')

同样的,对于列表数据类型,也可以把元组转换成列表。

T = ('Alice', 'Bob', 'Candy', 'David', 'Ellena')
print(T) # ==> ('Alice', 'Bob', 'Candy', 'David', 'Ellena')
L = list(T)
print(L) # ==> ['Alice', 'Bob', 'Candy', 'David', 'Ellena']

但是,tuple和list不一样的是,tuple是固定不变的,一旦变成tuple,tuple中的每一个元素都不可被改变,同时也不能再往tuple中添加数据,而list是可以的。

T = ('Alice', 'Bob', 'Candy', 'David', 'Ellena')
# 替换元素
T[1] = 'Boby'
# 报错
Traceback (most recent call last):
  File "<stdin>", line 1, in <module>
TypeError: 'tuple' object does not support item assignment

注意,元组(tuple)的这个特性是非常重要的,在运行上tuple的性能是list的数倍

(2)例子

创建一个tuple,顺序包含0~9的十个数,并尝试将其转换为列表list。

T = (0,1,2,3,4,5,6,7,8,9)
print(T)
L = list(T)
print(L)

2、访问tuple元素的其他方法

由于tuple一旦定义之后便不可修改,所以在实际编程中,tuple经常用于存放固定不变的数据
因此在使用上,tuple提供了便捷的方法可以访问tuple中的数据。

(1)count()方法

count()方法用来统计tuple中某个元素出现的次数。

T = (1, 1, 2, 2, 3, 3, 1, 3, 5, 7, 9)
print(T.count(1)) # ==> 3
print(T.count(5)) # ==> 1

对于不存在的元素,count方法不会报错,而是返回0,这是合理的,因为元组里面有0个不存在的元素。

T = (1, 1, 2, 2, 3, 3, 1, 3, 5, 7, 9)
print(T.count(10)) # ==> 0

(2)index()方法

index()方法可以返回指定元素的下标,当一个元素多次重复出现时,则返回第一次出现的下标位置。

T = (1, 1, 2, 2, 3, 3, 1, 3, 5, 7, 9)
T.index(9) # ==> 10
T.index(5) # ==> 8
T.index(1) # ==> 0 # 多次出现,返回第一次出现的位置

注意,index()方法和count()方法不一样,当指定的元素不存在时,使用index()方法Python会报错。

T = (1, 1, 2, 2, 3, 3, 1, 3, 5, 7, 9)
T.index(100)
# 报错
Traceback (most recent call last):
  File "<stdin>", line 1, in <module>
ValueError: tuple.index(x): x not in tuple

(3)例子

在某次考试中,班上的成绩如下:(100, 69, 29, 100, 72, 99, 98, 100, 75, 100, 100, 42, 88, 100),统计满分(100分)同学的数量。

scores = (100, 69, 29, 100, 72, 99, 98, 100, 75, 100, 100, 42, 88, 100)
num = scores.count(100)
print(num)

3、创建单个元素的tuple

(1)介绍

tuple和list一样,可以包含 0 个、1个和任意多个元素。
包含多个元素的 tuple,前面我们已经创建过了。

包含 0 个元素的 tuple,也就是空tuple,直接用()表示:

T = ()
print(T) # ==> ()

接着,我们创建包含一个元素的tuple。

T = (1)
print(T) # ==> 1

这和我们期望的输出有些差异,为什么包含一个元素的元组打印出来之后没有小括号,而是只有一个数字1呢?
回顾一下前面数字的四则运算。

result = 3 * 4 - 2
print(result) # ==> 10
# 改变优先级,先运算减法
result = 3 * (4 - 2)
print(result) # ==> 6

可以看到,改变优先级我们是通过()来实现的,这和元组的定义有冲突,这就解释了前面只有一个元素的元组,为什么打印出来却得到一个数字的结果了。
因为()既可以表示tuple,又可以作为括号表示运算时的优先级,结果(1)被Python解释器计算出结果 1,导致我们得到的不是tuple,而是整数 1。
因此,要定义只有一个元素的tuple,需要在元素后面添加一个逗号,

T = (1, )
print(T) # ==> (1, )

而对于多个元素的tuple,则加和不加这个逗号,效果是一样的。

>>> T = (1, 2, 3,)
>>> print(T) # ==> (1, 2, 3)

(2)例子

tuple的元素也可以是tuple,判断以下定义T的元素中,有多少个tuple。

T = ((1+2),  ((1+2),), ('a'+'b'), (1, ), (1,2,3,4,5))
T = ((1+2),((1+2),),('a'+'b'),(1,),(1,2,3,4,5))
print(T) # ==>(3, (3,), 'ab', (1,), (1, 2, 3, 4, 5))

 第一个不是,第二个是,第三个不是,第四个和第五个是,因为第一个元素(1+2),第三个元素('a'+'b'),计算的结果分别是数字3和字符串ab,不是tuple,所以总共是3个tuple。

4、可变tuple

(1)介绍

前面我们学习了,对于tuple,它和list一个最大的不同点就是tuple是不可变的,tuple里面的元素,也是不可替换的。但是这针对的是仅包含基础数据类型(数字类型、布尔类型、字符串类型)的数据,对于组合数据类型,则不受这个约束。

T = (1, 'CH', [3, 4])

这里T有三个元素,第一个元素是数字类型,第二个元素是字符串类型,第三个元素是列表类型的,我们尝试修改第三个元素的数据。

T = (1, 'CH', [3, 4])
L = T[2]
print(L) # ==> [3, 4]
# 尝试替换L中的元素
L[1] = 40
print(L) # ==> [3, 40]
print(T) # ==> (1, 'CH', [3, 40])

这个时候,我们发现,元组T中的第三个元素已经成功被改变了,这就有悖前面的定义,元组是不可改变的。那么这到底是为什么呢?
这是因为虽然tuple中的list元素改变了,但是tuple本身指向的list仍然是同一个list,list本身并没有改变,改变的只是list里面的一个元素,这是tuple所约束不到的范围。

T = (1, 'CH', [3, 4])
L2 = [3, 40]
# 尝试替换tuple中的list
T[2] = L2
# 报错
Traceback (most recent call last):
  File "<stdin>", line 1, in <module>
TypeError: 'tuple' object does not support item assignment

如果我们直接替换list,这也是不行的。

(2)例子

T = (1, 'CH', [3, 4])
tuple里面包含list,导致tuple是可变的,修改T的定义,不改变基本数据类型的数据,使T是不可变的。

T = (1, 'CH', (3, 4))
print(T)

把list换为元组就不可变了

七、Dict容器

1、什么是dict

(1)字典介绍

我们已经知道,List 和 tuple 可以用来表示顺序集合,例如,班里同学的名字:

['Alice', 'Bob', 'Candy', 'David', 'Ellena'] # List
('Alice', 'Bob', 'Candy', 'David', 'Ellena') # tuple

或者考试的成绩:

[45, 60, 75, 86, 49] # list
(45, 60, 75, 86, 49) # tuple

如果同学名字的列表和同学成绩的列表是一一对应的,那么通过下标,我们也可以找到每个同学的成绩。

names = ['Alice', 'Bob', 'Candy', 'David', 'Ellena']
scores = [45, 60, 75, 86, 49]
index = 0
for name in names:
    score = scores[index]
    print('name = {}, score = {}'.format(name, score))
    index = index + 1

事实上,我们可以得到这样的映射。

'Alice' ==> 45
'Bob' ==> 60
'Candy' ==> 75
'David' ==> 86
'Ellena' ==> 49

但是使用两个list,始终有些麻烦的,尤其是需要变换一个列表的顺序后,另外一个列表也需要做同样的变换,否则就可能出现对应不上的问题。
python的dict就是专门保存这种映射的,使用dict可以方便的保存“名字”->“成绩”的映射。
在dict中,每一项包含一个key和一个valuekey和value是一一对应的,在解决上面的问题中,我们可以使用名字作为key,成绩作为value,那么dict的定义如下:

d = {
    'Alice': 45,
    'Bob': 60,
    'Candy': 75,
    'David': 86,
    'Ellena': 49
}

在定义里,我们使用花括号{}表示这是一个dict,然后key和value之间使用冒号:分割,并且每一组key:value的最后,以逗号,表示这一组的结束。
我们也可以使用以下的方式定义一个dict。

d = dict()
print(d) # ==> {}

不过这种定义方式,默认得到的是一个空dict,需要调用函数往里面添加数据,我们后面会继续学习。

(2)例子

新来的Gaven同学成绩是86,编写一个dict,把Gaven同学的成绩也加进去。

d = {
    'Alice': 45,
    'Bob': 60,
    'Candy': 75,
    'David': 86,
    'Ellena': 49
}

 新的字典:

d = {
    'Alice': 45,
    'Bob': 60,
    'Candy': 75,
    'David': 86,
    'Ellena': 49,
    'Gaven': 86
}
print(d)

2、读取dict元素

(1)介绍

我们现在可以创建一个dict,保存名字和成绩的对应关系。

d = {
    'Alice': 45,
    'Bob': 60,
    'Candy': 75,
    'David': 86,
    'Ellena': 49,
    'Gaven': 86
}

此时,如果想通过名字来查询某个同学的成绩,也就是通过key来查询value,这个时候怎么办呢?
dict提供通过key找到对应value的功能,通过d[key]的形式,就可以得到对应的value。

d = {
    'Alice': 45,
    'Bob': 60,
    'Candy': 75,
    'David': 86,
    'Ellena': 49,
    'Gaven': 86
}
print(d['Bob']) # ==> 60
print(d['Alice']) # ==> 45

这和list通过下标找到对应位置的元素是类似的。
回顾一下前面使用下标的方式访问list元素的时候,当下标不存在时,就会引发错误,在dict中,也是一样的,当对应的key不存在时,也会引发错误。

d = {
    'Alice': 45,
    'Bob': 60,
    'Candy': 75,
    'David': 86,
    'Ellena': 49,
    'Gaven': 86
}
print(d['Dodo'])
# 抛出异常
Traceback (most recent call last):
  File "<stdin>", line 1, in <module>
KeyError: 'Dodo'

它的意思是key不存在,因此我们在需要通过key找到value时,一定要先判断key存不存在,然后才使用上面的方式获取对应的value,以避免错误。

if 'Alice' in d:
    print(d['Alice']) # ==> 45
if 'Dodo' in d: # Dodo不存在,所以不会走下面的逻辑
    print(d['Dodo'])

除了使用这种方法,还有一种方法可以通过key来获取对应的value,这种方法不会引起错误,dict本身提供get方法,把key当作参数传递给get方法,就可以获取对应的value,当key不存在时,也不会报错,而是返回None。

print(d.get('Alice')) # ==> 45
print(d.get('Dodo')) # ==> None

因为通过get方法在代码实现上更加简单,且不会引起错误,因此更加推荐使用get方法来获取dict的元素

(2)例子

根据如下dict,打印出Alice, Bob, Candy, Mimi, David的成绩,当同学不存在时,打印None。

d = {
    'Alice': 45,
    'Bob': 60,
    'Candy': 75,
    'David': 86,
    'Ellena': 49
}

 读取代码如下:

d = {
    'Alice': 45,
    'Bob': 60,
    'Candy': 75,
    'David': 86,
    'Ellena': 49
}
print(d.get('Alice'))
print(d.get('Bob'))
print(d.get('Candy'))
print(d.get('Mimi'))
print(d.get('David'))

3、添加dict元素

(1)介绍

dict和tuple不一样,dict是可变的,我们随时可以往dict中添加新的key-value,比如对于上节课的成绩dict:

d = {
    'Alice': 45,
    'Bob': 60,
    'Candy': 75,
    'David': 86,
    'Ellena': 49
}

需要往里面添加Dodo、Mimi的成绩时,可以使用赋值语句往里面添加元素

d['Mimi'] = 72
d['Dodo'] = 88
print(d)

实际上,value可以是任意类型的元素,可以是list、tuple等,假如Mimi近两次成绩分别是72,73,Dodo近两次的成绩分别是88,90,则可以使用赋值语句往dict中添加list元素。

d['Mimi'] = [72, 73]
d['Dodo'] = [88, 90]
print(d)

此后,如果Mimi、Dodo的第三次成绩也出来了,分别是75,90,则可以先通过key把对应的value查询出来,然后再往类型是list的value中添加第三次的成绩。

d['Mimi'].append(75)
d['Dodo'].append(90)
print(d)

(2)例子

已有同学的某次成绩dict如下:

d = {
    'Alice': [45],
    'Bob': [60],
    'Candy': [75],
}

Alice、Bob、Candy的最近三次的成绩分别是[50, 61, 66],[80, 61, 66],[88, 75, 90],更新dict,使得dict可以保存同学多次的成绩。

d = dict()
d['Alice'] = []
d['Bob'] = []
d['Candy'] = []
d['Alice'].append(50)
d['Alice'].append(61)
d['Alice'].append(66)
d['Bob'].append(80)
d['Bob'].append(61)
d['Bob'].append(66)
d['Candy'].append(88)
d['Candy'].append(75)
d['Candy'].append(90)
print(d)

4、更新dict元素

(1)介绍

上一节我们学习了往dict中添加元素的方法,通过赋值语句就可以把元素添加到dict中去,但是赋值的时候,我们并不确定key是否已经在dict里面了,如果dict里面已经有对应的key了,那将会发生什么呢?

d = {
    'Alice': 45,
    'Bob': 60,
    'Candy': 75,
    'David': 86,
    'Ellena': 49
}
d['Bob'] = 75
print(d)
# ==> {'Alice': 45, 'Bob': 75, 'Candy': 75, 'David': 86, 'Ellena': 49}

这个时候我们发现,原来Bob的成绩是60,现在变成75了,因为d['Bob'] = 75的缘故。
因此我们发现这个赋值语句其实有两个功能:

  1. 当key不存在时,往dict中添加对应的key: value元素。
  2. 当key存在时,会更新dict,用新的value替换原来的value。

因此,在使用赋值语句往dict中添加元素时,为了避免不必要的覆盖问题,我们需要先判断key是否存在,然后再做更新。(使用前先判断)

(2)例子

已有同学们的成绩如下,更新Alice的成绩为60,并把旧的成绩记录下来。

d = {
    'Alice': 45,
    'Bob': 60,
    'Candy': 75,
    'David': 86,
    'Ellena': 49
}

 代码如下:

d = {
    'Alice': 45,
    'Bob': 60,
    'Candy': 75,
    'David': 86,
    'Ellena': 49
}
old_score = d.get('Alice')
d['Alice'] = 60
print(old_score)
print(d)

5、删除dict元素

(1)介绍

当同学转校时,我们需要把同学的成绩从已有的成绩dict中删除,这个时候我们就需要学习如何删除dict中的元素。
dict提供便捷的pop()方法,允许我们快速删除元素,pop()方法需要指定需要删除的元素的key,并返回对应的value。
假设Alice转校了,需要把Alice的成绩删除,可以这样写:

d = {
    'Alice': 45,
    'Bob': 60,
    'Candy': 75,
    'David': 86,
    'Ellena': 49
}
print(d) # ==> {'Alice': 45, 'Bob': 60, 'Candy': 75, 'David': 86, 'Ellena': 49}
alice_score= d.pop('Alice')
print(alice_score) # ==> 45
print(d) # ==> {'Bob': 60, 'Candy': 75, 'David': 86, 'Ellena': 49}

需要注意的是,pop()方法的参数是dict中的key,当key不存在时,同样会引起错误。比如在上述操作中,已经把Alice的成绩删除了,假如再次pop('Alice'),将会引发错误。

d.pop('Alice')
# 报错
Traceback (most recent call last):
  File "<stdin>", line 1, in <module>
KeyError: 'Alice'

(2)例子

在dict中,使用keys()方法,可以返回dict的所有key,在删除某个元素时,可以通过这个方法先判断某个元素是否存在,改造前面的程序,使得即使key不存在时,删除也不会抛异常。

d = {
    'Alice':45,
    'Bob':60,
    'Candy':75,
    'David':86,
    'Ellena':49
    }
name = 'Coco'
if name in d.keys():
    d.pop(name)
else:
    print('{} not in d'.format(name))

6、dict的特点

(1)查找速度快

dict的第一个特点是查找速度快,无论dict有10个元素还是10万个元素,查找速度都一样。而list的查找速度随着元素增加而逐渐下降。
不过dict的查找速度快不是没有代价的,dict的缺点是占用内存大,还会浪费很多内容,list正好相反,占用内存小,但是查找速度慢。

(2)有序与无序

在Python3.5之前,dict中的元素是无序的,也就是dict中元素的插入顺序和打印顺序未必一致,比如使用Python3.5之前的版本执行以下代码:

d = {
    'Alice': 45,
    'Bob': 60,
    'Candy': 75,
    'David': 86,
    'Ellena': 49
}
print(d) # ==> {'Bob': 60, 'Ellena': 49, 'Alice': 45, 'Candy': 75, 'David': 86}

可以看到,打印的顺序和定义的顺序并不一致。
但是在Python3.6、Python3.7版本中,却得到了有序的结果。

print(d) # ==> {'Alice': 45, 'Bob': 60, 'Candy': 75, 'David': 86, 'Ellena': 49}

为什么在不同的版本中,会得到不一样的结果呢?这是因为底层的实现发生了改变,我们可以认为在Python3.6的版本以后,dict是有序的,但是一般而言,为了避免不必要的误解,一般在需要有序的dict时,我们会使用一种叫做Ordereddict的字典,来确保有序。

(3)key不可变

对于基础数据类型字符串数字等,这些都是不可变的,可以作为dict的key,而对于复杂数据类型,经过前面的学习,我们知道tuple是不可变的,list是可变的,因此tuple可以作为dict的key,但是list不可以作为dict的key,否则将会报错。

key = (1, 2, 3) # 以tuple作为key
d[key] = True
key = [1, 2, 3]
d[key] = True
# 报错
Traceback (most recent call last):
  File "<stdin>", line 1, in <module>
TypeError: unhashable type: 'list'

如上所示,如果将list作为dict的key,将会引起错误。
由于dict是按 key 查找,所以,在一个dict中,key不能重复

(4)遍历dict

通过直接print(d),我们打印出来的是完整的一个dict;有时候,我们需要把dict中m一定条件的元素打印出来,比如成绩超过60的,在这种情况下,我们需要则需要遍历dict(这种时候需要使用for循环),并通过条件判断把满足条件的打印出来。
遍历dict有两种方法, 第一种是遍历dict的所有key,并通过key获得对应的value

d = {
    'Alice': 45,
    'Bob': 60,
    'Candy': 75,
    'David': 86,
    'Ellena': 49
}
for key in d: # 遍历d的key
    value = d[key]
    if value > 60:
        print(key, value)
# ==> Candy 75
# ==> David 86

第二种方法是通过dict提供的items()方法,items()方法会返回dict中所有的元素,每个元素包含key和value。

for key, value in d.items():
    if value > 60:
        print(key, value)
# ==> Candy 75
# ==> David 86

(5)例子

同学的近三次成绩如下,把每个同学的每次成绩依次输出。

d = {'Alice': [50, 61, 66], 'Bob': [80, 61, 66], 'Candy': [88, 75, 90]}
d = {'Alice': [50, 61, 66], 'Bob': [80, 61, 66], 'Candy': [88, 75, 90]}
for key,value in d.items():
    for score in value:
        print(key,score)

7、操作dict的其他方法

除了前面学习的增删改查,dict还提供了非常多的工具函数帮助我们方便快捷的使用dict。

(1)获取dict的所有key

dict提供keys()函数,可以返回dict中所有的key。

d = {'Alice': [50, 61, 66], 'Bob': [80, 61, 66], 'Candy': [88, 75, 90]}
for key in d.keys():
    print(key)
# ==> Alice
# ==> Bob
# ==> Candy

(2)获取dict所有的value

dict提供values()函数,可以返回dict中所有的value。

d = {'Alice': [50, 61, 66], 'Bob': [80, 61, 66], 'Candy': [88, 75, 90]}
for value in d.values():
    print(value)
# ==> [50, 61, 66]
# ==> [80, 61, 66]
# ==> [88, 75, 90]

(3)清除所有元素

dict提供clear()函数,可以直接清除dict中所有的元素。

d = {'Alice': [50, 61, 66], 'Bob': [80, 61, 66], 'Candy': [88, 75, 90]}
print(d) # ==> {'Alice': [50, 61, 66], 'Bob': [80, 61, 66], 'Candy': [88, 75, 90]}
d.clear()
print(d) # ==> {}

(4)例子

已知d = {'Alice': [50, 61, 66], 'Bob': [80, 61, 66], 'Candy': [88, 75, 90]},输出d的元素个数。

我提供了三种方法:

d = {'Alice': [50, 61, 66], 'Bob': [80, 61, 66], 'Candy': [88, 75, 90]}
keys = d.keys()
print(len(keys))

values = d.values()
print(len(values))

num = 0
for key in d:
    num = num + 1
print(num)

八、Set容器

1、Set介绍

(1)介绍

在前面,我们学习了dict,知道dict的key是不重复的,当我们往dict里添加一个相同key的value时,新的value将会覆盖旧的value。
有的时候,我们只想要 dict 的 key,不关心 key 对应的 value,目的就是保证这个集合的元素不会重复,这时,set就派上用场了。
set和list类似,拥有一系列元素,但是set和list不一样,set里面的元素是不允许重复的,而list里面可以包含相同的元素;set与list的另一个区别是,set里面的元素是没有顺序的
创建set的方式是使用set()并传入一个list,list的元素将会被转换成set的元素。

s = set([1, 4, 3, 2, 5])
print(s) # ==> set([1, 2, 3, 4, 5])

需要注意的是,上述打印的形式类似 list, 但它不是 list,仔细看还可以发现,打印的顺序和原始 list 的顺序有可能是不同的,因为set内部存储的元素是无序的。
另外,set不能包含重复的元素,我们传入重复的元素看看会发生什么。

s = set([1, 4, 3, 2, 5, 4, 2, 3, 1])
print(s) # ==> set([1, 2, 3, 4, 5])

可以看到,在传入set()的list中,包含了重复的元素,但是打印的时候,相同的元素只保留了一个,重复的元素都被去掉了,这是set的一个重要特点:自动去重

(2)例子

使用set表示班里面的五个同学。
'Alice', 'Bob', 'Candy', 'David', 'Ellena'

names = ['Alice', 'Bob', 'Candy', 'David', 'Ellena']
name_set = set(names)
print(name_set) # ==> set(['Bob', 'Ellena', 'Alice', 'Candy', 'David'])

2、读取set元素

(1)介绍

由于set里面的元素是没有顺序的,因此我们不能像list那样通过索引来访问。访问set中的某个元素实际上就是判断一个元素是否在set中,这个时候我们可以使用in来判断某个元素是否在set中
比如,存储了班里同学名字的set。

names = ['Alice', 'Bob', 'Candy', 'David', 'Ellena']
name_set = set(names)

请问'Alice'是班里面的同学吗?

'Alice' in name_set # ==> True

请问'Bobby'是班里面的同学吗?

'Bobby' in name_set # ==>False

请问'bob'是班里面的同学吗?

'bob' in name_set # ==> False

这个时候是否输出了不符合预期的结果?'Bob'是在name_set里面的,为什么输出了False呢?这是因为set元素是区分大小写的,必须大小写完全匹配,才能判断该元素在set里面。

(2)例子

由于name_set不能识别小写的名字,改进name_set,是小写的名字也能判断在name_set里面。

names = ['Alice', 'Bob', 'Candy', 'David', 'Ellena']
name_set = set(names)
names = ['Alice', 'Bob', 'Candy', 'David', 'Ellena', 'alice', 'bob', 'candy', 'david', 'ellena']
name_set = set(names)
print(name_set) # ==> set(['ellena', 'alice', 'Candy', 'Alice', 'candy', 'Ellena', 'Bob', 'David', 'bob', 'david'])

3、添加set元素

(1)介绍

我们通过set()传入list的方法创建了set,如果set在使用过程中需要往里面添加元素,这个时候应该怎么添加呢?
set提供了add()方法,我们可以使用add()方法,往set里面添加元素。
比如,班里面来了新的同学,名字叫Gina。

names = ['Alice', 'Bob', 'Candy', 'David', 'Ellena']
name_set = set(names)
name_set.add('Gina')
print(name_set) # ==> set(['Gina', 'Alice', 'Candy', 'David', 'Ellena', 'Bob'])

可以看到,'Gina'已经添加到name_set里面去了。对于set,如果添加一个已经存在的元素,不会报错,也不会改变什么,因为这个特点:自动去重

names = ['Alice', 'Bob', 'Candy', 'David', 'Ellena']
name_set = set(names)
name_set.add('Alice')
print(name_set) # ==> set(['Bob', 'Ellena', 'Alice', 'Candy', 'David'])

有些时候需要批量往set里面添加元素,如果一个一个add是比较麻烦的,有没有批量往set里面添加元素的方法呢?
set提供了update()方法,可以一次性给set添加多个元素
比如,新来了一批同学,名字分别是['Hally',  'Isen',  'Jenny',  'Karl'],则可以使用update()方法,批量往set中添加。

names = ['Alice', 'Bob', 'Candy', 'David', 'Ellena']
new_names = ['Hally', 'Isen', 'Jenny', 'Karl']
name_set = set(names)
name_set.update(new_names) # ==> set(['Jenny', 'Ellena', 'Alice', 'Candy', 'David', 'Hally', 'Bob', 'Isen', 'Karl'])
print(name_set)

(2)例子

使用两种方式往空的set中添加以下名字:['Jenny', 'Ellena', 'Alice', 'Candy', 'David', 'Hally', 'Bob', 'Isen', 'Karl']。

names = ['Jenny', 'Ellena', 'Alice', 'Candy', 'David', 'Hally', 'Bob', 'Isen', 'Karl']
name_set = set()

# 1. 第一种方法,使用add
for name in names:
    name_set.add(name)
print(name_set) # ==> set(['Jenny', 'Ellena', 'Alice', 'Candy', 'David', 'Hally', 'Bob', 'Isen', 'Karl'])

# 第二种方法,使用update
name_set.update(names)
print(name_set) # ==> set(['Jenny', 'Ellena', 'Alice', 'Candy', 'David', 'Hally', 'Bob', 'Isen', 'Karl'])

4、删除set元素

(1)介绍

和list、dict一样,有时候我们也需要考虑删除set的元素。
set提供了remove()方法允许我们删除set中的元素

name_set = set(['Jenny', 'Ellena', 'Alice', 'Candy', 'David', 'Hally', 'Bob', 'Isen', 'Karl'])
name_set.remove('Jenny')
print(name_set) # ==> set(['Ellena', 'Alice', 'Candy', 'David', 'Hally', 'Bob', 'Isen', 'Karl'])

需要注意的是,如果remove的元素不在set里面的话,那么将会引发错误

name_set = set(['Jenny', 'Ellena', 'Alice', 'Candy', 'David', 'Hally', 'Bob', 'Isen', 'Karl'])
name_set.remove('Jenny')
print(name_set) # ==> set(['Ellena', 'Alice', 'Candy', 'David', 'Hally', 'Bob', 'Isen', 'Karl'])
name_set.remove('Jenny') # ==> 重复remove 'Jenny'
# 引起错误
Traceback (most recent call last):
  File "<stdin>", line 1, in <module>
KeyError: 'Jenny'

因此,使用remove()方法,我们需要格外小心,需要提前判断要remove()的元素是否在set里面,确保存在后,再进行remove。总结:先判断,再删除

(2)例子

针对以下set,给定一个list,对于list里面的每个元素,如果set中包含这个元素,就将其删除,否则添加到set里面去。

L = [1, 2, 3, 4, 5, 6, 7, 8, 9, 10]
S = set([1, 3, 5, 7, 9, 11])
L = [1, 2, 3, 4, 5, 6, 7, 8, 9, 10]
S = set([1, 3, 5, 7, 9, 11])
for item in L:
    if item in S:
        S.remove(item)
    else:
        S.add(item)
print(S)

5、操作set的其他方法

(1)不会报错的删除方法discard()

除了使用remove()方法删除元素以外,还可以使用discard()方法删除元素,并且,和remove()不同的是,当元素不存在时,使用discard()并不会引发错误,所以使用discard()是更加高效的一个方法。

name_set = set(['Jenny', 'Ellena', 'Alice', 'Candy', 'David', 'Hally', 'Bob', 'Isen', 'Karl'])
name_set.discard('Jenny')
print(name_set) # ==> set(['Ellena', 'Alice', 'Candy', 'David', 'Hally', 'Bob', 'Isen', 'Karl'])
name_set.discard('Jenny')
print(name_set) # ==> set(['Ellena', 'Alice', 'Candy', 'David', 'Hally', 'Bob', 'Isen', 'Karl']

(2)清除所有元素的方法clear()

和dict一样,set也提供了clear()方法,可以快速清除set中的所有元素。

name_set = set(['Jenny', 'Ellena', 'Alice', 'Candy', 'David', 'Hally', 'Bob', 'Isen', 'Karl'])
print(name_set) # ==> set(['Jenny', 'Ellena', 'Alice', 'Candy', 'David', 'Hally', 'Bob', 'Isen', 'Karl'])
name_set.clear()
print(name_set) # ==> set([])

(3)集合的子集和超集

set提供方法判断两个set之间的关系,比如两个集合set,判断其中一个set是否为另外一个set的子集或者超集。

s1 = set([1, 2, 3, 4, 5])
s2 = set([1, 2, 3, 4, 5, 6, 7, 8, 9])
# 判断s1是否为s2的子集
s1.issubset(s2) # ==> True
# 判断s2是否为s1的超集
s2.issuperset(s1) # ==> True

(4)判断集合是否重合

有时候需要判断两个集合是否有重合的地方,如果使用传统的方法,需要使用for循环一个一个的去判断,非常麻烦,set提供isdisjoint()方法可以快速判断两个集合是否有重合,如果有重合,返回False,否则返回True。

s1 = set([1, 2, 3, 4, 5])
s2 = set([1, 2, 3, 4, 5, 6, 7, 8, 9])
s1.isdisjoint(s2) # ==> False,因为有重复元素1、2、3、4、5

(5)例子

已知两个集合s1、s2,判断两个集合是否有重合,如果有,把重合的元素打印出来。

s1 = set([1, 2, 3, 4, 5])
s2 = set([1, 2, 3, 4, 5, 6, 7, 8, 9])
s1 = set([1, 2, 3, 4, 6, 8, 10])
s2 = set([1, 2, 3, 4, 5, 6, 7, 8, 9])
flag = s1.isdisjoint(s2)
if not flag:
    for item in s1:
        if item not in s2:
            continue
        print(item)

九、函数

1、函数介绍

(1)介绍

对于什么是函数,其实在前面的内容中,已经多次接触到函数了,比如在set里面,使用remove()函数进行元素的删除,使用add()函数添加元素,使用update()函数批量添加元素,但是至今为止,我们都没有对函数有个充分的认识。本章,我们将具体学习函数。
我们知道圆的面积计算公式为:

S = πr²

当我们知道半径r的值时,就可以通过公式计算出面积,假设我们需要计算3个不同大小的圆的面积:

r1 = 12.34
r2 = 9.08
r3 = 73.1
s1 = 3.14 * r1 * r1
s2 = 3.14 * r2 * r2
s3 = 3.14 * r3 * r3

可以看到,在上述代码中,出现了几乎完全重复的代码,每次计算圆的面积的时候我们都是通过3.14*x*x(其中x是圆的半径)的方式计算出来的,这样写不仅非常麻烦,而且,当我们需要提高圆周率的精度时,把3.14改成3.14159265359的时候,我们需要修改涉及到的三行代码。
因此,我们可以使用函数,把重复的逻辑代码封装起来,这样子,我们就不需要每次计算的时候,都写3.14*x*x这样的代码了;当我们使用函数的时候,我们只需要定义一次,就可以多次使用。
我们把封装重复逻辑代码的过程就做抽象,抽象是数学中非常常见的概念。
比如:计算数列的和,比如:1 + 2 + 3 + ... + 100,写起来十分不方便,于是数学家发明了求和符号∑,可以把1 + 2 + 3 + ... + 100记作:

这种抽象记法非常强大,因为我们看到∑就可以理解成求和,而不是还原成低级的加法运算。
而且,这种抽象记法是可扩展的,比如:


还原成加法运算就变成了:

(1 x 1 + 1) + (2 x 2 + 1) + (3 x 3 + 1) + ... + (100 x 100 + 1)

可见,借助抽象,我们才能不关心底层的具体计算过程,而直接在更高的层次上思考问题。
写计算机程序也是一样,函数就是最基本的一种代码抽象的方式
Python不但能非常灵活地定义函数,而且本身内置了很多有用的函数,可以直接调用。

(2)调用函数

Python内置了很多有用的函数,我们可以直接调用。比如前面求list的长度len()函数等等,都是Python内置的函数,我们经常会使用到它们。
在这个文档里面,列举了Python内置的大部分函数,同学们有兴趣可以参考看看。

python函数库

要调用一个函数,需要知道函数的名称和参数,比如求绝对值的函数 abs(),它接收一个参数。
对于abs()函数,abs就是函数的名称,括号()内,就是函数的参数,当函数没有参数时,默认就是一个空括号。abs接收一个参数,这个参数就是需要求绝对值的数,这个参数可以是整数,也可以是浮点数

abs(-100) # ==> 100
abs(20) # ==> 20
abs(-3.14159) # ==> 3.14159

需要注意的是,传递的参数数量一定要和函数要求的一致,不然将会引起错误,比如,如果在abs()函数中传入两个参数。

abs(1, 2)
# 报错
Traceback (most recent call last):
  File "<stdin>", line 1, in <module>
TypeError: abs() takes exactly one argument (2 given)

在错误提示中,清晰的说明了abs()函数只接收一个参数,但是却传递了两个参数,所以引起了错误。
其次,如果传入的参数数量是对的,但是参数的类型不能被函数所接受,也会引起错误,比如:求绝对值的函数abs(),只有数字才拥有绝对值,如果传递一个字符串进去,将会引起错误。

abs('3.1415926')
# 报错
Traceback (most recent call last):
  File "<stdin>", line 1, in <module>
TypeError: bad operand type for abs(): 'str'

这里错误提示说,str类型是错误的参数类型。
除了abs()函数,还有很多常见的函数,比如cmp()函数,可以比较两个数的大小,这个时候,cmp()函数就接收两个参数。
对于cmp(x, y),如果x < y 返回 -1,如果x == y 函数返回0,如果x > y函数返回1。

cmp(1, 2) # ==> -1
cmp(2, 1) # ==> 1
cmp(3, 3) # ==> 0

还有基础数据类型的转换函数,int()函数可以将合法的其它类型数据转换为整数,str()函数可以将其它类型的数据转换为字符串。

int('123') # ==> 123
int(12.34) # ==> 12

str(123) # ==> '123'
str(1.23) # ==> '1.23'

在学习Python的过程中,我们将会接触到越来越多的Python内置函数,这些内置函数提供了非常有用的功能,大大降低我们编程的难度。

(3)例子

sum()函数接收一个list作为参数,并返回list所有元素之和。计算 1*1 + 2*2 + 3*3 + ... + 100*100。

L = []
num = 1
while num <= 100:
    L.append(num*num)
    num = num + 1
print(sum(L))

2、定义函数

(1)定义函数

除了使用Python内置的函数以外,在编程过程中,我们也经常需要自己定义函数。
在Python中,定义一个函数要使用 def 语句,依次写出函数名括号()括号中的参数和冒号:,然后,在缩进块中编写函数体,函数的返回值用 return 语句返回
我们以定义一个求绝对值的函数my_abs函数为例:

def my_abs(x):
    if x >= 0:
        return x
    else:
        return -x

注意,return表示返回的意思,函数体内部的语句在执行时,一旦执行到return时,函数就执行完毕,并将结果返回。因此,函数内部通过条件判断和循环可以实现非常复杂的逻辑。
我们继续定义一个求列表list所有元素的和的函数:

def list_sum(L):
    result = 0
    for num in L:
        result = result + num
    return result

这样子我们就定义了一个sum_list()的函数,注意,在最后return,我们把求和的结果result返回了,这样就可以在外部调用函数后获得result。

L = [1, 3, 5, 7, 9, 11]
result =list_sum(L) # 调用定义的sum_list函数并获得return返回的结果
print(result)

(2)例子

定义一个square_of_sum()函数,它接收一个list,返回list中每个元素平方的和。

def square_of_sum(L):
    result = 0
    for num in L:
        result = result + num*num
    return result
print(square_of_sum([1,2]))
print(square_of_sum([1,2,3]))

3、函数返回值

(1)返回值介绍

在上面内容中,我们在函数里面使用return返回了计算的结果,在外部调用这个函数的时候,就可以接收到结果。
有时候函数是没有返回结果的,这个时候从函数获取到的是一个空值None。
我们对list_sum()这个函数进行简单的修改,在函数内把结果打印出来,不通过return返回结果。

def list_sum(l):
    result = 0
    for num in l:
        result = result + num
    print('result is {}'.format(result))
    return

l = [1, 3, 5, 7, 9, 11]
result = list_sum(l) # 调用定义的sum_list函数并获得return返回的结果
print(result) # ==> None

print(result)中,我们得到None的结果,这是合理的,因为在函数内部,我们把结果打印出来了,但是没有把结果返回。
除了返回None、一个值以外,函数也可以返回多个值,在函数中,如果需要返回多个值,多个值之间使用逗号分隔即可,但是需要注意顺序。
比如,定义一个函数data_of_square,接收边长一个参数,同时返回正方形的周长和面积。

def data_of_square(side):
    C = 4 * side
    S = side * side
    return C, S

C, S = data_of_square(16)
print('周长 = {}'.format(C)) # ==> 周长 = 64
print('面积 = {}'.format(S)) # ==> 面积 = 256

也可以使用一个值存储函数返回的多值结果。

result = data_of_square(16)
print(result) # ==> (64, 256)

注意打印的result,其实它是tuple类型,如果我们需要取出结果中的周长或者面积,使用对应位置的下标就可以获得对应的结果。

result = data_of_square(16)
C = result[0]
S = result[1]
print('周长 = {}'.format(C)) # ==> 周长 = 64
print('面积 = {}'.format(S)) # ==> 面积 = 256

(2)例子

定义一个函数sub_sum(),这个函数接收一个列表作为参数,函数返回列表所有奇数项的和以及所有偶数项的和。

def sub_sum(L):
    index = 1
    sum_ji = 0
    sum_ou = 0
    for item in L:
        if index % 2 == 0:
            sum_ou += item
        else:
            sum_ji += item
        index += 1
    return sum_ji,sum_ou

L = [1,2,3,4,5,6,7,8,9,10]
result = sub_sum(L)
print('奇数项的和 = {}'.format(result[0]))
print('偶数项的和 = {}'.format(result[1]))

4、递归函数

(1)介绍

在函数内部,还可以调用其他函数,比如实现函数data_of_square的时候,它接收边长一个参数,同时返回正方形的周长和面积,而求周长和求面积是完全独立的逻辑,可以定义成两个新的函数,然后在data_of_square函数中再调用这两个函数,得到结果并返回。

def square_area(side):
    return side * side

def square_perimeter(side):
    return 4 * side

def data_of_square(side):
    C = square_perimeter(side)
    S = square_area(side)
    return C, S

在函数内部调用其他函数,是非常常见的,通过合理拆分逻辑,可以降低程序的复杂度。如果在一个函数内部调用其自身,这个函数就是递归函数
举个例子,我们来计算阶乘 n! = 1 * 2 * 3 * ... * n,用函数 fact(n)表示,可以看出:

fact(n) = n! = 1 * 2 * 3 * ... * (n-1) * n = (n-1)! * n = fact(n-1) * n

所以,fact(n)可以表示为 n * fact(n-1),只有n=1时需要特殊处理。
于是,fact(n)用递归的方式写出来就是:

def fact(n):
    if n==1:
        return 1
    return n * fact(n - 1)

这个fact(n)就是递归函数,可以试试计算得到的结果。

fact(1) # ==> 1
fact(5) # ==> 120

我们可以拆解fact(5)计算的详细逻辑

===> fact(5)
===> 5 * fact(4)
===> 5 * (4 * fact(3))
===> 5 * (4 * (3 * fact(2)))
===> 5 * (4 * (3 * (2 * fact(1))))
===> 5 * (4 * (3 * (2 * 1)))
===> 5 * (4 * (3 * 2))
===> 5 * (4 * 6)
===> 5 * 24
===> 120

递归函数的优点是定义简单,逻辑清晰。理论上,所有的递归函数都可以写成循环的方式,但循环的逻辑不如递归清晰。
使用递归函数需要注意防止栈溢出。在计算机中,函数调用是通过栈(stack)这种数据结构实现的,每当进入一个函数调用,栈就会加一层栈帧,每当函数返回,栈就会减一层栈帧。由于栈的大小不是无限的,所以,递归调用的次数过多,会导致栈溢出。可以试试计算 fact(10000)。

Traceback (most recent call last):
RecursionError: maximum recursion depth exceeded in comparison

(2)例子

分别使用循环和递归的形式定义函数,求出1~100的和。

#循环的方式
def sumA(n):
    sum = 0
    index = 1
    while index <=n:
        sum += index
        index += 1
    return sum

#递归
def sumB(n):
    sum = 0
    if n == 1:
        sum = 1
    else:
        sum = n + sumB(n - 1)
    return sum

print(sumA(100))
print(sumB(100))

5、函数参数

(1)介绍

函数参数是需要传递给函数内部的数据,在前面,我们已经简单接触了函数的参数,现在我们正式来认识它。
函数参数可以是任意的数据类型,只要函数内部逻辑可以处理即可。

def print_param(param):
    print(param)

对于print_param函数,由于函数的逻辑是直接打印参数,并没有做任何别的逻辑,所以这个函数可以接受整数、浮点数、list、tuple、dict等等的数据类型。

print_param(1)
print_param('3.1415926')
print_param([1, 2, 3, 4, 5])

但是,有时候由于函数的实现关系,需要特定的参数,就比如前面实现的求绝对值的函数my_abs(),如果传递一个字符串,就会引起错误。

def my_abs(x):
    if x >= 0:
        return x
    else:
        return -x

my_abs('str param')
# 报错
TypeError: '>=' not supported between instances of 'str' and 'int'

为了保证函数的正常运行,有时候需要对函数入参进行类型的校验,Python提供isinstance()函数,可以判断参数类型,它接收两个参数,第一个是需要判断的参数,第二个是类型。

isinstance(100, int) # ==> True
isinstance(100.0, int) # ==> False
isinstance('3.1415926', str) # ==> True

有了isinstance,就可以优化my_abs函数,不在里面运行出错了。

def my_abs(x):
    if not isinstance(x, int) or not isinstance(x, float):
        print('param type error.')
        return None
    if x >= 0:
        return x
    else:
        return -x

(2)例子

实现函数func,当参数类型为list时,返回list中所有数字类型元素的和,当参数类型为tuple时,返回tuple中所有数字类型元素的乘积。

def func(param):
    if isinstance(param,list):
        result = 0
        for item in param:
            if isinstance(item,int) or isinstance(item,float):
                result += item
        return result
    elif isinstance(param,tuple):
        result = 1
        for item in param:
            if isinstance(item,int) or isinstance(item,float):
                result *= item
        return result
    return None

print(func(6))
print(func((2,3)))
print(func([2,3]))

6、函数使用默认参数

(1)介绍

定义函数的时候,还可以有默认参数,默认参数的意思是当这个参数没有传递的时候,参数就使用定义时的默认值。
例如Python自带的 int() 函数,其实就有两个参数,我们既可以传一个参数,又可以传两个参数:

int('123') # ==> 123
int('123', 8) # ==> 83

int()函数的第二个参数是转换进制base,如果不传,默认是十进制 (base=10),如果传了,就用传入的参数。
可见,函数的默认参数的作用是简化调用,你只需要把必须的参数传进去。但是在需要的时候,又可以传入额外的参数来覆盖默认参数值。
我们来定义一个计算 x 的N次方的函数:

def power(x, n):
    result = 1
    while n > 0:
        n = n - 1
        result = result * x
    return result 

假设计算平方的次数最多,我们就可以把 n 的默认值设定为 2:

def power(x, n=2):
    result = 1
    while n > 0:
        n = n - 1
        result = result * x
    return result 

这样一来,计算平方就不需要传入两个参数了:

power(5) # ==> 25

另外需要注意的是,由于函数的参数按从左到右的顺序匹配,所以默认参数只能定义在必需参数的后面,否则将会出现错误。

def power(n=2,x):
    result = 1
    while n > 0:
        n = n - 1
        result = result * x
    return result 

(2)例子

定义一个 greet() 函数,它包含一个默认参数,如果没有传入参数,打印 Hello, world.,如果传入参数,打印Hello, 传入的参数内容

def greet(name='world'):
    print('Hello,' + name + '.')
    
greet()
greet('Alice')

7、函数使用可变参数

(1)介绍

除了默认参数,Python函数还接收一种参数叫做可变参数,可变参数即任意个参数的意思,可变参数通常使用*args来表示。

def func(*args):
    print('args length = {}, args = {}'.format(len(args), args))

func('a') # ==> args length = 1, args = ('a',)
func('a', 'b') # ==> args length = 2, args = ('a', 'b')
func('a', 'b', 'c') # ==> args length = 3, args = ('a', 'b', 'c')

注意,在使用上,Python会把可变参数定义为一个tuple,所以在函数内部,把可变参数当作tuple来使用就可以了,比如可以通过位置下标取出对应的元素等。
定义可变参数的目的也是为了简化调用。假设我们要计算任意个数的平均值,就可以定义一个可变参数:

def average(*args):
    sum = 0
    for item in args:
        sum += item
    avg = sum / len(args)
    return avg

这样,在调用的时候,我们就可以这样写:

average(1, 2) # ==> 1.5
average(1, 2, 2, 3, 4) # ==> 2.4
average()
# 报错
Traceback (most recent call last):
ZeroDivisionError: division by zero

在执行average()的时候,却报错了,这是因为在使用可变参数时,没有考虑周全导致的,因为可变参数的长度可能是0,当长度为0的时候,就会出现除0错误。因此需要添加保护的逻辑,这是同学在使用过程中需要特别注意的。

(2)例子

请完善average()函数,使得当可变参数长度为0的时候,也能正确返回结果。

def average(*args):
    sum = 0
    if len(args) == 0:
        return sum
    for item in args:
        sum += item
    avg = sum / len(args)
    return avg

8、函数使用可变关键字参数

(1)介绍

可变参数在使用上确实方便,函数会把可变参数当作tuple去处理,tuple在使用上有一定的局限性,比如有时候想找到特定位置的参数,只能通过下标的方式去寻找,如果顺序发生变化得时候,下标就会失效,函数逻辑就得重新修改实现。
Python函数提供可变关键字参数,对于可变关键字参数,可以通过关键字的名字key找到对应的参数值,想想这和我们之前学习过的什么类似?是的没错,dict,Python会把可变关键字参数当作dict去处理;对于可变关键字参数,一般使用**kwargs来表示。
例如,想要打印一个同学的信息,可以这样处理:

def info(**kwargs):
    print('name: {}, gender: {}, age: {}'.format(kwargs.get('name'), kwargs.get('gender'), kwargs.get('age')))

info(name = 'Alice', gender = 'girl', age = 16)

对于一个拥有必需参数,默认参数,可变参数,可变关键字参数的函数,定义顺序是这样的:

def func(param1, param2, param3 = None, *args, **kwargs):
    print(param1)
    print(param2)
    print(param3)
    print(args)
    print(kwargs)

func(100, 200, 300, 400, 500, name = 'Alice', score = 100)
# ==> 100
# ==> 200
# ==> 300
# ==> (400, 500)
# ==> {'name': 'Alice', 'score': 100}

当然,这么多类型的参数,很容易导致出错,在实际使用上,不建议定义这么多的参数

(2)例子

编写一个函数,它接受关键字参数names,gender,age三个list,分别包含同学的名字、性别和年龄,请分别把每个同学的名字、性别和年龄打印出来。
 

def info(**kwargs):
    names = kwargs['names']
    gender_list = kwargs['gender']
    age_list = kwargs['age']
    index = 0
    for name in names:
        gender = gender_list[index]
        age = age_list[index]
        print('name: {}, gender: {}, age: {}'.format(name, gender, age))
        index += 1
        
info(names = ['XiaoMing','XiaoHong','XiaoCheng'],gender = ['girl','boy','girl'],age = [20,18,25])

到此为止,我们的基本语法知识就结束了,但还远远不够,后续我会继续更新,bye~ 

  • 46
    点赞
  • 28
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值