欢迎关注:lxw-pro
基础,还是很重要的, 扎扎实实地打好基础,练好基本功,这样才能练习更高深的功法!
计算机顾名思义就是可以做数学计算的机器,因此,计算机程序理所当然地可以处理各种数值。但是
,计算机能处理的远不止数值,还可以处理文本、图形、音频、视频、网页等各种各样的数据,不同的数据,需要定义不同的数据类型。
学习编程,认识不同编程中的数据类型是必要的,在Python中,能够直接处理的数据类型有以下几种:
整数
整数和我们生活中定义的整数是一个概念,例如:1,2,3,10,100,1000,-1000 等等,都是整数,Python可以处理任意大小的整数。
对于整数,除了生活中常用的十进制以外,计算机由于使用二进制,所以,在计算机中也常用二进制或者十六进制表示整数。相比十进制数,在Python中使用以下方式表示二进制数和十六进制数。
二进制数只使用数字0、1表示,在Python中,二进制整数使用前缀0b表示,比如:0b0110,0b1100。
十六进制数除了0~9十个数字以外,还使用a、b、c、d、e、f,在Python中,十六进制使用前缀0x,比如:0x12ef,0xde2431af。
十进制数、二进制数和十六进制数之间是可以相互转换的,如果感兴趣的同学,可以学习《编程必备基础知识
》了解更多转换的细节。
浮点数
浮点数对应我们生活的是小数,例如:0.1,0.5, 12.34, 3.1415926等,都是浮点数。
为什么小数在计算机领域被称为浮点数呢?这是因为按照科学记数法表示时,一个浮点数的小数点位置是可变的(浮动的),比如,1.23x109和12.3x108是相等的,因此称为浮点数。
浮点数可以用数学写法,如1.23,3.14,-9.01。但是对于很大或很小的浮点数,就必须用科学计数法表示,在Python中,把10用e替代,比如:1.23x10^9就是1.23e9,或者12.3e8,0.000012可以写成1.2e-5,同学可以自行举出更多的例子。
这里有必要提醒一点就是,整数和浮点数在计算机内部存储的方式是不同的,整数运算永远是精确的,而浮点数运算则可能会有四舍五入的误差,如何检验,可以在Python终端中输入以下内容0.1 + 0.2,看看可以得到你期望的结果0.3吗?真实结果可能让你失望了。
同样的,这里涉及的原理同学也可以学习上面提到的课程进行进一步原理层面的理解。
字符串
字符串对应生活中的就是非数字类型的内容,比如一个句子,一段话,在Python中就是字符串,在Python中,字符串是以’‘或""括起来的任意文本,比如’abc’,“xyz"等等。请注意,''或”"本身只是一种表示方式,不是字符串的一部分,因此,字符串’abc’只有a,b,c这3个字符。
比如之前练习过的第一个Python程序:print(‘Hello World’),其中的Hello World就是字符串。
布尔值
布尔值对应于生活中的就是对和错,在计算机的世界里,大部分判断都是非错则对的,布尔值和布尔代数的表示完全一致,一个布尔值只有True、False两种值,要么是True,要么是False,在Python中,可以直接用True、False表示布尔值(请注意大小写,不需要使用字符串符号括起来),也可以通过布尔运算计算出来。
布尔值可以用and、or和not运算(注意and,or,not都是Python语言本身的关键字)。
and运算是与运算,只有所有都为 True,and运算结果才是 True。
or运算是或运算,只要其中有一个为 True,or 运算结果就是 True。
not运算是非运算,它是一个单目运算符,把 True 变成 False,False 变成 True。
空值
空值是Python里一个特殊的值,用None表示。
注意
,None和0是不一样的,None不能理解为0,因为0是有意义的,比如我有0个苹果表示我没有苹果,0表达的是数值0的意思,而不能表示为我有None个苹果,None是一个特殊的空值。
此外,Python还提供了列表、字典等多种数据类型,还允许创建自定义数据类型,我们后面会展开介绍。
任务
请判断以下数据分别属于什么数据类型:
3.1415926
‘Learn Python in imooc.’
100
0b1101
?不会了怎么办
可以使用print()将结果打印出来
什么是变量
在Python中,变量的概念基本上和初中代数的方程变量是一致的。例如,对于方程式 y=x*x ,x就是变量。当x=2时,计算结果是4,当x=5时,计算结果是25。
合法的变量名
只是在计算机程序中,变量不仅可以是数字,还可以是任意数据类型(字符串、布尔值、空值、字典等)。在Python中,定义一个变量需要遵循一定的约束,否则,Python可能识别不出它是一个变量。
变量名由大小写英文字母、数字和下划线_组成
变量不能用数字开头
变量尽量不要和Python关键字重合(比如前面学习过的:and、or、not,否则可能导致Python原有关键字发挥不出作用)
以下这些变量的定义都是合法的。
num, count, _none, min_value
他们都满足上面的三个条件。
以下这些变量的定义都是不合法的。
1num, 666, 1_cd, and
定义变量
定义变量的方式很简单,通过变量名 = 数据,即可定义一个变量。
举个例子:
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++等等属于静态语言。
任务
前面我们写了第一个Python程序,向屏幕打印了’Hello World’,请通过定义多个变量的方式,把相同的字符串打印出来。
?不会了怎么办
参考答案:
hello = 'Hello'
space = ' '
world = 'World'
print(hello, space, world)
在Python中,整数和浮点数虽然属于不同的数值类型,但是在运算上是可以一起运算的,这从生活经验出发,也是可以理解的。
四则运算
整数、浮点数可以直接进行四则运算。
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得到不一样的结果
num1 = 10
num2 = 3
result = num1 / num2
print(result) # ==> 3
num1 = 10
num2 = 3
result = num1 / num2
print(result) # ==> 3.3333333333333335
可以看到在python2,得到的是一个整数的结果,这是因为除数和被除数都是整数时,得到的结果也默认保存为整数了,这是非常不科学的,因此在python3,改进了这一点。
取模运算
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为偶数
地板除
Python除了普通除法以外,还有一个特殊的除法被称为地板除,对于地板除,得到的结果会忽略纯小数的部分,得到整数的部分,地板除使用//进行。
10//4 # ==> 2
10//2.5 # ==> 4.0
10//3 # ==> 3
小数点位数
使用Python计算小数的时候,经常需要保留小数点后若干位,可以使用round()函数来处理,这里先了解round的调用方式,使用两个参数,第一个是需要保留小数点位数的数值,第二个是保留的位数。
num = 10 / 3
print(num) # ==> 3.3333333333333335
round(num, 2) # ==> 3.33
任务
一个长方形的长为3.14cm,宽为1.57cm,请计算这个长方形的面积,保留小数点后两位。
?不会了怎么办
参考答案:
length = 3.14
width = 1.57
result = round(length * width, 2)
print(result)
前面我们了解到,布尔类型是Python的基础数据类型,布尔类型只有True和False两种值,本节课我们学习布尔类型的集中运算。
只有两个布尔值都为 True 时,计算结果才为 True。
True and True # ==> True
True and False # ==> False
False and True # ==> False
False and False # ==> False
只要有一个布尔值为 True,计算结果就是 True。
True or True # ==> True
True or False # ==> True
False or True # ==> True
False or False # ==> False
把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的结果。
短路计算
在计算a and b时,如果 a 是 False,则根据与运算法则,整个结果必定为 False,因此返回 a;如果 a 是 True,则整个计算结果必定取决与 b,因此返回 b。
在计算a or b时,如果 a 是 True,则根据或运算法则,整个计算结果必定为 True,因此返回 a;如果 a 是 False,则整个计算结果必定取决于 b,因此返回 b。
所以Python解释器在做布尔运算时,只要能提前确定计算结果,它就不会往后算了,直接返回结果。
任务
请运行如下代码,并解释打印的结果:
a = 'python'
print('hello,', a or 'world')
b = ''
print('hello,', b or 'world')
?不会了怎么办
解释:通过 or 运算,可以把空字符串"变成"默认字符串,而非空字符串保持不变。
前面我们讲解了什么是字符串。字符串可以用' '或者" "括起来表示
。
如果字符串本身包含’怎么办?比如我们要表示字符串 I’m OK ,这时,可以用" "括起来表示:
“I’m OK”
类似的,如果字符串包含",我们就可以用’ '括起来表示:
‘Learn “Python” in imooc’
但是,如果字符串既包含’又包含"怎么办?
这个时候,就需要对字符串中的某些特殊字符进行“转义”,Python字符串用\进行转义。
要表示字符串Bob said “I’m OK”
由于’和"会引起歧义,因此,我们在它前面插入一个\表示这是一个普通字符,不代表字符串的起始,因此,这个字符串又可以表示为
‘Bob said “I’m OK”.’
注意
:转义字符 \不计入字符串的内容中。
常用的转义字符还有:
\n表示换行
\t 表示一个制表符
\\表示 \ 字符本身
任务
请在Python中输出以下字符串special string: ', ", , \, \n, \t
?不会了怎么办
参考答案:
s = 'special string: \', ", \\, \\\\, \\n, \\t'
print(s)
如果一个字符串包含很多需要转义的字符,对每一个字符都进行转义会很麻烦。为了避免这种情况,我们可以在字符串前面加个前缀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 in imooc!'''
任务
请把下面的字符串用r’‘’…‘’'的形式改写,并用print打印出来:
'\"To be, or not to be\": that is the question.\nWhether it\'s nobler in the mind to suffer.'
?不会了怎么办
首先根据换行符\n把字符串拆成多行,然后,添加 r 后去掉转义字符\
参考代码:
print(r'''"To be, or not to be": that is the question.
Whether it's nobler in the mind to suffer.''')
字符串是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', 'imooc')
print(result) # ==> Hello World, Hello China, Hello Beijing, Hello imooc.
template = 'Hello {3}, Hello {2}, Hello {1}, Hello {0}.'
result = template.format('World', 'China', 'Beijing', 'imooc')
print(result) # ==> Hello imooc, Hello Beijing, Hello China, Hello World.
除了使用顺序,还可以指定对应的名字,使得在format过程更加清晰。
template = 'Hello {w}, Hello {c}, Hello {b}, Hello {i}.'
world = 'World'
china = 'China'
beijing = 'Beijing'
imooc = 'imooc'
result = template.format(w = world, c = china, b = beijing, i = imooc)
print(result) # ==> Hello World, Hello China, Hello Beijing, Hello imooc.
任务
请使用两种format的方式打印字符串Life is short, you need Python。
?不会了怎么办
参考答案:
print('Life is short, you need {}'.format('Python'))
print('Life is short, you need {launguage}'.format( launguage = 'Python'))
在python2中,字符串的编码问题是使用python2必经历的门槛,所幸到了python3,python3从编程语言的层面就减少了很多编码的问题,但是学习字符串编码还是很有必要的
。
为什么有编码问题
因为计算机只能处理数字,如果要处理文本,就必须先把文本转换为数字才能处理。最早的计算机在设计时采用8个比特(bit)作为一个字节(byte),所以,一个字节能表示的最大的整数就是255(二进制11111111=十进制255),
0- 255被用来表示大小写英文字母、数字和一些符号,这种编码方式被称为ASCII编码,比如大写字母 A 的编码是65,小写字母 z 的编码是122。
如果要表示中文,显然一个字节是不够的,至少需要两个字节,而且还不能和ASCII编码冲突,所以,中国制定了GB2312
编码,用来把中文编进去。
类似的,日文和韩文等其他语言也有这个问题。为了统一所有文字的编码,Unicode
应运而生。Unicode把所有语言都统一到一套编码里,这样就不会再有乱码问题了。
Unicode通常用两个字节表示一个字符,原有的英文编码从单字节变成双字节,只需要把高字节全部填为0就可以。
python3的编码
在python3中,默认使用UTF-8 Unicode来进行编码,因此我们可以在python中输入任意形式的Unicode字符串,都不会遇到像python2
中遇到的问题(在python2中,需要显式指明该字符串是Unicode字符串),如果没有了解过python2,则完全可以忽略这一点,python3
提供了更加简单易懂的编码方式。
s1 = '这是中文字符串'
s2 = 'this is an English string'
print(s1)
print(s2)
在python3中,中文字符串和英文字符串无异。
任务
请定义并打印中英文混合的字符串 "这是一句中英文混合的Python字符串:Hello World!"
?不会了怎么办
参考答案:
s = '这是一句中英文混合的Python字符串:Hello World!'
print(s)
字符串由一个个字符组成,每一个字符都有一个唯一的位置。比如字符串’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
任务
请从字符串’AABCDEFGHHIJ
’中,使用切片的方式取出’ABCDEFGH’。
?不会了怎么办
参考答案:
s = 'AABCDEFGHHIJ'
sub = s[1:9]
print(sub)
计算机之所以能做很多自动化的任务,因为它可以自己做条件判断,通过条件判断,选择做什么样的逻辑(当然,逻辑是需要我们提前写好的),我们称之为条件分支判断。
举个例子,在100分试卷的考试中,小于60分我们认为是不及格的,因此,我们可以使用程序自动判断考试是否及格。
score = 59
if score < 60:
print('抱歉,考试不及格')
这里,因为score = 59 < 60,所以if的判断是True,因此就会执行print(‘抱歉,考试不及格’)。
这里有几个需要注意
的地方:
可以看到print(‘抱歉,考试不及格’)这行代码明显比上一行代码缩进了,这是因为这行代码是if判断的一个子分支,因此需要缩进,在Python规范中,一般使用4个空格作为缩进
在if语句的最后,有一个冒号:,这是条件分支判断的格式,在最后加入冒号:,表示接下来是分支代码块
任务
如果年龄达到18岁,则是成年人,咚咚呛的年龄是19岁,请使用if语句判断咚咚呛是否成年,如果成年,输出’adult’,并把咚咚呛的年龄打印出来。
?不会了怎么办
参考答案:
age = 19
if age >= 18:
print('your age = {}'.format(age))
print('adult')
使用if判断,可以在当if条件为True时,执行if的子逻辑分支,但有时候,也想在if条件不为True时,执行别的子逻辑分支。
比如:在上一节课的例子中,我希望当分数小于60分时输出,抱歉,考试不及格,否则,输出恭喜你,考试及格,这个时候可以使用if-else语句。
score = 59
if score < 60:
print('抱歉,考试不及格')
else:
print('恭喜你,考试及格')
这个时候,程序就会根据score的值,走不同的子逻辑分支,同学可以改变score的值试一试。
在这里,同样需要注意两个地方,第一个是冒号:,在else中,同样需要冒号;其次是缩进,在else的子逻辑分支,同样需要缩进。
任务
如果年龄达到18岁,则是成年人,请使用if-else语句实现以下逻辑,如果成年,输出’adult’,否则,输出’teenager’。
?不会了怎么办
参考代码:
age = 16
if age >= 18:
print('adult')
else:
print('teenager')
在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('恭喜你,考试及格')
任务
如果年龄达到18岁,则是成年人,如果年龄6岁到18岁,则是青少年,如果年龄3岁到6岁,则是小孩子,如果年龄在3岁以下,则是婴儿,请使用if-elif-else语句实现逻辑,如果成年,输出’adult’,如果是青少年,输出’teenager’,如果是小孩子,输出kid,如果是婴儿,输出baby。
?不会了怎么办
多个条件判断需要使用多个elif。
参考答案:
age = 1
if age >= 18:
print('adult')
elif age >= 6:
print ('teenager')
elif age >= 3:
print ('kid')
else:
print ('baby')
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的最后一个字符为止。
任务
班里考试后,老师要统计几位同学的平均成绩,已知5位同学的成绩用list表示如下: L = [75, 92, 59, 68, 99]
请利用for循环计算出平均成绩。
?不会了怎么办
利用一个sum变量累加就可以计算出总成绩。
L = [75, 92, 59, 68, 99]
sum = 0.0
for x in L:
sum = sum + x
print(sum / 5)
和 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循环不会无限进行下去。
任务
请求出1~10的乘积。
?不会了怎么办
参考代码:
num = 1
sum = 1
while num <= 10:
sum = sum * num
num = num + 1
print(sum)
用 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
任务
请综合使用while True和break,计算0~1000以内,所有偶数的和。
?不会了怎么办
参考答案:
num = 0
sum = 0
while True:
if num > 1000:
break
if num % 2 == 0:
sum = sum + num
num = num + 1
print(sum)
使用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
任务
请综合使用while和continue,计算0~1000以内,所有偶数的和。
?不会了怎么办
参考答案:
num = 0
sum = 0
while num <= 1000:
num = num + 1
if num % 2 == 1:
continue
sum = sum + num
print(sum)
就像多层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
任务
字符串s1=‘ABC’,字符串s2=‘123’,字符串s3=‘xyz’,请输出s1、s2、s3中所有字符的排列。
?不会了怎么办
参考答案:
s1 = 'ABC'
s2 = '123'
s3 = 'xyz'
for ch1 in s1:
for ch2 in s2:
for ch3 in s3:
print(ch1 + ch2 + ch3)
生活中,容器指的是可以容纳物品的收纳器,在程序中,容器是一种可以把多个元素放在一起的数据结构,容器中的元素可以逐个地迭代获取,可以用in,
not in等关键字判断某个元素是否包含在容器中。
在Python中,包括列表(list)、元组(tuple)、字典(dict)、集合(set)等,他们都可以放入多个元素,因此都可以算作是容器,这些容器是Python编程中非常重要的数据结构,我们接下来重点学习这些数据结构。
比如列出所有同学们的成绩:
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)
任务
Alice同学某次考试语文(Chinese)、数学(Math)、英语(English)三科的成绩分别是92、75、99,请使用list保存这些数据。
注意
:科目和成绩属于不同的数据类型。
?不会了怎么办
list可以同时包含字符串和数字
。
参考答案:
L = ['Alice', 'Chinese', 92, 'Math', 75, 'English', 99]
print(L)
列表是有序的,因此我们可以按顺序访问列表中的元素。
L = ['Alice', 66, 'Bob', True, 'False', 100]
for item in L:
print(item)
回想一下,在前面,我们学习过字符串,这里使用for循环访问列表中的每一个元素和使用for循环访问字符串中的每一个字符是非常类似的。
事实上字符串也可以看作是一种特殊的列表,它只能按顺序存放多个字符。通过for循环访问列表每一个元素的方式,我们称为迭代。
而对于一个空的列表,使用for循环访问,将不会打印任何东西。
L = []
for item in L:
print(item)
任务
请使用迭代的方式按顺序输出列表 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)
由于列表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 "" , 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) # ==> []
任务
五名同学的成绩可以用一个list表示:L = [95.5, 85, 59, 66,
72],请按照索引的方式分别打印出第一名、第二名、第三名。
?不会了怎么办
参考答案:
L = [95.5, 85, 59, 66, 72]
print(L[0])
print(L[1])
print(L[4])
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 "" , line 1, in <module>
IndexError: list index out of range
任务
三名同学的成绩可以用一个list表示:L = [95.5, 85, 59, 66,
72],请按照倒序索引的方式分别打印出第一名、第二名、第三名。
?不会了怎么办
参考答案:
L = [95.5, 85, 59, 66, 72]
print(L[-5])
print(L[-4])
print(L[-1])
现在班里面有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
任务
班上已有同学[‘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)
如果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']
任务
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)
对于列表,除了可以向列表添加元素,删除列表元素以外,列表已有的元素,也是可以修改的,通过索引指定位置,并赋值新的元素,即可替换列表中原有的元素。
假如班上同学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 "" , line 1, in <module>
IndexError: list assignment index out of range
任务
班上某次考试,[‘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)
有时候,一维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第三次的成绩。
任务
有三个长方体,他们的长宽高分别是[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)和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 "" , line 1, in <module>
TypeError: 'tuple' object does not support item assignment
请注意
,元组(tuple)的这个特性是非常重要的,在运行上tuple的性能是list的数倍。
任务
请创建一个tuple,顺序包含0~9的十个数,并尝试将其转换为列表list。
?不会了怎么办
参考答案:
T = (0, 1, 2, 3, 4, 5, 6, 7, 8, 9)
print(T)
L = list(T)
print(L)
由于tuple一旦定义之后便不可修改,所以在实际编程中,tuple经常用于存放固定不变的数据
。
因此在使用上,tuple提供了便捷的方法可以访问tuple中的数据。
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
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 "" , line 1, in <module>
ValueError: tuple.index(x): x not in tuple
任务
在某次考试中,班上的成绩如下:(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)
scores.count(100)
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)
任务
tuple的元素也可以是tuple,请判断以下定义T的元素中,有多少个tuple。
T = ((1+2), ((1+2),), (‘a’+‘b’), (1, ), (1,2,3,4,5))
?不会了怎么办
参考答案:
3个
第一个元素(1+2),第三个元素('a'+'b'),计算的结果分别是数字3和字符串ab,不是tuple。
前面我们学习了,对于tuple,它和list一个最大的不同点就是tuple是不可变的
,tuple里面的元素,也是不可替换的
。但是这针对的是仅包含基础数据类型(数字类型、布尔类型、字符串类型)的数据,对于组合数据类型,则不受这个约束。
T = (1, 'CH', [3, 4])
这里T有三个元素,第一个元素是数字类型,第二个元素是字符串类型,第三个元素是列表类型的,我们尝试修改第三个元素的数据。
T = (1, 'CH', [3, 4])
L = T[2]
print(L) # ==> [3, 4]
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]
T[2] = L2
# 报错
Traceback (most recent call last):
File "" , line 1, in <module>
TypeError: 'tuple' object does not support item assignment
如果我们直接替换list,这也是不行的。
任务
T = (1, ‘CH’, [3, 4])
tuple里面包含list,导致tuple是可变的,请修改T的定义,不改变基本数据类型的数据,使T是不可变的。
?不会了怎么办
参考答案:
T = (1, 'CH', (3, 4))
print(T)
我们已经知道,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和一个value,key和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,需要调用函数往里面添加数据,我们后面会继续学习。
任务
新来的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
}
我们现在可以创建一个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 "" , 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的元素。
任务
根据如下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'))
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)
任务
已有同学的某次成绩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)
上一节课我们学习了往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的缘故。
因此我们发现这个赋值语句其实有两个功能:
当key不存在时,往dict中添加对应的key: value元素。 当key存在时,会更新dict,用新的value替换原来的value。
因此
,在使用赋值语句往dict中添加元素时,为了避免不必要的覆盖问题,我们需要先判断key是否存在,然后再做更新。
任务
已有同学们的成绩如下,请更新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)
当同学转校时,我们需要把同学的成绩从已有的成绩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 "" , line 1, in <module>
KeyError: 'Alice'
任务
在dict中,使用keys()方法,可以返回dict的所有key,在删除某个元素时,可以通过这个方法先判断某个元素是否存在,请改造前面的程序,使得即使key不存在时,删除也不会抛异常。
?不会了怎么办
参考答案:
d = {
'Alice': 45,
'Bob': 60,
'Candy': 75,
'David': 86,
'Ellena': 49
}
name = 'Alice'
if name in d.keys():
d.pop(name)
else:
print('{} not in d'.format(name))
dict的第一个特点是查找速度快,无论dict有10个元素还是10万个元素,查找速度都一样。而list的查找速度
随着元素增加而逐渐下降。
不过dict的查找速度快不是没有代价的,dict的缺点是占用内存大,还会浪费很多内容,list正好相反,占用内存小,但是查找速度慢。
在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的字典,来确保有序。
对于基础数据类型,字符串、数字等,这些都是不可变的,可以作为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 "" , line 1, in <module>
TypeError: unhashable type: 'list'
如上所示,如果将list作为dict的key,将会引起错误。
由于dict是按 key 查找,所以,在一个dict中,key不能重复
。
通过直接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
任务
同学的近三次成绩如下,请把每个同学的每次成绩依次输出。
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)
除了前面学习的增删改查,dict还提供了非常多的工具函数帮助我们方便快捷的使用dict。
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
dict提供values()函数,可以返回dict中所有的value。
d = {'Alice': [50, 61, 66], 'Bob': [80, 61, 66], 'Candy': [88, 75, 90]}
for key in d.values():
print(key)
# ==> [50, 61, 66]
# ==> [80, 61, 66]
# ==> [88, 75, 90]
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) # ==> {}
任务
已知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))
在前面,我们学习了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的一个重要特点。
任务
请使用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'])
由于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里面。
任务
由于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'])
我们通过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)
任务
请使用两种方式往空的set中添加以下名字:[‘Jenny’, ‘Ellena’, ‘Alice’, ‘Candy’, ‘David’,
‘Hally’, ‘Bob’, ‘Isen’, ‘Karl’]。
?不会了怎么办
参考答案:
names = ['Jenny', 'Ellena', 'Alice', 'Candy', 'David', 'Hally', 'Bob', 'Isen', 'Karl']
name_set = set()
for name in names:
name_set.add(name)
print(name_set) # ==> set(['Jenny', 'Ellena', 'Alice', 'Candy', 'David', 'Hally', 'Bob', 'Isen', 'Karl'])
name_set.update(names)
print(name_set) # ==> set(['Jenny', 'Ellena', 'Alice', 'Candy', 'David', 'Hally', 'Bob', 'Isen', 'Karl'])
和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 "" , line 1, in <module>
KeyError: 'Jenny'
因此,使用remove()方法,我们需要格外小心,需要提前判断
要remove()的元素是否在set里面,确保存在后,再进行remove。
任务
针对以下set,给定一个list,对于list里面的每个元素,如果set中包含这个元素,就将其删除,否则添加到set里面去。
L = [1, 2, 3, 4, 5, 6, 7, 8, 9, 10]
S = set([1, 3, 5, 7, 9, 11])
?不会了怎么办
参考答案
:Python 操作set的其他方法
不会报错的删除方法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']
和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([])
set提供方法判断两个set之间的关系,比如两个集合set,判断其中一个set是否为另外一个set的子集或者超集。
s1 = set([1, 2, 3, 4, 5])
s2 = set([1, 2, 3, 4, 5, 6, 7, 8, 9])
s1.issubset(s2) # ==> True
s2.issuperset(s1) # ==> True
有时候需要判断两个集合是否有重合的地方,如果使用传统的方法,需要使用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
任务
已知两个集合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)
L = [1, 2, 3, 4, 5, 6, 7, 8, 9, 10]
S = set([1, 3, 5, 7, 9])
for item in L:
if item in S:
S.remove(item)
else:
S.add(item)
print(S) # ==> set([2, 4, 6, 8, 10])
除了使用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']
和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([])
对于什么是函数,其实在前面的学习过程当中,已经多次接触到函数了,比如在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.14xx(其中x是圆的半径)的方式计算出来的,这样写不仅非常麻烦,而且,当我们需要提高圆周率的精度时,把3.14改成3.14159265359的时候,我们需要修改涉及到的三行代码。
因此,我们可以使用函数,把重复的逻辑代码封装起来,这样子,我们就不需要每次计算的时候,都写3.14xx这样的代码了;当我们使用函数的时候,我们只需要定义一次,就可以多次使用。
我们把封装重复逻辑代码的过程就做抽象,抽象是数学中非常常见的概念。
比如:计算数列的和,比如: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不但能非常灵活地定义函数,而且本身内置了很多有用的函数,可以直接调用。
Python内置了很多有用的函数,我们可以直接调用。比如前面求list的长度len()函数等等,都是Python内置的函数,我们经常会使用到它们。
在这个文档里面,列举了Python内置的大部分函数
,同学们有兴趣可以参考看看。
https://docs.python.org/3/library/functions.html
要调用一个函数,需要知道函数的名称和参数,比如求绝对值的函数 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 "" , line 1, in <module>
TypeError: abs() takes exactly one argument (2 given)
在错误提示中,清晰的说明了abs()函数只接收一个参数,但是却传递了两个参数,所以引起了错误。
其次,如果传入的参数数量是对的,但是参数的类型不能被函数所接受,也会引起错误,比如:求绝对值的函数abs(),只有数字才拥有绝对值,如果传递一个字符串进去,将会引起错误。
abs('3.1415926')
# 报错
Traceback (most recent call last):
File "" , 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内置函数,这些内置函数提供了非常有用的功能,大大降低我们编程的难度。
任务
sum()函数接收一个list作为参数,并
返回list所有元素之和。请计算 11 + 22 + 33 + … + 100100。
?不会了怎么办
参考答案:
L = []
x = 1
while x <= 100:
L.append(x * x)
x = x + 1
print(sum(L))
除了使用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)
任务
请定义一个square_of_sum()函数,它接收一个list,返回list中每个元素平方的和。
?不会了怎么办
参考答案:
def square_of_sum(L):
sum = 0
for x in L:
sum = sum + x * x
return sum
print(square_of_sum([1, 2, 3, 4, 5]))
print(square_of_sum([-5, 0, 5, 15, 25]))
在上一节课中,我们在函数里面使用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
任务
定义一个函数sub_sum(),这个函数接收一个列表作为参数,函数返回列表所有奇数项的和以及所有偶数项的和。
?不会了怎么办
参考答案:
def sub_sum(L):
index = 0
sum1 = 0
sum2 = 0
for item in L:
if index % 2 == 0:
sum1 += item
else:
sum2 += item
index += 1
return sum1, sum2
L = [1, 2, 3, 4, 5, 6, 7, 8, 9, 10]
result = sub_sum(L)
print('奇数项的和 = {}'.format(result[0]))
print('偶数项的和 = {}'.format(result[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
任务
请分别使用循环和递归的形式定义函数,求出1~100的和。
?不会了怎么办
参考答案:
# 循环
def my_sumA(n):
sum = 0
index = 1
while index <= n:
sum += index
index += 1
return sum
# 递归
def my_sumB(n):
sum = 0
if n == 1:
sum = n
else:
sum = n + my_sumB(n - 1)
return sum
函数参数是需要传递给函数内部的数据,在前面,我们已经简单接触了函数的参数,现在我们正式来认识它。
函数参数可以是任意的数据类型,只要函数内部逻辑可以处理即可。
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
任务
请实现函数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
字符串的冒泡排序
二叉树的前序、中序、后序及层次遍历
面向对象01类创建~面向对象02类变量
字符串循环左移
动态规划
我们一路奋战,不是为了改变世界,而是为了不让世界改变我们。