前面讲了列表的各种操作: 这里我们要讲的是字典,有人说,字典是Python最强大的功能,能包含万物!让我们进入主题吧!
1、字典dict
字典是python中唯一的映射类型,采用键值对(key-value)的形式存储数据。python对key进行哈希函数运算,根据计算的结果决定value的存储地址,所以字典是无序存储的(自Python3.6后,默认是有序的),且key必须是可哈希的。可哈希表示key必须是不可变类型,如:数字、字符串、元组。
dict 用{}来表示 键值对数据 {key:value} 唯一性
键 都必须是可哈希的 不可变的数据类型就可以当做字典中的键
值 没有任何限制
字典(dictionary)是除列表意外python之中最灵活的内置数据结构类型。列表是有序的对象结合,字典是无序的对象集合。两者之间的区别在于:字典当中的元素是通过键来存取的,而不是通过偏移存取。
什么是可哈希, 目前所学的
可哈希(不可变)的对象有,int、tuple、bool,
不可哈希(可变)的有 list、set、dict、str
注意点:
1、字典的循环中,是不能在里面进行删除的
1.1、增:
dic = {}
dic['name'] = '周润发' # 如果dict中没有出现这个key, 就会新增⼀一个key-value的组 合进dict ,字典里有则会覆盖
dic['age'] = 18
print(dic) # 如果dict中没有出现过这个key-value. 可以通过setdefault设置默认值
dic.setdefault('李嘉诚') # 也可以往⾥里里⾯面设置值.
dic.setdefault("李嘉诚", "房地产") # 如果dict中已经存在了了. 那setdefault将不会 起作⽤用
print(dic)
1.2、删:
ret = dic.pop("jay")
print(ret)
del dic["jay"] # 删除含有这个key的键值对
print(dic)
ret = dic.popitem() # 随机删除,但是在3.6中一般是删除最后一个
dic.clear() # 清空列表
1.3、改:
dic = {"id": 123, "name": 'sylar', "age": 18}
dic1 = {"id": 456, "name": "麻花藤", "ok": "wtf"}
dic.update(dic1) # 把dic1中的内容更更新到dic中. 如果key重名. 则修改替换. 如果不存 在key, 则新增.
print(dic)
print(dic1)
dic[key] = value # 强行赋值
1.4、查:
dic = {'name':'wang li', 'age':23'}
print(dic['name'])
# print(dic['sylar']) # 报错
print(dic.setdefault('name') # setdefault 也可以查取后面的值 又返回值'sylar'
print(dic.get("ok"))
print(dic.get("sylar")) # None ,后面没有返回值就返回None
print(dic.get("sylar", "⽜B")) # ⽜B,如果在字典里没有找到key则会输出牛B
字典的相应的操作:(解构、循环取值)
dic = {"id": 123, "name": 'sylar', "age": 18, "ok": "科⽐比"} print(dic.keys()) # dict_keys(['id', 'name', 'age', 'ok']) 不用管它是什么.当成list来⽤用就⾏ ,这是一种高仿列表
for key in dic.keys(): # 类似于 for i in dic 都是取键
print(key)
print(dic.values()) # dict_values([123, 'sylar', 18, '科⽐比']) 一样. 也当 list来⽤ (高仿列表)
for value in dic.values():
print(value)
print(dic.items()) # dict_items([('id', 123), ('name', 'sylar'), ('age', 18), ('ok', '科⽐比')]) 这个东⻄西也是list. 可以使用print(items,type(item) - > <class 'dict_items'>
for key, value in dic.items(): # ?? 这个是解构 分别取值
print(key, value)
# 解构
a, b = 1, 2
print(a, b)
(c, d) = 3, 4 # 也是可以的
print(c, d)
e, f = [1, 2, 3] # 解构的时候注意数量量必须匹配 ,不匹配会报错
print(e, f)
h,g = '12'
print(h, g) # 字符串也可以
1.5、字典的嵌套:
# 字典的嵌套
dic1 = {
"name": "汪峰",
"age": 18,
"wife": {
"name": '章⼦怡',
"age": 28
},
"children": ['第一个毛孩子', '第二个毛孩子'],
"desc": '峰哥不不会告我吧. 没关系. 我想上头条的'
}
print(dic1.get("wife").get("name")) # 取出章子怡的姓名
print(dic1.get("children"))
print(dic1.get("children")[1] # 取出 “第一个毛孩子"
13.有两个字符串列表,a和b,每个字符是由逗号分隔的一些字符,
(升级题)尽量做得支持扩展
a = [
'a,1',
'b,3,22',
'c,3,4',
'f,5',
]
b=[
'a,2,1',
'b,4',
'd,2',
'e,12',
'g,12,43',
'a,4354,6'
]
# 按每个字符串的第一个值,合并a和b到c
a = [
'a,1',
'b,3,22',
'c,3,4',
'f,5',
]
b=[
'a,2,1',
'b,4',
'd,2',
'e,12',
'g,12,43',
'a,4354,6'
]
g = { i[0]:i for i in a}
for i in b:
if i[0] in g:
g[i[0]]=g[i[0]][2:]+i[1:]
else:
g[i[0]]=i
print(list(g.values()))
get的妙用
2、其他(for、enumerate 、range)
for循环:用户按照顺序循环可迭代的内容
msg = '老男孩python是全国范围内最好的python培训机构'
for item in msg:
print(item)
li = ['alex','银角','女神','egon','太白']
for i in li:
print(i)
dic = {'name':'太白','age':18,'sex':'man'}
for k,v in dic.items():
print(k,v)
enumerate:枚举,对于一个可迭代的(iterable)/可遍历的对象(如列表、字符串),可以设置开始值,默认是1
enumerate将其组成一个索引序列,利用它可以同时获得索引和值
li = ['alex','银角','女神','egon','太白']
for i in enumerate(li):
print(i) # 这里输出的是元组,类似于(0, 'alex'),(1, '银角')……
for index,name in enumerate(li,1):
print(index,name) # 这里输出的是从指定序号1,开始数,切记不是元组了,1 alex 2 银角 ……
range: 指定范围,生成指定数字:
for i in range(1,10):
print(i)
for i in range(1,10,2): # 步长
print(i)
for i in range(10,1,-2): # 反向步长
print(i)
3、is 和 == 的区别:
is是比较内存地址是否一样, 而== 是比较左右两边的值是否一样
当涉及到查看内存地址时,需要使用 id() 来查看
a = 100
b = 100
print(a == b)
print( a is b ) # 显然这里的结果都是True
print(id(a)) # 结果24565845680
print( id(b)) # 结果24565845680 内存地址,相当于门牌号
4、小数据池:
只有数字和字符串类型,小数据池的作用就是节省内存,也就是说在小数据池里面的数据,Python不会再次给它开辟空间,会把已有的内存地址赋值变量
数字:-5 ~ 256 (都是闭区间),有时候在pycharm中,它会隐瞒真相,本来是i = 1000,b =1000 ,i is b 结果为True,在终端就不会这样
这个范围指向的是同一内存地址
字符串: 理论上是没有范围的,也就是说只要有重复的字符串都会指向同意内存,以下的除外:
1.如果含有特殊字符,是不存在小数据池中,如 “wangli@”,“ ale”
2.str(单个的str)*int 且 int>20 不存在小数据池中 (注意列表,元组等其他都是不在小数据类型中,都会生成新的内存地址,且规律很小,有的在pycharm中和在终端中都是不一样的效果 )
5、编码与解码:
基本概念很简单。首先,我们从一段信息即消息说起,消息以人类可以理解、易懂的表示存在。我打算将这种表示称为“明文”(plain text)。对于说英语的人,纸张上打印的或屏幕上显示的英文单词都算作明文。
其次,我们需要能将明文表示的消息转成另外某种表示,我们还需要能将编码文本转回成明文。从明文到编码文本的转换称为“编码”,从编码文本又转回成明文则为“解码”
编码问题是个大问题,如果不彻底解决,它就会像隐藏在丛林中的小蛇,时不时地咬你一口。
那么到底什么是编码呢?
//ASCII
记住一句话:计算机中的所有数据,不论是文字、图片、视频、还是音频文件,本质上最终都是按照类似 01010101 的二进制存储的。
再说简单点,计算机只懂二进制数字!
所以,目的明确了:如何将我们能识别的符号唯一的与一组二进制数字对应上?于是美利坚的同志想到通过一个电平的高低状态来代指0或1,
八个电平做为一组就可以表示出
256种不同状态,每种状态就唯一对应一个字符,比如A--->00010001,而英文只有26个字符,算上一些特殊字符和数字,128个状态也够
用了;每个电平称为一个比特为,约定8个比特位构成一个字节,这样计算机就可以用127个不同字节来存储英语的文字了。这就是ASCII编码。
扩展ANSI编码
刚才说了,最开始,一个字节有八位,但是最高位没用上,默认为0;后来为了计算机也可以表示拉丁文,就将最后一位也用上了,
从128到255的字符集对应拉丁文啦。至此,一个字节就用满了!
//GB2312
计算机漂洋过海来到中国后,问题来了,计算机不认识中文,当然也没法显示中文;而且一个字节所有状态都被占满了,万恶的帝国主义亡
我之心不死啊!我党也是棒,自力更生,自己重写一张表,直接生猛地将扩展的第八位对应拉丁文全部删掉,规定一个小于127的字符的意
义与原来相同,但两个大于127的字符连在一起时,就表示一个汉字,前面的一个字节(他称之为高字节)从0xA1用到0xF7,后面一个字节
(低字节)从0xA1到0xFE,这样我们就可以组合出大约7000多个简体汉字了;这种汉字方案叫做 “GB2312”。GB2312 是对 ASCII 的中文扩展。
//GBK 和 GB18030编码
但是汉字太多了,GB2312也不够用,于是规定:只要第一个字节是大于127就固定表示这是一个汉字的开始,不管后面跟的是不是扩展字符集里的
内容。结果扩展之后的编码方案被称为 GBK 标准,GBK 包括了 GB2312 的所有内容,同时又增加了近20000个新的汉字(包括繁体字)和符号。
//UNICODE编码:
很多其它国家都搞出自己的编码标准,彼此间却相互不支持。这就带来了很多问题。于是,国际标谁化组织为了统一编码:提出了标准编码准
则:UNICODE 。
UNICODE是用两个字节来表示为一个字符,它总共可以组合出65535不同的字符,这足以覆盖世界上所有符号(包括甲骨文)
//utf8:
unicode都一统天下了,为什么还要有一个utf8的编码呢?
大家想,对于英文世界的人们来讲,一个字节完全够了,比如要存储A,本来00010001就可以了,现在吃上了unicode的大锅饭,
得用两个字节:00000000 00010001才行,浪费太严重!
基于此,美利坚的科学家们提出了天才的想法:utf8.
UTF-8(8-bit Unicode Transformation Format)是一种针对Unicode的可变长度字符编码,它可以使用1~4个字节表示一个符号,根据
不同的符号而变化字节长度,当字符在ASCII码的范围时,就用一个字节表示,所以是兼容ASCII编码的。
这样显著的好处是,虽然在我们内存中的数据都是unicode,但当数据要保存到磁盘或者用于网络传输时,直接使用unicode就远不如utf8省空间啦!
这也是为什么utf8是我们的推荐编码方式。
Unicode与utf8的关系:
一言以蔽之:Unicode是内存编码表示方案(是规范),而UTF是如何保存和传输Unicode的方案(是实现)这也是UTF与Unicode的区别。
ASCIII 字母,数字,特殊字符;8位 1个字节表示1个字符。
不支持 中文
支持 英文 数字 符号
英文 8位 1个字节
unicode: 万国码: 兼容ASCIIC码
支持 中文 英文 数字 符号
英文 32位 4个字节
中文 32位 4个字节
Q :0000 0001 0000 0001 0000 0001 0000 0001
中:0000 0101 0000 0001 0000 0001 0000 0001
utf-8: 可变长度的万国码
a: 0000 0001 # 英文 8位 1个字节
欧洲: 0000 0001 0000 0001 # 可变长度,最少8位,中文24位,只要不少于8位
中: 0000 0001 0000 0001 0000 0001
gbk: 国标码 兼容ASIIC码
a: 0000 0001 # 英文 16位 2个字节
中:0000 0001 0000 0001 # 中文 16位 2个字节
在python2的版本中,默认使用ASCIIC
在Python3的版本中,默认使用Unicode,在计算的时间会非常方便 (因为如果 'a哈哈' 中有英文也有汉字,如果用utf-8则要计算那种情况用1个字节,那种情况用4个字节,所以Unicode是很贴近电脑的内存读取的,而utf-8和gbk等是有利于网络传输和数据储存的
编码(encode):Unicode编程你需要的编码,
编码之后的内容是字节(bytes类型)
解码(decode)
ASCIIC码中内容编码之后还是原来的内容(因为牛逼,都是已它为基础的):
s = 'wangli'
b = s.encode('utf-8') # 结果是b 'wangli'
str不能直接存储和传输 (在Python3中 str在内存的编码方式是unicode,表现方式还是str)
必须要转换为bytes类型,才可以。
补充:utf8是如何节约硬盘和流量的:
1 s = "I'm 苑昊"
在上述代码中,我们看到的Unicode字符集是这样的编码表
I 0049
' 0027
m 006d
0020
苑 82d1
昊 660a
每一个字符对应一个十六进制字
计算机只懂二进制,因此,严格按照Unicode的方式,应该这样存储:
I 00000000 01001001
' 00000000 00100111
m 00000000 01101101
00000000 00100000
苑 10000010 11010001
昊 01100110 00001010
这个字符串总共占用了12个字节,但是对比中英文的二进制码,可以发现,英文前9位都是0!浪费啊,浪费硬盘,浪费流量。怎么办?UTF8:
I 01001001
' 00100111
m 01101101
00100000
苑 11101000 10001011 10010001
昊 11100110 10011000 10001010
utf8用了10个字节,对比unicode,少了两个,因为我们的程序英文会远多于中文,所以空间会提高很多
6、编码转换
6.1、字符串转换为bytes
s = '中国'
s_1 = s.encode('utf-8') # 这里使用encode编码,将其用"utf-8"编码成unicode
print(s1) # 执行输出 b'\xe4\xb8\xad\xe5\x9b\xbd'
s = 'hello girl'
s1 = s.encode('gbk')
print(s1) # 执行结果 b'hello girl'
s1 = b'\xe4\xb8\xad\xe5\x9b\xbd'
s2 = s1.decode(encoding='utf-8')
print(s2) #bytes转换位str 结果为 :中国
只有将字符串写入文件或者发送数据时,才需要用到编码转换。为什么? 因为无论网络还是电脑的内存存储都是以bytes为传输单位的
具体的可以查看网址: http://www.ituring.com.cn/book/miniarticle/61192 还有
py3也有两种数据类型:str和bytes; str类型存unicode数据,bytse类型存bytes数据,与py2比只是换了一下名字而已。
下图就是证明Py3的情况:
注意:
人类:bytes ---> Unicode
机器: Unicode ---> binary
但是在Py2中:
py3的编码哲学:
Python 3最重要的新特性大概要算是对文本和二进制数据作了更为清晰的区分,不再会对bytes字节串进行自动解码。文本总是Unicode,由str类型表示,二进制数据则由bytes类型表示。Python 3不会以任意隐式的方式混用str和bytes,正是这使得两者的区分特别清淅。 你不能拼接字符串和字节包,也无法在字节包里搜索字符串(反之亦然),也不能将字符串传入参数为字节包的函数(反之亦然)。 也就是说在Py3中Unicode的表现形式就是str
说到这,才来到我们的重点!
抛开执行执行程序,请问大家,文本编辑器大家都是用过吧,如果不懂是什么,那么word总用过吧,ok,当我们在word上编辑文字的时候,不管是中文还是英文,计算机都是不认识的,那么在保存之前数据是通过什么形式存在内存的呢?yes,就是unicode数据,为什么要 存unicode数据,这是因为它的名字最屌:万国码!解释起来就是无论英文,中文,日文,拉丁文,世界上的任何字符它都有唯一编码对应,所以兼容性是最好的。
好,那当我们保存了存到磁盘上的数据又是什么呢?
答案是通过某种编码方式编码的bytes字节串。比如utf8---一种可变长编码,很好的节省了空间;当然还有历史产物的gbk编码等等。于是,在我们的文本编辑器软件都有默认的保存文件的编码方式,比如utf8,比如gbk。当我们点击保存的时候,这些编辑软件已经"默默 默地"帮 我们做了编码工作。
那当我们再打开这个文件时,软件又默默地给我们做了解码的工作,将数据再解码成unicode,然后就可以呈现明文给用户了!所以,unicode是离用户更近的数据,bytes是离计算机更近的数据。
说了这么多,和我们程序执行有什么关系呢?
先明确一个概念:py解释器本身就是一个软件,一个类似于文本编辑器一样的软件!
总结:
1.在python2默认编码是ASCII, python3里默认是unicode
2.unicode 分为 utf-32(占4个字节),utf-16(占两个字节),utf-8(占1-4个字节), so utf-16就是现在最常用的unicode版本, 不过在文件里存的还是utf-8,因为utf8省空间
3.在py3中encode,在转码的同时还会把string 变成bytes类型,decode在解码的同时还会把bytes变回string
1 goods = []
2 shopping_car = []
3 def fengefu(sun):
4 print(sun.center(20,'+'))
5 def price(name,goods):
6 for i in goods:
7 if i['name'] == name:
8 return i['price']
9 def fun():
10 while True:
11 if confire.lower() =='yes':
12 with open('data2.txt',encoding='utf8',mode='w') as f:
13 fengefu('结算')
14 for i in li:
15 print('您已购买商品%s,单价%s,数量%s'%(i,price(i,goods),li[i]))
16 f.write('您已购买商品%s,单价%s,数量%s'%(i,price(i,goods),li[i]))
17 print('您本次累计消费%s,余额%s'%(sum,int(usr_money)-sum))
18 f.write('您本次累计消费%s,余额%s'%(sum,int(usr_money)-sum))
19 exit()
20 elif confire.lower() == 'no':
21 break
22 def chong_qian(usr_money):
23 usr_money = int(input('充值金额:')) + usr_money
24 return usr_money
25 usr_money = int(input('请充值: '))
26 with open('data.txt',encoding='utf-8',mode='r') as f:
27 a = f.readline().split()
28 for i in f:
29 i = i.split()
30 dic =dict([(a[0],i[0]),(a[1],int(i[1]))])
31 goods.append(dic)
32 fengefu('商品信息')
33 for i,v in enumerate(goods,1):
34 print(i,v['name'],v['price'])
35 print('n',' 购物车结算')
36 print('q/Q',' 退出程序')
37 fengefu('用户输入')
38 while True:
39 usr_chose=input('选择购买的商品: ')
40 if usr_chose.isdigit() and 1 <= int(usr_chose) < len(goods):
41 print(goods[int(usr_chose)-1]['name'],'单价 %s'%goods[int(usr_chose)-1]['price'])
42 shopping_car.append(goods[int(usr_chose)-1])
43 elif usr_chose.lower() == 'n':
44 fengefu('结账信息')
45 li = {}
46 for i in shopping_car:
47 li[i['name']]=shopping_car.count(i) #字典里键值对唯一
48 while True:
49 sum =0
50 for i in li:
51 print(i,'购买次数%s次'%li[i],'单价 %s'%price(i,goods))
52 sum += price(i,goods) * li[i]
53 if sum <= usr_money:
54 fengefu('结账信息')
55 confire = input('输入yes结账,no重新购买: ')
56 fun()
57 li.clear()
58 shopping_car.clear()
59 break
60 else:
61 while True:
62 inp = input('余额不足,1、请删减商品 2、充值>>>')
63 if inp == '2':
64 usr_money = chong_qian(usr_money)
65 break
66 elif inp == '1':
67 shan = input('请输入要修改的商品:').strip()
68 for i in li:
69 if i == shan:
70 while True:
71 num = input('请重新输入数量:').strip()
72 if num.isdigit():
73 li[i] = int(num)
74 break
75 else:print('输入有误,请输入数字')
76 break
77 else:print('输入有误')
78 break
79 else: print('输入有误')
80
81 elif usr_chose.lower == 'q':
82 print('欢迎下次光临!')
83 break
84 else:print('输入有误')
购物车