一、元组(tuple)
列表属于可变序列,可以任意修改列表中的元素。元组属于不可变序列,不能修改元组中的元素。因此,元组没有增加元素、修改元素、删除元素相关的方法。
因此,我们只需要学习元组的创建和删除,元组中元素的访问和计数即可。元组支持如下操作:
1. 索引访问;
2. 切片操作;
3. 连接操作;
4. 成员关系操作;
5. 比较运算操作;
6. 计数:元组长度len()、最大值max()、最小值min()、求和sum()等。
1.1 元组的创建
1、通过()创建元组。小括号可以省略。
a = (10, 20, 30) 或者 a = 10, 20, 30
如果元组只有一个元素,则必须后面加逗号。这是因为解释器会把(1)解释为整数1,(1,)解释为元组。
a = (1)
type(a)=<class 'int'>
a = (1,) #或者 a=1,
type(a)=<class 'tuple'>
2、通过tuple()创建元组
tuple(可迭代的对象)
b = tuple() #创建一个空元组对象
b = tuple("abc")
b = tuple(range(3))
b = tuple([2, 3, 4])
del b #删除元组b
总结:
tuple()可以接收列表、字符串、其他序列类型、迭代器等生成元组。
list()可以接收元组、字符串、其他序列类型、迭代器生成列表。
1.2 元组的元素访问和计数
1、元组的元素不能修改
a = (20, 10, 30, 40)
a[3] = 40
2、元组的元素访问和列表一样,只不过返回的仍然是元组对象。
3、列表关于排序的方法list.sorted()是修改原列表对象,元组没有该方法。如果要对元组排序,只能使用内置函数sorted(tupleObj),并生成新的列表对象。
1.3 zip
zip(列表1、列表2,……)将多个列表对应位置的元素组合成为元组,并返回这个zip对象。
a = [1, 2, 3]
b = [40, 50, 60]
c = [11, 12, 13]
d = zip(a, b, c)
list(d) = [(1, 2, 3), (40, 50, 60), (11, 12, 13)]
1.4 生成器推导式创建元组
从形式上看,生成器推导式与列表推导式类似,只是生成器推导式使用小括号。列表推导式直接生成列表对象,生成推导式生成的不是列表也不是元组,而是一个生成器对象。
通过生成器对象,转化成列表或者元组。也可以使用生成器对象的_next_()方法进行遍历,或者直接作为迭代器对象来使用。不管用什么方式使用,元素访问结束后,如果需要重新访问其中的元素,必须重新创建该生成器对象。
s = (x*2 for x in range(5))
tuple(s) = (0, 2, 4, 6, 8)
list(s)= [] #只能访问一次元素。第二次就为空了,需要再生成一次。
s = (x*2 for x in range(5))
s.__next__() = 0 # 注意 这里是两个下划线
s.__next__() = 2
元组总结
1、元组的核心特点是:不可变序列;
2、元组的访问和处理速度比列表快;
3、与整数和字符串一样,元组可以作为字典的键,列表则永远不能作为字典的键使用。
二、字典
字典是“键值对”的无序可变序列,字典中的每个元素都是一个“键值对”,包含:“键对象”和“值对象”。可以通过“键对象”实现快速获取、删除、更新对应“值对象”。
列表中我们通过“下标数字”找到对应的对象。字典中通过“键对象”找到对应的“值对象”。“键”是任意的不可变数据,比如:整数、浮点数、字符串、元组。但是:列表、字典、集合这些可变对象,不能作为“键”。并且“键”不可重复。
“值”可以是任意的数据,并且可重复。
一个典型的字典的定义方式:
a = {'name':'carol','age':18,'job':'programmer'}
2.1 字典的创建
1、我们可以通过{}、dict()来创建字典对象。
a = {'name':'carol','age':18,'job':'programmer'}
b= dict('name'='carol','age'=18,'job'='programmer')
a = dict([("name","carol"),("age",18)])
c = {} #空的字典对象
d = dict() #空的字典对象
2、通过zip()创建字典对象
k = ['name','age','job']
v = ['carol',18,'student']
d = dict(zip(k,v))
d={'name': 'carol', 'age': 18, 'job': 'student'}
3、通过fromkeys创建值为空的字典
a = dict.fromkeys(['name','age','job'])
{'name': None, 'age': None, 'job': None}
2.2 字典元素的访问
1、通过 [键] 获得“值”。若键不存在,则抛出异常。
a = {'name':'carol','age':18,'job':'programmer'}
a['name']=carol
2、通过get()方法获得“值”。推荐使用。优点是:指定键不存在,返回None;也可以设定指定键不存在时默认返回的对象。推荐使用get()获取“值对象”。
a = {'name':'carol','age':18,'job':'programmer'}
a.get('name')=carol
3、列出所有的键值对
a = {'name':'carol','age':18,'job':'programmer'}
a.items=dict_items([('name','carol'),('age',18),('job','programmer')])
4、列出所有的键,列出所有的值
a = {'name':'carol','age':18,'job':'programmer'}
a.keys()=dict_keys(['name','age','job'])
a.values()=dict_values(['carol',18,'programmer'])
5. len() 键值对的个数
6. 检测一个“键”是否在字典中
a = {'name':'carol','age':18,'job':'programmer'}
“name” in a
True
2.3 字典元素添加、修改、删除
1、给字典新增“键值对”。如果“键”已经存在,则覆盖旧的键值对;如果“键”不存在,则新增“键值对”。
a = {'name':'carol','age':18,'job':'programmer'}
a['address']='西三旗1号院'
a['age']=16
2、使用update()将新字典中所有键值对全部添加到旧字典对象上。如果key有重复,则直接覆盖。
a = {'name':'carol','age':18,'job':'programmer'}
b={'name':'Betty','money':100,'sex':'女'}
a.update(b)
3、字典中元素的删除,可以使用del()方法;或者clear()
删除所有键值对;pop()删除指定键值对,并返回对应“值对象”;
a = {'name':'carol','age':18,'job':'programmer'}
del(a['name'])
b=a.pop('age') #从a中删除“age”键值对,并赋值给b。
a.clear() #调用clear就全部删除了a
4. popitem():随机删除和返回该键值对。字典是“无序可变序列”,因此没有第一个元素、最后一个元素的概念;popitem弹出随机的项,因为字典并没有“最后的元素”或者其他有关顺序的概念。若想一个接一个地移除并处理项,这个方法就非常有效(因为不用首先获取键的列表)。
a = {'name':'carol','age':18,'job':'programmer'}
a.popitem()
2.4 序列解包
1、序列解包可以用于元组、列表、字典。序列解包可以让我们方便的对多个变量赋值。
x,y,z=(20, 30, 10)
x=20 y=30 z=10
(a, b, c)=(9, 8, 10)
[a, b, c]=[10, 20, 30]
a=10 b=20
2、序列解包用于字典时,默认是对“键”进行操作;如果需要对键值对操作,则需要使用items()
;如果需要对“值”进行操作,则需要使用values()。
a={'name':'carol','age':18,'job':'programmer'}
x, y, z=a
x=name y=age z=job #默认对键进行操作
x, y, z=a.items #对键值进行操作
x, y, z=a.values #对值进行操作
2.5 练习
r1={'name':'高小一', 'age':'18', 'salary':'30000', 'city':'北京'}
r2={'name':'高小二', 'age':'19', 'salary':'20000', 'city':'上海'}
r3={'name':'高小五', 'age':'20', 'salary':'10000', 'city':'深圳'}
tb = [r1, r2, r3]
#获得第二行人的薪资
print(tb[1].get("salary")) #获得第二行人的薪资
# 打印表中所有人的薪资
for i in range(len(tb)):
print(tb[i].get("salary"))
# 打印表中所有数据
for i in range(len(tb)):
print(tb[i].get("name"), tb[i].get("age"),tb[i].get("salary"), tb[i].get("city"))
2.6 字典核心底层原理(重要)
字典对象的核心是散列表。散列表是一个稀疏数组(总是有空白元素的数组),数组的每个单元叫做bucket。每个buccket有两部分:一个是键对象的引用,一个是值对象的引用。
由于,所有bucket结构和大小一致,我们可以通过偏移量来读取指定bucket。
1、将一个键值对放进字典的底层过程
a{}
假设字典a对象创建完后,数组长度为8.
我们要把"name"="carol"这个键值对应放到字典对象a中,首先第一步需要计算键"name"的散列值。Python中可以通过hash()来计算。
bin(hash("name"))
0b11000001010000111000110110011000101011011101011101000111101001
由于数组长度为8,我们可以拿计算出的散列值的最右边3位数字作为偏移量,即“001”,十进制是数字1。我们查看偏移量1,对应的bucket是否为空。如果为空,则将键值对放进去。如果不为空,则依次取右边3位作为偏移量,即“101”,十进制是数字5。再查看偏移量为5的bucket是否为空。直到找到为空的bucket将键值放进去。
流程如下:
2、扩容
python会根据散列表的拥挤程度扩容。“扩容”指的是:创造更大的数组,将原有内容拷贝到新的数组中。接近2/3/时,数组就会扩容。
3、根据键查找“键值对”的底层过程
a.get("name")
'carol'
当我们调用a.get("name"),就是根据键“name”查找到“键值对”,从而找到值对象“carol”。
第一步,我们仍然要计算“name”对象的散列值:
bin(hash("name"))
0b11000001010000111000110110011000101011011101011101000111101001
和存储的底层流程算法一致,也是依次取散列值的不同位置的数字。假设数组长度为8,我们可以拿计算出的散列值的最右边3位数字作为偏移量,即“001”,十进制是数字1。我们查看偏移量1,对应的bucket是否为空。如果为空,则返回None。如果不为空,则将这个bucket的键对象计算对应散列值,和我们的散列值进行比较,如果相等,则将对应“值对象”返回。如果不相等,则再依次取其他几位数字,重新计算偏移量。依次取完后,仍然没有找到,则返回None。流程如下:
用法总结:
1、键必须可散列:
(1)数字、字符串、元组,都是可散列的。
(2)自定义对象需要支持下面三点:
① 支持hash()函数;
② 支持通过__eq__()方法检测相等性;
③ 若a==b为真,则hash(a)==hash(b)也为真。
2、字典在内存中开销巨大,典型的空间换时间。
3、键查询速度很快。
4、往字典里面添加新建可能导致扩容,导致散列表中键的次序变化。因此,不要在遍历字典的同时进行字典的修改。
三、集合
集合是无序可变,元素不能重复。实际上,集合底层是字典实现,集合的所有元素都是字典中的“键对象”,因此是不能重复的且唯一的。
3.1 集合创建和删除
1、使用{}创建集合对象,并使用add()方法添加元素。
a={3, 5, 7}
a.add(9)
a={9, 3, 5, 7}
2、使用set(),将列表、元组等可迭代对象转成集合。如果原来数据存在重复数据,则只保留一个。
a=['a', 'b', 'c', 'b']
b=set(a)
b={'b', 'a', 'c'}
3、remove()删除指定元素;clear()清空整个集合
a={10, 20, 30, 40, 50}
a.remove(20)
3.2 集合相关操作
像数学中概念一样,python对集合也提供了并集、交集、差集等运算。
a = {1, 3, 'sxt'}
b = {'he', 'it', 'sxt'}
a|b #并集
{1, 3, 'sxt','he', 'it' }
a&b #交集
{'sxt'}
a-b #差集
{1, 3}
a.union(b) #并集
{1, 3, 'sxt','he', 'it' }
a.intersection(b) #交集
{'sxt'}
a.difference(b) #差集
{1, 3}
四、控制语句
前面学习的变量、数据类型(整数、浮点数、布尔)、序列(字符串、列表、元组、字典、集合),可以看做是数据的组织方式。数据可以看做是“砖块”。
流程控制语句是代码的组织方式,可以看做是“混凝土”。
一个完整的程序,离不开“砖块”,也离不开“混凝土”。他们的组合,才能让我们建立从小到“一个方法”,大到“操作系统”,这样各种各样的“软件”。
PyCharm开发环境的使用
集成开发环境【IDE】:PyCharm、IDLE、wingIDE、Eclipse、IPython
下载地址:Download PyCharm: The Python IDE for data science and web development by JetBrains
4.1 选择结构
选择结构通过判断条件是否成立,来决定执行哪个分支。选择结构有多种形式,分为:单分支、双分支、多分支。流程图如下:
4.1.1 单分支选择结构
if 语句单分支结构的语法形式如下:
if 条件表达式:
语句/语句块
其中:
① 条件表达式:可以是逻辑表达式、关系表达式、算术表达式等等。
② 语句/语句块:可以是一条语句,也可以是多条语句。多条语句,缩进必须对齐一致。
【操作】输入一个数字,小于10,则打印这个数字。
num = input("输入一个数字:")
if int(num)<10:
print(num)
4.1.2 条件表达式详解
在选择和循环结构中,条件表达式的值为False的情况如下:
False、0、0.0、空值None、空序列对象(空列表、空元组、空集合、空字典、空字符串)、空range对象、空迭代对象。
其他情况,均为True。Python所有的合法表达式都可以看做条件表达式,甚至包括函数调用的表达式。
【操作】测试各种条件表达式
if 3: #整数作为条件表达式 print("ok")
a=[] #列表作为条件表达式,由于为空列表,是False if a: print("空列表,False")
s="False" #非空字符串,是True if s: print("非空字符串,是True")
c=9 if 3<c<20: print("3<c<20") if 3<c and c<20: print("3<c and c<20")
if True: #布尔值 print("True")
4.1.3 条件表达式中,不能有赋值操作符“=”
在python中,条件表达式不能出现赋值操作符“=”,避免了其他语言中经常误将关系运算符“==”写作赋值运算符“=”带来的困扰。如下代码将会报语法错误:
if 3<c and (c=20): print("赋值符不能出现在条件表达式中")
4.2 双分支选择结构
双分支结构的语法格式如下:
条件表达式:
语句1/语句块1
else:
语句2/语句块2
【操作】输入一个数字,小于10,则打印该数字;大于10,则打印“数字太大”
num = input("输入一个数字:") if int(num)<10: print(num) else: print("数字太大")
4.2.1 三元条件运算符
python提供了三元运算符,用来在某些简单双分支赋值情况。三元条件运算符语法格式如下:
条件为真时的值 if (条件表达式) else 条件为假时的值
【操作】上一个案例代码,可以用三元条件运算符实现:
num = input("输入一个数字:") print(num if int(num)<10 else "数字太大")
可以看到,这种写法更加简洁、易读。
4.3 多分支选择结构
多分支选择结构的语法格式如下:
if 条件表达式1:
语句1/语句块1
elif 条件表达式2:
语句2/语句块2
……
elif 条件表达式n:
语句n/语句块n
[else:
语句n+1/语句块n+1
]
【注】计算机行业,描述语法格式时,使用中括号[]通常表示可选,非必选。
多分支结构,几个分支之间是有逻辑关系的,不能随意颠倒顺序。
【操作】输入一个学生的成绩,将其转化成简单描述:不及格(<60)、及格(60-79)、良好(80-89)、优秀(90-100)
方法一:(利用完整的条件表达)
score = int(input("请输入分数:")) grade="" if (score<60): grade="不及格" if(60<=score<80): grade = "及格" if(80<=score<90): grade = "良好" if(90<=score<=100): grade = "优秀" print("分数是{0}, 等级是{1}".format(score,grade))
每个分支都使用了独立的、完整的判断,顺序可以随意挪动,而不影响程序运行。
方法二:
score = int(input("请输入分数:")) grade="" if (score<60): grade="不及格" elif score<80: #60-80之间 grade = "及格" elif score<90: grade = "良好" else: grade = "优秀" print("分数是{0}, 等级是{1}".format(score,grade))
多分支结构,几个分支之间是有逻辑关系的,不能随意颠倒顺序。
【操作】已知点的坐标(x, y),判断其所在的象限。
x = int(input("请输入x轴坐标:"))
y = int(input("请输入y轴坐标:"))
a =""
if (x==0 and y==0):
a = "原点"
elif (x==0):
a = "y轴"
elif y==0:
a = "x轴"
elif x>0 and y>0:
a = "第一象限"
elif x<0 and y>0:
a = "第二象限"
elif x<0 and y<0:
a = "第三象限"
else:
a = "第四象限"
print("坐标点({0},{1}), 位于{2}".format(x,y,a))
4.4 选择结构嵌套
选择结构可以嵌套,使用时一定要注意控制好不同级别代码块的缩进量,因为缩进量决定了代码的从属关系。语法格式如下:
if 表达式1:
语句块1
if 表达式2:
语句块2
else:
语句块3
else:
if 表达式4:
语句块4
【操作】输入一个分数。分数在0-100之间。90以上是A,80以上是B,70以上是C,60以上是D。60以下是E。
方法一:
score = int(input("请输入一个分数:"))
if (score>=0 and score<=100):
if score>=90:
print("A")
elif score>=80:
print("B")
elif score>=70:
print("C")
elif score>=60:
print("D")
else:
print("E")
else:
print("输入分数错误")
方法二:
score = int(input("请输入一个分数:"))
degree = "ABCED"
num = 0
if (score>100 or score<0):
print("输入分数错误,请输入一个0-100的分数")
else:
num = score//10
if num<6:
num=5
print(degree[9-num])
五、循环结构
循环结构用来重复执行一条或多条语句。表达这样的逻辑:如果符合条件,则反复执行循环体里的语句。在每次执行完后都会判断一次条件是否为True,如果为True则重复执行循环体里的语句。如图所示:
循环体里面的语句至少应该包含改变条件表达式的语句,以使循环趋于结束;否则,就会变成一个死循环。
5.1 while循环
while 循环的语法格式如下:
while 条件表达式:
循环体语句
【操作】利用while循环打印从0-10数字
num = 0
while num <= 10:
print(num, end="\t")
num += 1
【注】end="\t",表示不换行。
【操作】利用while循环,计算1-100之间数字的累加和;计算1-100之间偶数的累加和,计算1-100之间奇数的累加和。
num = 0
sum_all = 0 #1-100所有数的累加和
sum_even = 0 #1-100偶数的累加和
sum_odd = 0 #1-100奇数的累加和
while num <=100:
sum_all += num
if num%2 == 0:
sum_even += num
else:
sum_odd +=num
num += 1 #迭代,改变条件表达式,使循环趋于结束
print("1-100所有数的累加和", sum_all)
print("1-100偶数的累加和", sum_even)
print("1-100奇数的累加和", sum_odd)
5.2 for循环和可迭代对象遍历
for 循环通常用于可迭代对象的遍历。for 循环的语法格式如下:
for 变量 in 可迭代对象:
循环体语句
【操作】遍历一个元组或列表
for x in (20, 30, 40):
print(x*3, end="\t")
5.3 可迭代对象
python包含以下几种可迭代对象:
1、序列。包含:字符串、列表、元组
2、字典
3、迭代器对象(iterator)
4、生成器函数(generator)
5、文件对象
【操作】遍历字符串中的字符
for x in list("carol007"):
print(x, end="\t")
【操作】遍历字典
a = {'name':'carol','age':18,'job':'programmer'}
for x in a: # 遍历字典所有的key
print(x, end="\t")
for x in a.keys(): # 遍历字典所有的key
print(x, end="\t")
for x in a.values(): # 遍历字典所有的value
print(x, end="\t")
for x in a.items(): # 遍历字典所有的“键值对”
print(x, end="\t")
5.4 range对象
rangez对象是一个迭代器对象,用来产生指定范围的数字序列。格式为:
range(start, end, [,step])
生成的数值序列从start开始到end结束(不包含end)。若没有填写start,则默认从0开始。step是可选的步长,默认为1。如下是几种典型示例:
for i in range(10) 产生序列:0 1 2 3 4 5 6 7 8 9
for i in range(3, 10) 产生序列:3 4 5 6 7 8 9
for i in range(3, 10, 2)产生序列:3 5 7 9
【操作】利用for 循环,计算1-100之间数字的累加和;计算1-100之间偶数的累加和;计算1-100之间奇数的累加和。
num = 0
sum_all = 0 #1-100所有数的累加和
sum_even = 0 #1-100偶数的累加和
sum_odd = 0 #1-100奇数的累加和
for i in range(101):
sum_all = sum_all+ num
if i%2==1:
sum_odd += i
else:
sum_even += i
print("1-100所有数的累加和", sum_all)
print("1-100偶数的累加和", sum_even)
print("1-100奇数的累加和", sum_odd)
5.5 嵌套循环
一个循环体内可以嵌入另一个循环体,一般称为“嵌套循环”,或者“多重循环”。
【操作】打印如下图案:
0 0 0 0 0
1 1 1 1 1
2 2 2 2 2
3 3 3 3 3
4 4 4 4 4
for x in range(5):
for y in range(5):
print(x, end="\t")
print() # 起到换行的作用
【操作】利用嵌套循环打印九九乘法表
for x in range(1,10):
for y in range(1,x+1):
print("{0}*{1}={2}".format(x, y, x*y ), end="\t")
print()
【操作】用列表和字典存储下表信息,并打印出表中高于15000的数据
tb = []
r1 =dict(name="高小一", age=18, salary=30000, city="北京")
r2 = dict(name="高小二", age=19, salary=20000, city="上海")
r3 = dict(name="高小五", age=20, salary=10000, city="深圳")
tb = [r1, r2, r3]
for i in tb:
if i.get("salary")>15000:
print(i)
5.6 break 语句
break语句可用于while和for循环,用来结束整个循环。当有嵌套循环时,break语句只能挑出最近一层的循环。
【操作】使用break语句结束循环
while True:
a = input("请输入一个字符(输入Q或q结束)")
if a=='Q' or a=='q':
print("循环结束,退出")
break
else:
print(a)
5.7 continue语句
continue语句用于结束本次循环,继续下一次。多个循环嵌套时,continue也是应用于最近的一层循环。
【操作】要求输入员工的薪资,若薪资小于0则重新输入。最后打印出录入员工的数量和薪资明细,以及平均薪资。
empNum = 0
salarySum = 0
salarys = []
while True:
s = input("请输入员工的薪资(按Q或q结束)")
if s.upper()=='Q':
print("录入完成,退出")
break
if float(s)<0:
continue
empNum +=1
salarys.append(float(s))
salarySum += float(s)
print("员工人数{0}".format(empNum))
print("录入薪资:", salarys)
print("平均薪资{0}".format(salarySum/empNum))
5.8 else 语句
while、for循环可以附带一个else语句(可选)。如果for、while语句没有被break语句结束,则会执行else子句,否则不执行。语法格式如下:
while 条件表达式:
循环体
else:
语句块
或者:
for 变量 in 可迭代对象:
循环体
else:
语句块
5.9 循环代码优化
虽然计算机越来越快,空间越来越大,我们仍然要在性能问题上“斤斤计较”。编写循环时,遵守下面三个原则可以大大提高运行效率,避免不必要的低效计算:
1、尽量减少循内部不必要的计算
2、嵌套循环中,尽量减少内层循环的计算,即可能向外提。
3、局部变量查询较快,尽量使用局部变量。
【循环代码优化测试】
import time
start = time.time()
for i in range(1000):
result = []
for m in range(10000):
result.append(i*1000+m*100)
end = time.time()
print("耗时:{0}".format((end-start)))
start2 = time.time()
for i in range(1000):
result = []
c = i*1000 # 优化的点在这里
for m in range(10000):
result.append(c+m*100)
end2 = time.time()
print("耗时:{0}".format((end2-start2)))
5.10 其他优化手段:
1、连接多个字符串,使用join()而不使用+
2、列表进行元素插入和删除,尽量在列表尾部操作。
5.11 使用zip()并行迭代
通过zip()函数对多个序列进行并行迭代,zip()函数在最短序列“用完”时就会停止。
【操作】测试zip()并行迭代
names =("carol", "Betty","一","二")
ages = (18, 16, 20, 25)
jobs = ("老师", "程序员", "公务员", "学生" )
for name,age,job in zip(names, ages, jobs):
print("{0}--{1}--{2}".format(name, age, job))
for i in range(4):
print("{0}--{1}--{2}".format(names[i], ages[i], jobs[i]))