1.*和**
	1.数学运算中,*为乘,**为次方
	2.当函数接收元组参数时,使用“*args”。当函数接收字典参数时,使用“**args”。这种方法在函数需要获取可变数量的参数时特别有用。
 	  如果使用的是*作前缀,多余的函数参数都会作为一个元组存储在args中。如果使用的是**作前缀,多余的参数则会被认为是一个字典的健/值对。
	3.对于def func(*args),*args表示把传进来的位置参数存储在tuple(元组)类型的args里面。例如,调用func(1, 2, 3),args就表示(1, 2, 3)这个元组。
	4.对于def func(**args),**args表示把参数作为字典的健-值对存储在dict(字典)类型的args里面。例如,调用func(a='I',b='am',c='wcdj'),
	  args就表示{'a':'I', 'b':'am', 'c':'wcdj'}这个字典。
	5.注意普通参数与*和**参数同时使用的情况,一般将*args和**args放在参数列表最后。
	6.*args 例子
			def powersum(power, *args):  
				total=0  
				for i in args:  
					total+=pow(i,power)  
				return total  

			powersum(2, 3, 4) #25  
			powersum(2, 10)   #100 
			 
	7.**args 例子
			def findad(username, **args):  
				print 'Hello: ', username  
				for name, address in args.items():  
					print 'Contact %s at %s' % (name, address)  
					
			findad('wcdj',gerry='gerry@byteofpython.info',wcdj='wcdj@126.com', yj='yj@gmail.com')
			#Hello: wcdj
			#Contact gerry at gerry@byteofpython.info
			#Contact wcdj at wcdj@126.com
			#Contact yj at yj@gmail.com

2.%
	1.%是一个运算符号,求余数。求模运算,相当于mod,也就是计算除法的余数,比如5%2就得到1。
	2.%是一个格式符。python中%常用的操作有%s,%d,%r等。%s,%r,%d分别表示字符串以str(),rper(),以及十进制整数表示,%f表示结果为浮点型。
		1.%f 浮点型
			import math    
			# %a.bf,a表示浮点数的打印长度,b表示浮点数小数点后面的精度 ,%f时表示原值,默认是小数点后5位数    
			print "PI=%f" % math.pi
			output: PI=3.141593    
			# 只是%9f时,表示打印长度9位数,小数点也占一位,不够左侧补空格
			print "PI=%9f" % math.pi
			output: PI=_3.141593  

		2.%d 整型
			[python] view plain copy
			num=14    
			#%d打印时结果是14    

		3.%s 字符串
			a = 'test'
			print 'it is a %s' %(a)
			打印的结果就是 it is a test

3.log()
	import math
	math.log(x[, base]) 
		log() 返回 x 的自然对数。log()是不能直接访问的,需要导入 math 模块,通过静态对象调用该方法。
		参数:
			x	数值表达式。
			base	可选,底数,默认为 e。
		返回值:返回 x 的自然对数,x > 0。
		例子:
			import math
			math.log(100.12) # 4.6063694665635735
			math.log(100.72) # 4.612344389736092
			math.log(math.pi) # 1.1447298858494002
			math.log(10,2) # 3.3219280948873626

4.slice()
	slice(stop) 相当于 slice(None, stop, None) 切片操作,默认从第一个元素开始截取到stop元素位置为止
	slice(start, stop[, step])	切片操作,默认从start元素位置开始截取到stop元素位置为止,步长是可选操作
		函数实现切片对象,主要用在切片操作函数里的参数传递
		参数:
			start	起始位置
			stop	结束位置
			step	间距
		返回值:返回一个切片对象 
		例子:
			myslice = slice(5)
			myslice # slice(None, 5, None)
			arr = range(10)
			arr #range(0, 10)
			arr[myslice] # range(0, 5)

5.OrderedDict类
	1.OrderedDict 也是 dict 的子类,其最大特征是,它可以“维护”添加 key-value 对的顺序。
	  简单来说,就是先添加的 key-value 对排在前面,后添加的 key-value 对排在后面。
	  由于 OrderedDict 能维护 key-value 对的添加顺序,因此即使两个 OrderedDict 中的 key-value 对完全相同,但只要它们的顺序不同,
	  程序在判断它们是否相等时也依然会返回 false。
	
	2.例子
		from collections import OrderedDict
		# 创建OrderedDict对象
		dx = OrderedDict(b=5, c=2, a=7)
		print(dx) # OrderedDict([('b', 5), ('c', 2), ('a', 7)])
		d = OrderedDict()
		# 向OrderedDict中添加key-value对
		d['Python'] = 89
		d['Swift'] = 92
		d['Kotlin'] = 97
		d['Go'] = 87
		# 遍历OrderedDict的key-value对
		for k,v in d.items():
			print(k, v)

		上面程序首先创建了 OrderedDict 对象,接下来程序向其中添加了 4 个 key-value 对,OrderedDict 完全可以“记住”它们的添加顺序。
		运行该程序,可以看到如下输出结果:
			OrderedDict([('b', 5), ('c', 2), ('a', 7)])
			Python 89
			Swift 92
			Kotlin 97
			Go 87

	3.例子
		正如前面所说的,两个 OrderedDict 中即使包含的 key-value 对完全相同,但只要它们的顺序不同,程序也依然会判断出两个 OrderedDict 是不相等的。
		例如如下程序:
			# 创建普通的dict对象
			my_data = {'Python': 20, 'Swift':32, 'Kotlin': 43, 'Go': 25}
			# 创建基于key排序的OrderedDict
			d1 = OrderedDict(sorted(my_data.items(), key=lambda t: t[0]))
			# 创建基于value排序的OrderedDict
			d2 = OrderedDict(sorted(my_data.items(), key=lambda t: t[1]))
			print(d1) # OrderedDict([('Go', 25), ('Kotlin', 43), ('Python', 20), ('Swift', 32)])
			print(d2) # OrderedDict([('Python', 20), ('Go', 25), ('Swift', 32), ('Kotlin', 43)])
			print(d1 == d2) # False

		上面程序先创建了一个普通的 dict 对象,该对象中包含 4 个 key-value 对;接下来程序分别使用 sorted() 函数对 my_data(dict 对象)的 items 进行排序:
		d1 是按 key 排序的;d2 是按 value 排序的,这样得到的 d1、d2 两个 OrderedDict 中的 key-value 对是一样的,只不过顺序不同。
		运行上面程序,可以看到如下输出结果:
			OrderedDict([('Go', 25), ('Kotlin', 43), ('Python', 20), ('Swift', 32)])
			OrderedDict([('Python', 20), ('Go', 25), ('Swift', 32), ('Kotlin', 43)])
			False
		从上面的输出结果可以看到,虽然两个 OrderedDict 所包含的 key-value 对完全相同,但由于它们的顺序不同,因此程序判断它们不相等。
	
	4.例子
		此外,由于 OrderedDict 是有序的,因此 Python 为之提供了如下两个方法:
		1.popitem(last=True):默认弹出并返回最右边(最后加入)的 key-value 对;如果将 last 参数设为 False,则弹出并返回最左边(最先加入)的 key-value 对。
		2.move_to_end(key, last=True):默认将指定的 key-value 对移动到最右边(最后加入);
		  			    如果将 last 改为 False,则将指定的 key-value 对移动到最左边(最先加入)。

		下面程序示范了 OrderedDict 的两个方法的用法:
			from collections import OrderedDict
			d = OrderedDict.fromkeys('abcde')
			# 将b对应的key-value对移动到最右边(最后加入)
			d.move_to_end('b')
			print(d.keys()) # odict_keys(['a', 'c', 'd', 'e', 'b'])
			# 将b对应的key-value对移动到最左边(最先加入)
			d.move_to_end('b', last=False)
			print(d.keys()) # odict_keys(['b', 'a', 'c', 'd', 'e'])
			# 弹出并返回最右边(最后加入)的key-value对
			print(d.popitem()[0]) # e
			# 弹出并返回最左边(最先加入)的key-value对
			print(d.popitem(last=False)[0]) # b

		运行上面程序,可以看到如下输出结果:
			odict_keys(['a', 'c', 'd', 'e', 'b'])
			odict_keys(['b', 'a', 'c', 'd', 'e'])
			e
			b
		通过上面的输出结果可以看出,使用 OrderedDict 的 move_to_end() 方法可以方便地将指定的 key-value 对移动到 OrderedDict 的任意一端;
		而 popitem() 方法则可用于弹出并返回 OrderedDict 任意一端的 key-value 对。

6.__getitem__()函数的调用:可通过“类的实例对象[任意参数]”调用__getitem__()函数
		class Data:
			def __init__(self,name):
				self.name=name
		 
			def __getitem__(self, parameter):
				return parameter+self.name

		d=Data('编程')
		print(d.name) # 编程
		print(d['python']) # python编程

7.collections 容器数据类型:Counter
	1.python官方文档:https://docs.python.org/zh-cn/3.9/library/collections.html#collections.Counter.most_common
	2.collections 这个模块实现了特定目标的容器,以提供Python标准内建容器 dict , list , set , 和 tuple 的替代选择。
		namedtuple()	创建命名元组子类的工厂函数
		deque		类似列表(list)的容器,实现了在两端快速添加(append)和弹出(pop)
		ChainMap		类似字典(dict)的容器类,将多个映射集合到一个视图里面
		Counter		字典的子类,提供了可哈希对象的计数功能
		OrderedDict	字典的子类,保存了他们被添加的顺序
		defaultdict	字典的子类,提供了一个工厂函数,为字典查询提供一个默认值
		UserDict		封装了字典对象,简化了字典子类化
		UserList		封装了列表对象,简化了列表子类化
		UserString 	封装了列表对象,简化了字符串子类化

	3.通过手动实现Counter的流程和调用collections.Counter()版本 来完成统计词频的例子
                #统计词频 手动实现版本
                colors = ['red', 'blue', 'red', 'green', 'blue', 'blue']
                result = {}
                for color in colors:
                    if result.get(color)==None:
                        result[color]=1
                    else:
                        result[color]+=1
                print (result) #{'red': 2, 'blue': 3, 'green': 1}
    
                #统计词频 collections.Counter()版本
                from collections import Counter
                colors = ['red', 'blue', 'red', 'green', 'blue', 'blue']
                c = Counter(colors)
                print (dict(c)) #{'red': 2, 'blue': 3, 'green': 1}

	4.Counter的使用
                #创建一个空的Counter。
                cnt = Counter()
                #也可以创建的时候传进去一个迭代器(数组,字符串,字典等)。
                c = Counter('gallahad')  # 传进字符串
                print (c) #Counter({'a': 3, 'l': 2, 'g': 1, 'h': 1, 'd': 1})
                c = Counter({'red': 4, 'blue': 2}) # 传进字典
                print (c) #Counter({'red': 4, 'blue': 2})
                c = Counter(cats=4, dogs=8)   # 传进元组
                print (c)#Counter({'dogs': 8, 'cats': 4})

                #判断是否包含某元素,可以转化为dict然后通过dict判断,Counter也带有函数可以判断
                c = Counter(['eggs', 'ham'])
                print (c) #Counter({'eggs': 1, 'ham': 1})
                print (c['bacon'])# 不存在就返回0

                #删除元素
                del c['ham']
                print (c) #Counter({'eggs': 1})

                #获得所有元素
                c = Counter(a=4, b=2, c=0, d=-2)
                print (list(c.elements())) #['a', 'a', 'a', 'a', 'b', 'b']

                #most_common(k) 查看最常出现的k个元素
                c = Counter('abracadabra')
                print (c) #Counter({'a': 5, 'b': 2, 'r': 2, 'c': 1, 'd': 1})
                c = Counter('abracadabra').most_common(3) #查看最常出现的3个元素
                print (c) #[('a', 5), ('b', 2), ('r', 2)]

                #Counter更新
                c = Counter(a=3, b=1)
                d = Counter(a=1, b=2)
                print (c + d)    # 相加
                #Counter({'a': 4, 'b': 3})
                print (c - d)     # 相减,如果小于等于0,删去
                #Counter({'a': 2})
                print (c & d)       # 求最小
                #Counter({'a': 1, 'b': 1})
                print (c | d)      # 求最大
                #Counter({'a': 3, 'b': 2})

	5.例子
                #例子:读文件统计词频并按照出现次数排序,文件是以空格隔开的单词的诸多句子
                from collections import Counter
                lines = open("./data/input.txt","r").read().splitlines()
                lines = [lines[i].split(" ") for i in range(len(lines))]
                words = []
                for line in lines:
                    words.extend(line)
                result = Counter(words)
                print (result.most_common(10))

                #当需要统计的文件比较大,使用read()一次读不完的情况
                from collections import Counter
                result = Counter()
                with open("./data/input.txt","r") as f:
                    while True:
                        lines = f.read(1024).splitlines()
                        if lines==[]:
                            break
                        lines = [lines[i].split(" ") for i in range(len(lines))]
                        words = []
                        for line in lines:
                            words.extend(line)
                        tmp = Counter(words)
                        result+=tmp
                print (result.most_common(10))

随机获取 random.choice、random.choices、random.sample

1.官方文档解释:https://docs.python.org/zh-cn/3.9/library/random.html?highlight=choices#random.choices

2.random.choice(seq)
	从非空序列 seq 返回一个随机元素。如果 seq 为空,则引发 IndexError。
	从非空序列中随机选取一个数据并带回,该序列可以是list、tuple、str、set。

3.random.choices(population, weights=None, *, cum_weights=None, k=1)
	population:序列。 weights:相对权重。 cum_weights:累加权重。 k:选取次数。
	1.从population中随机选取k次数据,返回一个列表,注意会出现同一个元素多次被选中的情况。
	  可以设置相对权重、累加权重,population中有几个元素就要有相同数量的权重值,但不能同时设置相对权重和累积权重,只能设置其中一个权重类型。
	  注意每次选取都不会影响原序列,每一次选取都是基于原序列。
	2.例子
		1.random.choices([1,2,3,4,5],k=5)
 			序列中各个成员出现的概率基本持平。如果未指定相对权重或累积权重,每个成员的选择的概率相等。
		2.random.choices([1,2,3,4,5],[0,0,1,0,0],k=5)
			每次输出均得到[3,3,3,3,3]结果。不写weights或cum_weights参数名的话,默认为weights。权限值为0的元素不会被选中。
		3.random.choices([1,2,3,4,5],weights=[0,0,1,0,0],k=5)
 			每次输出均得到[3,3,3,3,3]结果。权限值为0的元素不会被选中。
		4.random.choices([1,2,3,4,5],weights=[1, 3, 9, 15, 30],k=5)
			设置weights=[1, 3, 9, 15, 30]在底层会自动计算为cum_weights=[1, 4, 13, 28, 58]
			即设置weights=[1, 3, 9, 15, 30]等同于设置cum_weights=[1, 4, 13, 28, 58]
	3.结论
		1.参数weights设置相对权重,权重值是一个列表,设置之后,每一个成员被抽取到的概率就被确定了,权重值列表的数量和序列中元素值的数量相同。
		2.cum_weights设置累加权重,没有设置cum_weights累加权重,只有设置weights相对权重的话,
		  Python会自动把相对权重自动计算为累加权重,即如果你直接给出累加权重,那么就不需要给出相对权重,且Python省略了一步计算。
		  比如设置weights=[1,2,3,4],那么自动计算转换为cum_weights=[1,3,6,10]。

 	4.官方解释
		1.从population中选择替换,返回大小为 k 的元素列表。 如果 population 为空,则引发 IndexError。
		2.如果指定了 weight 序列,则根据相对权重进行选择。或者,如果给出 cum_weights 序列,则根据累积权重(使用 itertools.accumulate() 计算)进行选择。 
	  	  例如,相对权重``[10, 5, 30, 5]``相当于累积权重``[10, 15, 45, 50]``。 
	  	  在内部,相对权重在进行选择之前会转换为累积权重,因此提供累积权重可以节省工作量。
		3.如果既未指定 weight 也未指定 cum_weights ,则以相等的概率进行选择。如果提供了权重序列,则它必须与 population 序列的长度相同。 
	  	  一个 TypeError 指定了 weights 和*cum_weights*。
		4.weights 或 cum_weights 可以使用任何与 random() 所返回的 float 值互操作的数值类型(包括整数、浮点数和分数但不包括十进制小数)。 权重假定为非负数。
		5.对于给定的种子,具有相等加权的 choices() 函数通常产生与重复调用 choice() 不同的序列。 choices() 使用的算法使用浮点运算来实现内部一致性和速度。 
	  	  choice() 使用的算法默认为重复选择的整数运算,以避免因舍入误差引起的小偏差。

	
	5.例子
			import random
			a = [1,2,3,4,5]
			#设置相对权重weights=[1, 3, 9, 15, 30]在底层会自动计算为累加权重cum_weights=[1, 4, 13, 28, 58]
			c = random.choices(a, weights=[1, 3, 9, 15, 30], k=100)
			#容器.count(要统计的元素值):统计容器中的该元素值出现的次数是多少
			print(c.count(5))#41
			print(c.count(4))#27
			print(c.count(3))#21
			print(c.count(2))#8
			print(c.count(1))#3
			 
			#直接设置累加权重cum_weights=[1, 4, 13, 28, 58],则免去相对权重计算转换为累加权重的环节
			#直接设置累加权重cum_weights=[1, 4, 13, 28, 58]等同设置相对权重weights=[1, 3, 9, 15, 30]
			c = random.choices(a, cum_weights=[1, 4, 13, 28, 58], k=100)
			#容器.count(要统计的元素值):统计容器中的该元素值出现的次数是多少
			print(c.count(5))#51
			print(c.count(4))#22
			print(c.count(3))#17
			print(c.count(2))#7
			print(c.count(1))#3


	6.choices源码解析
                def choices(self, population, weights=None, *, cum_weights=None, k=1):
                    random = self.random
                    if cum_weights is None:
                        if weights is None:
                            _int = int
                            total = len(population)
                            return [population[_int(random() * total)] for i in range(k)]
                        cum_weights = list(_itertools.accumulate(weights))
                    elif weights is not None:
                        raise TypeError('Cannot specify both weights and cumulative weights')
                    if len(cum_weights) != len(population):
                        raise ValueError('The number of weights does not match the population')
                    bisect = _bisect.bisect
                    total = cum_weights[-1]
                    return [population[bisect(cum_weights, random() * total)] for i in range(k)]

		#在List列表a中插入项X,并假设List列表a已排序,则保持其排序。
		#如果x已经在List列表a中,则将其插入到最左边x的左边。
		#可选参数lo(默认0)和 可选参数hi(默认len(List列表a))绑定了将要搜索的List列表a
		def bisect_right(a, x, lo=0, hi=None):
			if lo < 0:
				raise ValueError('lo must be non-negative')
			if hi is None:
				hi = len(a)
			while lo < hi:
				mid = (lo+hi)//2
				if x < a[mid]: hi = mid
				else: lo = mid+1
			return lo
			
		import _bisect
		import itertools

		population = [1,2,3,4,5] #定义列表
		weights=[1, 3, 9, 15, 30]#定义相对权重
		cum_weights = list(itertools.accumulate(weights))#把相对权重计算转换为累计权重
		print("cum_weights=",cum_weights)#cum_weights=[1, 4, 13, 28, 58]
		print(random.random()) #0.23923274361912816

		#源码调用bisect,实际调用bisect_right
		bisect = _bisect.bisect
		#源码的choices函数中还会获取累计权重中的最后一个权重值
		total = cum_weights[-1]

		result = []
		for i in range(100):
			t = random.random() * total
			#源码调用bisect,实际调用bisect_right
			# index = bisect(cum_weights,t)
			index = bisect_right(cum_weights,t)
			result.append(population[index])

		# print(result)
		print(result.count(5))#64
		print(result.count(4))#17
		print(result.count(3))#9
		print(result.count(2))#7
		print(result.count(1))#3


4.random.sample
	a = ['ahh','hhh','zzz','emm']
	print(random.sample(a,4)) #['ahh', 'emm', 'hhh', 'zzz']
	从population中选取k个元素,返回一个列表,集群可以是list、tuple、str、set。
	与random.choices()的区别:choices是选取k次,sample是选取k个,choices选取k次的相当于选取后population中元素不变,sample选取k个则选取后population中去掉该元素。
	故random.sample()的k值不能超出population中的元素个数。

hash()

1.hash() 用于获取取一个对象(字符串或者数值等)的哈希值。
  hash() 函数可以应用于数字、字符串和对象,不能直接应用于 list、set、dictionary。
  在 hash() 对对象使用时,所得的结果不仅和对象的内容有关,还和对象的id()有关,也就是和内存地址有关。
2.hash() 函数的用途
	hash() 函数的对象字符不管有多长,返回的 hash 值都是固定长度的,也用于校验程序在传输过程中是否被第三方(木马)修改,
	如果程序(字符)在传输过程中被修改hash值即发生变化,如果没有被修改,则 hash 值和原始的 hash 值吻合,
	只要验证 hash 值是否匹配即可验证程序是否带木马(病毒)。
3.例子
	>>>hash('test')            # 字符串
	2314058222102390712
	>>> hash(1)                 # 数字
	1
	>>> hash(str([1,2,3]))      # 集合
	1335416675971793195
	>>> hash(str(sorted({'1':1}))) # 字典
	7666464346782421378

	class Test:
		def __init__(self, i):
			self.i = i
	for i in range(10):
		t = Test(1)
		print(hash(t), id(t))

	(277855628, 4445690048)
	(277855637, 4445690192)
	(277855628, 4445690048)
	(277855637, 4445690192)
	(277855628, 4445690048)
	(277855637, 4445690192)
	(277855628, 4445690048)
	(277855637, 4445690192)
	(277855628, 4445690048)
	(277855637, 4445690192)