列表(list)
序列是Python中最基本的数据结构
序列都可以进行的操作包括索引切片,加,乘,查
Python有6个序列的内置类型,但最常见的是列表和元组
列表(list):
存放任意元素的容器
有序
有索引
可切片
中括号表示[ ]
list1 = [1,3,5,7,9,11,'a','b']#列表的定义
#方式二 li = list(序列对象)
#方式三 lis = ['hello'] * 5
print(len(list1)) #len(可迭代对象)内置函数: 获取列表元素个数
print(list1[2])
print(list1[4])#根据下标取值,下标从0开始,最大为 len(list1) - 1
print(list1[-1])
print(list1[-3])##取出倒数第一和倒数第三的元素
#倒叙的下标从-1开始,至-len(list1)
#添加元素
list1.append(13)#当前列表的末尾添加列表元素13
list1.insert(1,4) #在下标1处添加元素4,后面的元素下标依次+1
list1 += [15,17]#在列表末尾添加数组的元素
list1.extend('三弟')#添加可迭代对象——>[...... '三', '弟']
#删除元素
#list1.remove(3)#删除指定的元素,没有就报错
if 3 in list1:#如果元素3在这个列表中就删除
list1.remove(3)
print(list1)
#删除指定的元素前可以做一个if判断
del list1[2]#利用del删除一个元素(根据下标删除)
del list1 #删除整个列表,从内存上删除,再次调用list1会报错,此变量不存在
list1.pop(1) #根据下标删除,不传参时,默认删除最后一位
list1.clear() #清空列表,剩下一个空列表——> []
#修改元素
list1[1] = 28 #根据下标修改元素
list1[0:3] = 'zhkhdfhl'
#切片修改元素,先删除下标0至2的元素,再将'zhkhdfhl'解构为8个元素,添加到下标0-2的位置,其余元素后移
#查找元素
list1.index(28)#返回元素28所在下标,没有会报错
#获取元素
for element in list1:
print(element)
#列表排序
list1.sort() #默认升序,改变原列表
#sort(key = None,reverse=False)key定义排序规则,reverse 默认升序
list1.sort(key = len,reverse=True)#以长度排序,降序
sorted(list1,key = len,reverse = True)#内置函数(非列表函数):返回一个以长度为规则降序的列表,不改变原来的列表,返回新列表
#列表元素反转
l = [1,2,3]
l.reverse()
print(l)#[3, 2, 1]
列表的切片操作
list1 = ['a','b','c','d','e','f','g']
list2 = list1[2:5]#剪切下标2至5(不包括5)的元素组成一个新列表返回
print(list2)
#完整的剪切整个列表
list3 = list1[:] #创建新的列表,返回整个列表元素
list4 = list1 #没有创建新列表,只是相同引用的复制
print(id(list4))#通过id(list)可以获取对象的id值,list4和list1id值一致,所以是同一个对象
print(id(list1))
#倒叙输出整个列表
list5 = list1[::-1]#返回新的列表
print(list5)
#list5.reverse()一样能倒叙元素,区别是reverse()在原列表上改变元素位置,[::-1]返回新列表
list6 = list(range(10))
#range(10)得到0-9的序列
#list(range(10))将序列转为列表
#[0, 1, 2, 3, 4, 5, 6, 7, 8, 9]
list7 = list6[1 : 7 : 2]
#从下标1至下标7(不包括7)元素片段中,每隔2位取一位
print(list7)#[1, 3, 5]
'''
from collections.abc import Iterable, Iterator
def demo():
j = range(10)#产生一个可迭代的数值序列对象
print(isinstance(j, Iterable))#True
print(isinstance(j, Iterator))#False
for i in range(10):#通过for循环取值
print(i,end=' ')#0 1 2 3 4 5 6 7 8 9
range() 函数:
它表示的是左闭右开区间;
它接收的参数必须是整数,可以是负数,但不能是浮点数等其它类型;
它是不可变的序列类型,可以进行判断元素、查找元素、切片等操作,但不能修改元素;
它是可迭代对象,却不是迭代器;
不支持进行加法拼接与乘法重复(字符串和元组就支持)
(range 对象仅仅表示一个遵循着严格模式的序列,而重复与拼接通常会破坏这种模式)
range(start, stop[, step])
1.step参数默认1;start参数默认0。
2.如果step是正整数,则最后一个元素小于stop。
3.如果step是负整数,则最后一个元素大于stop。
4.step参数必须是非零整数,否则抛出VauleError异常
range(10,0) ==>step参数默认1,当前序列最后一个元素必定大于0(即stop值),不满足上述第二个条件,所以for循环无法输出值,也不会报错
range(10,0,-1) ==>step参数为-1,当前序列个元素大于0时终止 10 9 8 7 6 5 4 3 2 1
'''
列表的复制和嵌套
#== 比较两边内容
#is 比较两边地址
li = [1,2,3,4,5,6,7,8,9]
li2 = li.copy()#复制li列表元素,返回一个新列表
print(li == li2)#True 说明li和li2的内容相同
print(li is li2)#False 说明li和li2的内存地址不同
li2[2] = 'a'#修改li2中元素
print(li == li2)#False 修改后li2内容与li不同
print(li is li2)#False
#列表的嵌套
li = [1,2,3,[4,5,6,7],8,9]
li2 = li.copy()
print(li == li2)#True 说明li和li2的内容相同
print(li is li2)#False 说明li和li2的内存地址不同
#修改嵌套列表中的普通元素
li2[2] = 'a'#修改li2中元素
print(li == li2)#False 修改后li2内容与li不同
print(li is li2)#False
#以上结果与非嵌套列表结果一致
#修改嵌套列表中复杂元素内容
li2[3][2] = 'a'
print(li2)
#[1, 2, 3, [4, 5, 'a', 7], 8, 9]
print(li == li2))#True 说明li和li2的内容相同
print(li is li2)#False 说明li和li2的内存地址不同
#对于嵌套列表中复杂对象的拷贝,给的是引用
print(li)
#[1, 2, 3, [4, 5, 'a', 7], 8, 9]
#li2只拷贝了li中第一层数据,对于第二层数据采用拷贝引用的方式,所以第二层数据的变化
#会使得两个列表中第二层数据同步变动,这就是浅拷贝
#如何实现深拷贝呢?连第二层也拷贝
import copy #引入copy模块
li2 = copy.deepcopy(li) #调用copy模块中方法实现深拷贝
对于不可变数据类型(数字,字符串,元组。。)深浅拷贝没有区别
元组
元组(tuple):
只读列表
可切片
不能增删改元素
嵌套元组中列表中的元素可以增删改
元组在创建时间和占用空间上优于列表
小括号表示 ()
li = (1,2,3,[4,5,6,7],8,9)
li[3][1] = 58
print(li)#(1, 2, 3, [4, 58, 6, 7], 8, 9)
li = ([4,5,6,7])
print(type(li))#<class 'list'> 只有一个元素时,此元组引用的类型就是元素的类型
li = ([4,5,6,7],)#一个元素时加个逗号就是元组类型
print(type(li))#<class 'tuple'>
#元组不可修改,可以拼接
li = (3,4,5)
li2 = (6,7)
print(li + li2)#返回新元组
#排序
li = (8,6,5,4,9,2,4,5,6,7,3)
li1 = sorted(li)
print(li1)#[2, 3, 4, 4, 5, 5, 6, 6, 7, 8, 9]
#反转元素
li = (3,4,5,6,7)
li1 = reversed(li)#返回一个可迭代对象<reversed object at 0x0000000002668EC8>
for i in li1:
print(i ,end = ' ')
#元组转为列表
l = list(li)
#列表转为元组
tu = tuple(l)
字典
字典(dict)
映射数据类型
可存储任意对象
每一个元素都是以键值对形式存在
表示形式:dic = {'key1':'value1','key2':'value2'}
3.5版本以前无序(输入输出顺序),3.6以上有序
key必须是不可变数据类型,value任意
不可变数据类型:
元组,bool,int,str ,float 可哈希
不可变数据类型的优点就是内存中不管有多少个引用,相同的对象只占用了一块内存
缺点就是当需要对变量进行运算从而改变变量引用的对象的值时,
由于是不可变的数据类型,所以必须创建新的对象,这样就会使得内存地址一次次的改变,
创建了一个个新的对象,不过不再使用的内存会被垃圾回收器回收
i = 1 #int类型
j = 1
print(id(i) == id(j))#True 值相同,引用的内存地址是一致的,是同一个对象
i +=1 #值的变化相当于新建了一个对象
print(id(i) == id(j))#False
当改变一个对象的值,如果其id值变化那么对象是不可变数据类型,反之id值不变,那么对象是可变类型
可变数据类型:
list,dict,set 不可哈希
对于可变数据类型来说,具有同样值的对象是不同的对象
l1 = [1,2,3] #列表
l2 = [1,2,3]
print(id(l1))#41043912
print(id(l1) == id(l2))#False 同样值的对象是不同的对象
l1.append(4)#值的变化不会导致内存地址的变化,不会创建新的对象
print(id(l1))#41043912
为什么字典 key 必须是不可变的?
字典从键值计算的哈希值来查找键,字典查询速度快
不可变数据类型:值相同,id值只有一个,便于哈希查找value
可变数据类型:值相同,多个id值,哈希会抛出异常
可哈希对象:
必须实现__hash__() 与 eq() 方法
有个hash 值,这个值在整个生命周期都不会变化
可以进行相等比较
例如往集合中添加对象时会用__hash__() 方法来获取hash值,看它是否与集合中现有对象的hash值相同,如果相同则会舍去不加入,如果不同,则使用__eq__() 方法比较是否相等,以确定是否需要加入其中。
我们自定义的类的实例对象默认也是可哈希的(hashable),而hash值也就是它们的id()。
总结:一个对象可哈希,通过它的id值可以在其生命周期内确定它的唯一性。
字典的增删改操作
dic = { 1 : 'name' , 2 : 'age' }
#添加元素
#dic[key] = value
dic[3] = 'school'#如果已经存在当前key,会覆盖之前的value值
print(dic)#{1: 'name', 2: 'age', 3: 'school'}
#添加元素
# setdefault(key,value)
#若存在当前key就不添加,并返回对应的value值;否则,添加进字典,返回新添加的value值
ret = dic.setdefault(3, 'number')
print(ret)#school
print(dic)#{1: 'name', 2: 'age', 3: 'school'}
#删除元素
dic = { 1 : 'name' , 2 : 'age' , 3: 'school'}
#元素的删除 根据key 删除键值对,并返回其中的值 ; key不存在会报错(KeyError)
value = dic.pop(2)
print(value)#age
#pop(key,'错误提示语') pop的第二种用法,传入两个参数,当key不存在时,会输出'错误提示语',而不是报错
value = dic.pop(4,'当前字典不存在这个key')#当前字典不存在这个key
#删除元素
#popitem() 没有参数,返回删除的键值对(以元组的形式)
#3.5版本以前 随机删除一个元素 3.6版本以后 删除最后一个元素 并返回删除的键值对
value = dic.popitem()
print(value)#(3, 'school')
#删除元素
dic = { 1 : 'name' , 2 : 'age' , 3: 'school'}
del dic[2] #利用del 根据key删除元素
print(dic)#{1: 'name', 3: 'school'}
del dic #删除整个字典(引用 + 内存)
print(dic) #此处会报错,因为dic变量在上一步时已经从内存中删除
#清空字典
dic = { 1 : 'name' , 2 : 'age' , 3: 'school'}
dic.clear()#清空字典,无返回值,dic成为空字典
print(dic)#{}
#修改元素
dic = { 1 : 'name' , 2 : 'age' , 3: 'school'}
#方式一
dic[3 ] = 's'#既能添加元素,也能修改(key一致时覆盖原先的value)
#方式二 字典的update( dict ) 传入一个字典参数,key一致就覆盖原value值,key不一致就是添加新元素
dic2 = {3:'arr'}
dic.update(dic2)
print(dic)#{1: 'name', 2: 'age', 3: 'arr'}
#查询元素
dic = { 1 : 'name' , 2 : 'age' , 3: 'school'}
#方式一 print(dict引用名)
print(dic)
#方式二 for循环
for i in dic :# 默认获取字典所有的key
print(i,end=' ')
print(dic.keys())#dict_keys([1, 2, 3]) 以列表形式获取所有的key值
for i in dic.keys() :
print(i,end=' ')
print(dic.values())#dict_values(['name', 'age', 'school']) 以列表形式获取所有的value值
for i in dic.values() :
print(i,end=' ') #name age school
#输出键值对
print(dic.items())#dict_items([(1, 'name'), (2, 'age'), (3, 'school')])
#以列表形式获取所有的元组形式的键值对
for i in dic.items() :
print(i,end=' ')#(1, 'name') (2, 'age') (3, 'school')
#变种
for k,v in dic.items() :
print(k,v,end=' ')
#1 name 2 age 3 school
dic = { 1 : 'name' , 2 : 'age' , 3: 'school'}
#获取单个value值
value = dic[key]
#获取单个value值 有key值就输出对应的value值,没有就输出提示语句,这样就不会报错
value = dic.get(4,'没有这个key')
print(value)#没有这个key
python输出语句的版本差异
python 2.7以下
print a
python 3
print(a)
#例一
i = 10 / 3
print i #确定i的值
#根据print i可知当前版本为2.x
#2.x中除法是向下取整数
#3.x和现实中除法一致
#所以i 为3
#例二
k = 1000
count = 0
while k > 1:
count +=1
print k #由此可知这是python2.x
k = k / 2 #向下取整数
else:
print()
print(count)
#count值为多少 9
#python3 中为10
= 赋值
==比较值是否一致
is比较内存地址是否一致
id()获取对象内存地址
hash() 获取可哈希对象的哈希值
hash(id(不可哈希对象)) == id(不可哈希对象)
print(id(1+2) == id(3))#True
print(id(4-1) == id(3))#True
print(id(1.5 * 2) == id(3))#False
print(id(6 / 2) == id(3))#False
print(id(4) == id(2 * 2))#True
print(id(8 / 2) == id(4))#False
#int类型(不可变类型)加减乘法得到的值一致时,是同一个对象
print(id(int(3)) == id(int(3.0)))#True
print(id(1.5 * 2) == id( 9 / 3))#True
print(id(3) == id( 9 / 3))#False
print(id(int(3)) == id( int(9 / 3))) #True
集合
集合(set):
可变数据类型(不可哈希)
无序的,不重复(自动去重)
元素必须是不可变类型(可哈希)
dict(3.5版本以前)和set是无序的
字典和集合无序的实现方式是hash表
通过hash值来将对象放入hash表中
从而达到无序的操作(对象的hash值是不断变化的)
s = {'2','5','1'}
print(hash('2')) #-4233449892055168110
print(hash('5')) #6824838064547305647
print(hash('1')) #7326123415472050427
print(s) #{'2', '1', '5'}
#每一次执行代码时,set中元素的hash值都不同,字符在哈希表中位置也不同,这就实现了集合的无序
#int类型的哈希值就是它自己
print(hash(5)) #5
#纯数字元素的集合输出是有序的,无论输出多少次,结果的顺序是不变的
e = {2,5,6,9,8.44,7.5,5,56,21,15,12,11}
#{2, 5, 6, 7.5, 8.44, 9, 11, 12, 15, 21, 56}
集合的相关操作
#集合的定义
set1 = {'3','1','1','1','1','1','5'}
print(set1) #{'3', '5', '1'}
#将列表转为集合
set2 = set(['3','1','1','1','1','1','5'])
print(set2) #{'3', '5', '1'}
# set3 = {[2,3],4,5}
# print(set3) 报错TypeError,集合元素必须是不可变类型
#创建一个空字典
dictNone = {}
#创建一个空集合
setNone = set()
#添加元素
se = set()
se.add('张三')
print(se) #{'张三'}
se.update('abcd')
se.update(['1','2','3','4'])
print(se) #{'d', 'a', '2', '3', 'b', '张三', '1', 'c', '4'}
#删除元素
ret = se.pop() #删除第一个元素,并返回这个元素
print(se) # {'a', '2', '3', 'b', '张三', '1', 'c', '4'}
print(ret) # d
#按元素删除,无返回值 ;没有这个元素时会报错,可以与if 配合使用
se.remove('a')#{'3', '2', 'b', '4', 'c', 'd', '1', '张三'}
#清空集合,剩下一个空集合
se.clear()
print(se) #set()
#从内存中删除集合
del se
#集合中的元素都是不可变元素,所以没有修改元素的方法
#查询元素,集合无序,无法根据索引查询
print(se) # {'2', 'a', 'c', '3', '4', '1', '张三', 'b', 'd'}
for i in se:
print(i,end=' ') # 2 a c 3 4 1 张三 b d
#多个集合间的应用
s1 = {'1','2','3','4'}
s2 = {'5','6','3','4'}
s3 = {'3','4'}
print(s1&s2)#交集 {'3', '4'}
print(s1|s2)#并集 {'5', '6', '1', '2', '4', '3'}
print(s1^s2)#反交集 {'5', '6', '1', '2'}
print(s1-s2)#差集 {'1', '2'}
print(s2-s1)#差集 {'5', '6'}
print(s1 < s3) #超集 s3是否含有s1所有元素 False
print(s3 < s1)#超集 s1是否含有s3所有元素 True
#列表的去重
lists = [2,2,3,3,4,4,5,5]
se = set(lists)
lists = list(se)
print(lists) #[2, 3, 4, 5]
#将可变类型的集合转为不可变类型的集合
#set无序排序且不重复,是可变的
#frozenset是冻结的集合,它是不可变的,存在哈希值,好处是它可以作为字典的key
#也可以作为其它集合的元素。缺点是一旦创建便不能更改,没有add,remove方法。
se = {'5', '6', '1', '2'}
f = frozenset(se) #传入一个集合作为参数
print(se) #{'6', '2', '5', '1'}
print(f) #frozenset({'6', '2', '5', '1'})
print(f == se) #True
print(id(f) ==id( se)) #False
数据结构间的转换
#列表
li = list(可迭代对象)
#list转str
''.join(li)
#str转list
str.split('条件')
#元组
tu = tuple(可迭代对象)
#集合
se = set(可迭代对象)
#字典
se={('3',1),('2',2),('1',3)} # 用元组存放键值对,因为转为字典时,key是不可变元素
di = dict(se)
print(di)#{'2': 2, '3': 1, '1': 3}
推导式
推导式是可以从一个数据序列构建另一个新的数据序列的结构体
列表(list)推导式
字典(dict)推导式
集合(set)推导式
列表推导式
#列表推导式
li = [ i for i in range(30) if i % 2 == 0]
print(li)#[0, 2, 4, 6, 8, 10, 12, 14, 16, 18, 20, 22, 24, 26, 28]
'''
li = [ i for i in range(30) if i % 2 == 0] 获取30以内所有的偶数
分解为正常的代码:
li = []
for i in range(30):
if i % 2 == 0:
li.append(i)
多行的代码用推导式一行就解决了
'''
# 示例二
li = [i ** 2 for i in range(6)] #0-5的平方
print(li) #[0, 1, 4, 9, 16, 25]
# 示例三
li = [x + y for x in 'abc' for y in '123']#拼接字符串
print(li) #['a1', 'a2', 'a3', 'b1', 'b2', 'b3', 'c1', 'c2', 'c3']
# 示例四
names = [['tom','jederom','jack','joee'],['jiu','jennuen','evawe','sheryy',]]
ret = [ li.capitalize() for name in names for li in name if li.count('e') == 2]
print(ret)
#生成器
l = (i for i in range(30) if i % 2 == 0)
print(l) #<generator object foo15.<locals>.<genexpr> at 0x00000000026C7248>
for i in l:
print(i,end=' ')
#生成器以()包裹,推导式以[]包裹
#生成器表达式返回一个对象
#需要值的时候可以用for循环获取,获取值的时候再去执行表达式,一次获取一个值,取出值后删除这个值,取下一个值
#所有值只能取一次,之后l对象内容为空;表示式可以反复获取值
#生成器不占用额外的内存空间来存储数据
#表示式一次性将所有值获取,占用大量空间
#生成器后续会和迭代器详细讲解
字典推导式
#字典推导式
#例一 将key与value的值对调,注意key和value此时必须都是不可变类型数据
dic = {'name' : 'zs' , 'age' : '23'}
ret = {dic[k] : k for k in dic} #for k in dic 返回字典所有的键
print(ret) #{'zs': 'name', '23': 'age'}
#例二 合并大小写key对应的值,将key改为小写
d = {'d':10,'D':12,'g':20,'G':22,'q':30,'Q':32,'y':40,'Y':42}
r = {k.lower(): d.get(k.lower(),0)+d.get(k.upper(),0) for k in d }
#k.lower() 将key转为小写 (字符串的lower方法)
#d.get(k.lower(),0) 根据小写的key找到对应的值并返回这个值,找不到就返回0
print(r) #{'d': 22, 'g': 42, 'q': 62, 'y': 82}
集合推导式
#集合推导式:去重
s = {x**2 for x in [-3,-2,-1,0,1,2,3]}
print(s)
简单实例
斗地主
'''
练习: 斗地主
1.集合存储键值对
键:有序数字0-53 存储的牌的顺序 0是方块3 , 1是梅花3.。。。
之后通过key值的大小来进行手牌的排序
值:扑克的面值
2.列表存储键,打散,分给3个人,留3张底牌
Version:0.1
Author:Hu
'''
import random
def foo():
maps = {} #存储的牌的顺序数字
num = [] #将牌的顺序数字存入,便于洗牌(打乱num中的顺序,然后根据maps[num]取出对应的手牌相当于扑克被打乱的效果)
str1 = [y + x for x in '3456789*JQKA2' for y in '♠♥♣♦'] #根据推导式获取扑克的面值
for i in range(0,str1.__len__()): #str1.__len__() 推导式的长度(元素的个数)
num.append(i) #将推导式的元素存入列表和字典中
maps[i]=str1[i]
maps[28]='♠10' #其中10会作为两位字符,无法成为♠10,只会成为♠1和♠0,用*占位代替10 ,再改回来
maps[29]='♥10'
maps[30]='♣10'
maps[31]='♦10'
flag = str1.__len__() #获取当前长度数值,作为小王的key,第二大的数 对应的就是小王
num.append(flag)
maps[flag]='小王'
flag += 1 #当前长度数值+1,作为大王的key,最大的数 对应的就是大王
num.append(flag)
maps[flag]='大王'
#打散
random.shuffle(num) #random随机模块提供的shuffle方法,传入一个列表作为参数,随机打散元素的位置
#分给3个玩家
user1 = [] #创建3个列表作为玩家的手牌区,存放手牌
user2 = []
user3 = []
care = [] #底牌区,存放三张底牌
for lens in range(num.__len__()):
if (lens >= (num.__len__() -3) ): #将打散后的扑克的序列数值组成的列表中,最后三位数放入底牌区
care.append(num[lens])
elif (lens % 3) == 0 :
user1.append(num[lens]) #将打散后的扑克的序列数值组成的列表中,对3个玩家进行发牌,实际发的是序列数值0-53,不是真实的牌的内容
elif (lens % 3) == 1 :
user2.append(num[lens])
elif (lens % 3) == 2 :
user3.append(num[lens])
user1.sort() #打散后玩家获取的扑克的序列数值,先排序
user2.sort()
user3.sort()
care.sort()
#print(maps)
print('张三的手牌是: ' )
for q in range(user1.__len__()): #取出玩家手牌区的序列数字([user1[q])作为 key 来获取字典中值 maps[user1[q]] 真正扑克的内容
print(maps[user1[q]],end = ' ')
print()
print('李四的手牌是: ' )
for e in range(user2.__len__()):
num = user2[e]
print(maps[num],end = ' ')
print()
print('王五的手牌是: ' )
for r in range(user3.__len__()):
print(maps[user3[r]],end = ' ')
print()
print('底牌的手牌是: ' )
for r in range(care.__len__()):
print(maps[care[r]],end = ' ')
if __name__ == '__main__':
#__name__(两个下划线) 内置变量,在当前执行的函数中有这个变量,__name__的值就是__main__
#在调用外部模块中,外部模块中有__name__,_name__的值就是就是外部模块的名字(不包含后缀名)
foo()