0
点赞
收藏
分享

微信扫一扫

Python学习之哈希表

IPython 增强了CPython

1.IPython中有很多内置的变量
	_ : 表示前一次输出
	__ : 表示倒数第二次输出
	___ :表示倒数第三次输出
	_dh:目录历史
	_oh : 输出历史

2.shell命令
	使用 !command 执行shell命令

3.魔术方法
	%cd 	
	%pwd:当前路径

	%timeit:测试代码运行等待时间和效率
	%%js:可以运行js脚本

集合Set

集合,简称集。由任意个元素构成的集体。高级语言都实现了这个非常重要的数据结构
类型。
Python中,他是可变的、无序的、不重复的元素的集合
	无序代表不能进行进行索引访问

如何定义初始化集合: 1.s=set() 空集 2.s=set(range(5)) set('abcde') set(b'abc123') 这种都属于给集合中方可迭代对象,遍历一下过滤重复的元素放入集合 {0, 1, 2, 3, 4} {'d', 'e', 'a', 'c', 'b'} {97, 98, 99, 49, 50, 51} 对应输出这3个集合 3.s={1,2,range(0,5)} 输出 {1, 2, range(0, 5)} {'a',b'acb',(),'a','b'} 输出 {'a', 'b', (), b'acb'} 切记set有无序性和去重特性

4.列表list是不能哈希类型。集合中不能存放不可哈希类型的数据 
	不可哈希类型: list byterarray  set 目前是这三个

集合的性质 1.去重:内容相等的和地址相等的都会去重保留一个 2.无序:每次输出顺序都不一样,所以不可索引 3.可哈希

对集合增加元素:
	s={'a','b','c','d'}
		print(s) 输出 {'c', 'a', 'b', 'd'}
	s.add('e')
		print(s) 输出 {'a', 'b', 'd', 'e', 'c'}
	s.add('e')
		如果加入的元素存在了,什么都不存在了

4.可迭代,就是能遍历一遍
5.批量增加
	s={1,2,'a','b',(3,4)}
	s.update(range(5)) 
		输出 {'b', 1, 2, 'a', 0, (3, 4), 3, 4}
      update()中放的是可迭代对象,有重复的就不要,放入不重复的元素

    s.update(range(5),range(10,15),{1,'a','c'},['a','d'])
    	输出 
    {0, 1, 2, 3, 'a', (3, 4), 4, 10, 11, 12, 13, 14, 'd', 'b', 'c'}

	update的时候,可以把其他的多个可迭代对象中的元素,挨着个的拿出来,拿出
    来之后,依次的去塞入s集合当中但是要去重,这个是就地修改

	s+{100,1,2} 集合不支持这样的加法

5.集合元素的删除
	s.remove(14) #14是元素值,移除。
		这个移除方法效率非常好,因为是非线性结构。这个移除看似是按值移除了
		背后的原理是哈希值对应的问题。remove()效率极高 O(1).它背后不是按
		照线性结构那种遍历。

	s.discard('e') :e如果不存在集合中,也不会报错 	
					有就移除,没有就不报错,效率极高O(1)
	s.clear() :标记一下清零
	s.pop() 随机的弹出一个值,一种无序的弹出。随机pop

集合的修改: 对于集合来说没有修改这一说,因为集合中存在的值,都是唯一的值。它这里的修改就是删除在新加元素

集合的查找: 非线性结构不可索引

集合的遍历问题: s={0, 1, 2, 3, 'a', (3, 4), 4, 10, 11, 12, 13, 14, 'd', 'b', 'c'}

for x in s:
	print(x)

判断一个元素有没有在集合中,可以使用in成员操作符就能判断
	0 in s 0是不是在s集合中 在的话 返回true

in 在列表中效率高吗?in 在set类型中效率高吗?
	in 在列表中效率不高,因为需要从前向后遍历 时间复杂度为O(n)
	in 在set中时间复杂度高,效率好 O(1)	

说明,如果内存中有线程列表,你需要做的操作,里面用到了遍历,O(n) 数据 
规模越大效率越低下。
列表这种数据结构不是让你快速检索的,除非规模很小,比如10个。

如果内存中有集合,不管数据规模多大,他的检索元素时间,不随着规模变化O(1)
效率极高 纳秒级别ns返回。

集合他的最典型应用,我们的值他是单值。我们单值还想去重,就放入集合。效率极高

爬虫的url 100万个url,你怎么知道那个被爬过,用集合存储。方便查询
有超大规模的集合就是用来给你判断的,你这个数据有没有在里面

只需要确定数据在或者不在请使用集合

hash内部原理
	就是哈希计算哪里知识点 参考数据结构

请问:列表和set遍历,谁效率高?
	都不高,都是O(n),尽量不要遍历。但凡是遍历都没有效率高的
	遍历效率只和数据规模有关,规模越大效率越低


可哈希类型:
	数值型 int float complex
	布尔型 True False
	字符串 String bytes
	tuple
	None

不可哈希类型:
	 list byterarray  set

Set 集合运算:

只要是容器都可以使用in

集合的概念:
	全集

	子集

	真子集和真超集

	并集

	交集

	差集


并集运算:
	a=[1,2,3]
	b=[2,3,4,5]

	a并b->a|b 输出
		{1,2,3,4,5}

	a.union(b) 等价于a|b
		不做本地修改
交集运算:
	空集表示: set()
	a=[1,2,3]
	b=[2,3,4,5]
	a&b 交集输出 {2,3}

差集:A-B 和 B-A是不同的
	A-B=A-(A&B)

	a-b

字典Dict

Dict既Dictionary 在别的编程语言中也称为mapping映射。它为什么叫做映射呢?
	是因为字典中每一个元素存储的都是一个键值对 key-value
	key :唯一的、不重复的

字典是可变的、无序的、key不重复的	
字典非常重要

1.字典的初始化 dict set list tuple 既是类名也是内建函数

dict():空字典、可变化
{} :空字典

d1={'a':1,'b':2} 
	输出为 {'a': 1, 'b': 2}

d2=dict(d1)  dict(mapping) 这个就是mapping的含义
	d2输出为 {'a': 1, 'b': 2}

dict(iterable) 使用可迭代对象进行初始化	

d3=dict([ ('a',3),['b',100] ])
	只能放二元结构
		这个会输出:{'a': 3, 'b': 100}	
	由于列表和元组都是有序的,从里面拿出的第一个元素直接拿来做key,
  第二个元素直接做 value

d3=dict([ ('a',3),['b',100],{3,100} ])
	输出{'a': 3, 'b': 100, 3: 100}
	这个{3,100}是一个集合,集合不一定有序。所以谁做key、谁做value是不一定的

d4 = dict(a=100,b=200,c=300)	
	输出 {'a': 100, 'b': 200, 'c': 300}

d5= dict([(1,100),[2,200],('a',111)])
	输出 {1: 100, 2: 200, 'a': 111}
d5= dict([(1,100),[2,200],('a',111)],a=222)	
	输出
	    {1: 100, 2: 200, 'a': 222}
	    这个的意思就是a为222

d6=dict(d4,c=333,d=444)	    
	输出 {'a': 100, 'b': 200, 'c': 333, 'd': 444}
		后面会覆盖前面

直接用大括号构造字典 {'a': 100, 'b': 200, 'c': 333, 'd': 444}		

对字典中的key要求,唯一且不重复

dict.fromkeys(range(5))
	输出  {0: None, 1: None, 2: None, 3: None, 4: None}
	创建了5个Key 0 1 2 3 4。它的每一个值都是None
dict.fromkeys(range(5),0)
	输出 {0: 0, 1: 0, 2: 0, 3: 0, 4: 0}
		vlaue值默认为0 这个参数的含义

d7=dict.fromkeys(range(5), [])
	输出 {0: [], 1: [], 2: [], 3: [], 4: []}		
		缺省值合法就行

2.字典元素的访问 对于一个字典来讲,最重要的就是key了

d6=dict(d4,c=333,d=444)	    
	输出 {'a': 100, 'b': 200, 'c': 333, 'd': 444}

d6['b']
	输出 200	 写key名称进行输出
d6['c']
	输出 333
d6['e'] 没有这个key,会报错	

d6.get('a') 
	输出 100
d6.get('e') 这里不会报错,即使这个key 'e'不存在
	会返回一个None
d6.get('e',222)	
	'e'在字典中没有,就给222 进行输出,但是不会对原字典进行修改

if d6.get('e'):
	pass        这里要注意None的问题

d6.setdefault('f')
	输出 {'a': 100, 'b': 200, 'c': 333, 'd': 444, 'f': None}
	没有'f'会进行添加,且value为 None

d6.setdefault('f',1234)	
	key不存在,使用缺省值凑成kv对,写入字典;key存在,相当于get

del d6['f']	
	删除 kv对

d6.setdefault('f',1234)			
	key不存在,使用缺省值凑成kv对,写入字典,同时返回这个写入的value值;
	key存在相当于get

d6.setdefault('f',345)
	这个语句的意思呢,先找'f'在不在字典里,'f'在取到1234。如果不在
	把345和f做kv对匹配并且输出		

	{'a': 100, 'b': 200, 'c': 333, 'd': 444, 'f': 1234}

元素访问三种
	d[key]
		有keyError 问题

	get(key[default])

	setdefault(key[default])

3.对字典进行新增元素 d6['g']=[1,2,3] d6.update(d5) 用另一个字典更新 用mapping更新 有则覆盖,没有则新增,就地修改,以后面为准

封装和解构 t = 1,2 会直接把t封装成一个元组 等价于 t=(1,2). 这个就是封装的意思

t=(1,2)
	x,y =t
	x输出1 y输出2
它相当于把t这个元组,	拆开,分别于左边的标识符一一对应

x, y = 3, 4
	x是3  y是4 输出

右边先封装成元组(3,4),然后为了赋值给x和y,解构后,依次对应标识符 

a=100
b=200
交换两个值
	t=a
	a=b
	b=t
a=100
b=200
a,b=b,a  交换 右边元组(200,100)
	第一个给第一个,第二个值给第二个值

解构:
	a,b=100,200  
		解构就是a=100  b=200  先把100 和200 封装为元组,在依次对应赋值
	a,b = (3,4)	
	a,b = [5,6]
	a,b = 'ab'
		a='a' b='b'
	a,b= b'xy'	
	
	a,b={7,8}
		这个set集合,分别给a和b,不能确定谁给谁

	a,b ={'m':9,'n':10}	字典
		a='m' b='n' 字典第一层拿到的的key值
	a,b= range(2)	
		输出 a=0 b=1

	a,b= range(3)	
		左侧的标识符要和右边的值要一一对应

	[a,b]=1,2
		右侧要求是可迭代对象,这里会报错	
	(a,b)=[1,2]
		a=1 b=2

	a, *b=range(5)
		b前面加 * 是什么意思呢?	尽可能收集数据放到一个列表中

		输出 a=0 b=[1,2,3,4]

	a, *b,c =range(5)
		尽可能收集剩余数据放到一个列表中
		输出 a=0 b=[1,2,3] c=5 
		a和c占用一个,其余给带星号的b,让他创建列表

	*b=range(5)
		这里会报错,不能这样使用
		b=range(5) 加星号是没有意义的	

	a,*b,*c,d =range(10)
		这个会输出什么?
		a=0 d=9 *b 和*c输出什么? 这里会报错的。不知道*b拿多少
			*c拿多少

	a,*b,c=range(2)		
		a=0 b=[] c=1 

	剩余变量不能单独使用
		*n=range(5)  这样写都是错误的	

	_, *a, _ = range(5)
		_:输出 4
		*a: 输出 [1,2,3]
		_: 输出 4

		下划线是同一个标识符,第一次赋值0,第二次赋值4 所以才才输出4

count=[0]*20
print(count)		
	输出 [0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0]

nums=[5,14,18,17,16,16,13,16,5]  7个元素

解析式和生成器表达式

列表解析式
	nums=[]
	for i in range(5):
		nums.append(i+1)
	print(nums)   	
	输出 [1, 2, 3, 4, 5]

	这段代码用来干什么的呢?
		创建了一个全新的列表,初始化内容	
	这种东西就可以转为另一种形式,列表解析式、推导式	

列表解析式、推导式 
	式的意思:表达式	

[i+1 for i in range(5)] 生成一个空列表
	从一个for循环中拿数据出来,这个数据拿出之后,对他加1,然后在加入了列表中
		这个就是我们说的解析式的语法
		立即生成一个新列表,填充内容

	鼓励使用,代码执行效率高		

列表初始化,尽量使用列表解析式表达

列表解析式是一种语法糖
	编译器会优化
	减少程序眼工作量
	简化代码,增强可读性
nums=[]
for i in range(5):
	nums.append((i+1)**2)
print(nums)

nums=[(i+1)**2 for i in range(5)]
print(nums)	

[1, 4, 9, 16, 25]
[1, 4, 9, 16, 25]

求10以内的奇数
	[i for i in range(10) if i%2==1]

	[i for i in range(1,10,2)]
	从range(1,10,2) 拿 i的值,一个个的加入列表中,加入的值放到第一个i中

实现20以内能被2整除或者能被3整除的数
	print([i for i in range(20) if i%2==0 or i%3==0])	

解析式当中不允许出现else和elif

[(x,y) for x in 'abcde' for y in range(3)]	
	#这个列表有几元素,元素类型是什么?
	[('a', 0), ('a', 1), ('a', 2), ('b', 0), ('b', 1), ('b', 2), 
  ('c', 0), ('c', 1), ('c', 2), ('d', 0), ('d', 1), ('d', 2), 
  ('e', 0), ('e', 1), ('e', 2)]

	生产了15个元组

	for x in 'abcde'
		for y in range(3)
			(x,y)=a,0
				  a,1
				  a,2    

	依次类推 3X5=15 一共循环了15次

[[x, y] for x in 'abcde' for y in range(3)]
	产生15个列表
	[['a', 0], ['a', 1], ['a', 2], ['b', 0], ['b', 1], ['b', 2], 
  ['c', 0], ['c', 1], ['c', 2], ['d', 0], ['d', 1], ['d', 2], 
  ['e', 0], ['e', 1], ['e', 2] ]

	这种简单的构建方式推荐使用

print([{x, y} for x in 'abcde' for y in range(3)])
	该列表解析式有几个元素,类型是什么?
[{0, 'a'}, {1, 'a'}, {2, 'a'}, {0, 'b'}, {1, 'b'}, {2, 'b'},
{'c', 0}, {'c', 1}, {'c', 2}, {0, 'd'}, {1, 'd'}, {2, 'd'}, 
{'e', 0}, {'e', 1}, {'e', 2}]

15个元素,每个都不相同,集合是无序的、不能重复的

print([{x, y} for x in 'aaaaa' for y in [0,0,0]])
	这个生成多少元素?
		解析式里的集合是不进行去重的,去重是在集合内部去重,集合于集合之间不去重
	[{0, 'a'}, {0, 'a'}, {0, 'a'}, {0, 'a'}, {0, 'a'}, {0, 'a'},
  {0, 'a'}, {0, 'a'}, {0, 'a'}, {0, 'a'}, {0, 'a'}, {0, 'a'}, 
  {0, 'a'}, {0, 'a'}, {0, 'a'}]



[(i,j) for i in range(7) if i>4 for j in range(20,25) if j>23]

	for i in range(7)
		if i > 4:        5 6 i的值大于4 才赋值给i
			赋值给元组(i,)
			for j in range(20,25)
				if j >23   24      j大于24 才赋值给j
					赋值给元组(,j)    

	输出这两个  [(5, 24), (6, 24)],看不懂进行转化

print([(i, j) for i in range(7) for j in range(20, 25) if j > 23]) 输出这个 [(0, 24), (1, 24), (2, 24), (3, 24), (4, 24), (5, 24), (6, 24)]

for i in range(7):
		for j in range(20,25):
			if i>4
			 	if j>23
			 		pass

	一斜到底才能修改为解析式		 		

[chr(random.randint(97, 123)) for i in range(10)]
	生成器表达式	

random.choices(string.ascii_lowercase,k=10)	
random.sample(string.ascii_lowercase,k=10)	


在某一个函数中,如果只需要一个参数,可迭代对象,如果要用生成器表达式
可以去除两边的小括号

print(["{:04}.{}".format( i, "".join( [chr(random.randint(97, 123)) for i in range(10)] 这个就是生成器表达式

)

) for i in range(1, 10)])

什么叫做生成器表达式? 把列表解析式中的中括号换成为小括号就行了

生成器表达式执行完,不会立即返回一个元组,返回的是一个生成器对象。
生成器对象是一个惰性的对象,比如range(),它返回的不是一个值,它返回一个
range对象,需要用for去驱动。
这个生成器对象也是需要这样的效果,需要驱动才能出元素。
惰性对象会做延迟计算,什么时候需要什么时候计算

生成器对象也是可迭代对象
生成器对象是迭代器,迭代器一定能迭代

可迭代对象最大 > 迭代器 > 生成器

a= (i+1 for i in range(5)) #生成器表达式 print(type(a)) #得到一个生成器对象,这个对象是惰性的 for x in a: #这里的a拿到的不是元组 print(x) print('-'*20)
for x in a: print(x)

#由生成器表达式得到这个生成器对象
#得到一个生成器对象,这个对象是惰性的
#这里的a拿到的不是元组,而是生成器类型

说白了现在,只拿到了地址,还不能知道具体内容

第二个for循环那个不会执行了,因为生成器对象,只能从头到尾遍历一次

next() 内建函数,作用就是拨动一下这个惰性对象。next函数只能用于迭代器中


b= range(4)
print(next(b)) 
	这里会出现语法错误,range出来时多情求值对象,不是迭代器,是一个可迭代对象

c=list(range(5))
print(next(c)) C是列表,可迭代对象,不是惰性求值,也不是迭代器,更不是生
成器	

如果迭代器已经遍历完,如果使用for就不遍历了
如果next,将抛出StopIteration 异常

生成器对象内部,有指针,指向当前位置,类似于链表

next 可以帮你判断这个是不是迭代器

一类是列表解析式	(顺序表)
一类是生成器解析式  (链表)


表达式不能过于复杂

解析式用于创建列表,或者生成器对象的。这个用处

预计算思想  
懒惰计算思想
	生成器用完就释放空间

str.join 
" ".join()   括号里是可迭代对象

集合解析式 换成大括号语法同 生成器表达式和列表解析式

解析式都是立即返回对象
生成器表达式,返回一个生成器对象

{(x, y) for x in 'abcde' for y in range(3)}

{('e', 1), ('c', 1), ('b', 2), ('a', 2), ('c', 0), ('d', 1), ('d', 0), ('e', 0), ('b', 1), ('e', 2), ('a', 1), ('d', 2), ('b', 0), ('c', 2), ('a', 0)}

首先在集合内部构建元组,元组内容是由 x y 构成

字典解析式

{x: y for x in 'abc' for y in range(3)}
	输出
		{'a': 2, 'b': 2, 'c': 2}

{str(0x61 + x): x ** 2 for x in range(1, 4)}		
	输出
		{'98': 1, '99': 4, '100': 9}
          b        c         d

{chr(0x61 + x): x ** 2 for x in range(1, 4)}
输出 {'b': 1, 'c': 4, 'd': 9}

注意这两个区别

str(0x61 + x)	
	这个的意思就是把 98 转换为字符串‘98’ 做了强制类型转换

内建函数和迭代器

datetime 模块名	
import datetime 
	datetime.datetime #类,抽象的概念,具体
	#具体的时间,时间对象

#不带时区的时间	
d1=datetime.datetime.now()	
	什么意思呢?
	从一个(datetime)时间模块下的(datetime)时间类下的,调用类的方法now
	从类的方法获取了一个具体的时间对象

	print(d1) #缺省print打印是世界对象的一种字符串表达	

d2 =datetime.datetime.utcnow()
	utc 近似认为0时区时间

d3 = datetime.datetime(2018,8,8,10,20,30)
	用类构造时间对象	

带时区的时间如何获得
		

for index,element in enumerate(l1): #(数据,列表的元素 )
	print(index,element)

	enumerate 他会返回两个数据,下标和值 。
		下标值由自己配,自己定index从几开始

迭代器:

iter([1,2,3]) 把非迭代器对象,转为迭代器
	生成一个全新的迭代器对象
	迭代器只能遍历一次,不能回头


zip()函数 

list(zip(range(5), range(5),range(5) ))
输出
	[(0, 0, 0), (1, 1, 1), (2, 2, 2), (3, 3, 3), (4, 4, 4)]

	每一个range()依次出一个数字构成 这5个元组

print(list(zip(range(5), range(5),range(2),range(5) )))
	输出
		[(0, 0, 0, 0), (1, 1, 1, 1)]
	典型的木桶原理,迭代几个,看谁短	

print( dict(zip('abcde',range(3)) ) )
	输出	
		{'a': 0, 'b': 1, 'c': 2}

{k:v for k,v in zip('abcde',range(3))}	
	字典解析式构造

举报

相关推荐

0 条评论