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)