1. python基础
1.1 字符串
1.1.1格式化:
% 用来格式化字符串,在字符串内部,
%s表示用字符串替换%d表示用整数替代%f表示浮点数%06d的,表示输出的整数显示位数,不足以0补充,超出当前位数则原样输出%.2f,表示小数点后显示的小数位数
format():它会用传入的参数依次替换字符串内的占位符{0},{1}
注:可迭代的对象
print ('%d %s cost me $%.2f.' %(2, 'books', 21.227))
"{} {}".format("hello", "world") # 不设置指定位置,按默认顺序
'hello world'
"{0} {1}".format("hello", "world") # 设置指定位置
'hello world'
"{1} {0} {1}".format("hello", "world") # 设置指定位置
'world hello world'
1.1.2 常用的操作
替换replace()、删除、截取、赋值、连接、比较、查找、分割split()等
str.replace(旧子串,新子串,替换次数)capitalize():将字符串第一个字符转换为大写,其他字符全部转化为小写title():将字符串每个单词的首字母转换为大写lower():将字符串中的大写转小写upper():将字符串中小写转大写ljust(): 返回一个原字符串左对齐,并使用指定字符串(默认为空格)填充至对应长度的新字符串str.startswith(子串,开始位置下标,结束位置下标)isalpha():如果字符串至少有一个字符并且所有字符都是字母则返回Trueisdigit():如果字符串只包含数字则返回Trueisalnum(): 如果字符串至少有一个字符并且所有字符都是字母或数字则返回True
# 1. 去除空格
str.strip()
# 2. 连接字符串 +
'love'+'lazy'
','.join(list)
# 3. 查找字符串 index 和find,区别find查找时变会返回-1,不影响程序执行
str.index('c',0,-1)
str.find('c')
# rfind 和find功能相同,但查找方向为右侧开始
# 4.比较字符串
str.cmp()
# 5. 是否包含指定字符串 in not in
a='hello word'
print('hello' in a)
# 6. 字符串长度 len(str) 或 str.len
# 7. 大小写 str.lower() str.upper()
# 8. 将字符串放入中间位置,可指定长度以及两边的字符str.center()
s='hello world'
print(s.center(40,'*'))
# 9.字符串统计 str.count()
s='hello world'
print(s.count('o'))
# 10. 字符串切片
str[::]
# 11 分割函数,partition()分割三部分
s='alex sb alex'
print(s.partition('sb'))
# 12. 返回指定长度的字符串,原字符串右对齐
str.zfill(11)
mystr="hello"
mystr.ljust(10,'.') # 'hello.....'
# 结束符
print("输出的内容",end="\n") # print默认为换行符
1.2 list 和tuple
1.2.1 list常用操作:
生成、添加元素append,访问(enumerate()同时输出索引值和元素内容)、删除、排序、切片、乘等
len(list),max(list)list(seq)将元组转换为列表tuple(seq):将列表转换为元祖list.count(obj)
元组在只包含一个元素时,tuple=(2,) 加’,'消除歧义
#创建list
list=[i for i in range(10)]
# 列表去重
my_list=[3,2,1,1,2,3]
unique_list=list(set(my_list))
from collections import OrderedDict
unique_list=list(OrderedDict.fromkeys(my_list)) #去重的同是保留list中的顺序
print(unique_list)
1.2.2 list 当作参数时的注意点
def f(x,li=[]):
print(id(li))
for i in range(x):
li.append(i*i)
print(li)
# 定义函数时,会保存函数中默认参数list的值,也就是列表li=[],当两次调用时都采用默认参数时,就会在原来的基础上append追加值。
f(4)
f(5)
# 优化
def f(x,li=[]):
if not li:
li=[]
for i in range(x):
li.append(x**2)
print(li)
# list 函数的copy,python变量名相当于标签名,list2=list1,直接赋值,实质上指向的是同一个内存值。任何一个变量(list1 or list2)发生改变,都会影响另外一个。
list1=[1,2,3,4,5,6]
list2=list1
list3=list1[:]
print(id(list1))
print(id(list2)) # list2 是 list1的视图,指向同一个内存值
print(id(list3)) # list切片产生新的lis
"""
Python中赋值语句不复制对象,而是在目标和对象之间创建绑定关系。对于自身可变或者包含可变项的集合对象,开发者有时会需要生成其副本用于改变操作,进而避免改变原对象。copy 模块提供了通用的浅层复制 copy() 和深层复制 deepcopy() 操作。
1. copy() 仅复制对象本身,而不对其中的子对象进行复制,如果对原子对象进行修改,那么浅层复制之后的对象也会随着修改。
2. deepcopy() 是真正意义上的复制,即重新开辟一片空间,经常说的复制实际上就是 deepcopy,深层复制之后的对象不受原对象的影响,无论原对象发生什么修改,深层复制的对象都不会发生改变。
"""
import copy
list1 = [1, 2, [3, 4], 5]
list2 = copy.copy(list1)
list3 = copy.deepcopy(list1)
list2 == list3
list2 is list3
# array 数组切片,赋值,实际指向同一个内存值,任意修改其中的一个变量,其他变量值都会被修改
import numpy as np
array1=np.array([1,2,3,4])
array2=array1
array3=array1[:]
# 生成新的array
array5 = array1.copy() # 对原始的array1的复制
1.2.3 运算符
- “ * ” 复制 print(‘_’*10)
- “+ ” 合并
#添加元素
list.append() # 尾部添加
list.insert(index,object) # 指定位置新增数据
#两个list的合并extend 、'+' ,列表结尾追加数据,如果数据是一个序列,则将这个序列的数据逐一添加到列表
list1.extend(list2)
list1+list2
#删除元素 remove和pop、 del
# 重复列表 *
l1=[1,2,3]
print(l1*3)
# 排序和反转
list.reverse() #列表元素反转
list.sort(cmp,key,reverse=False) #在原list上排序
list2=sorted(list)
# 元组
#元组中的元素不能被修改,但可以对元组进行连接组合,‘+’和'*'操作
tup1+tup2
1.3 dict 字典和 set集合
items返回dict的(健,值)tuple的一个列表keys()返回dict的健列表values()返回dict的值列表
创建集合使用{}或set(),但是如果要创建空集合只能使用set(),因为{}用来创建空字典
1.3.1 常见集合操作
add()增加数据;update()追加的数据是序列,例如,列表,集合,字符串(会依次添加每个字符)remove()删除集合中的指定数据,如果数据不存在则报错discard()删除集合中指定数据,如果数据不存在也不会报错pop()随机删除集合中的某个数据,并返回这个数据in/not in查找数据
#set 并、交、差、异或,判断子集
a | b
a & b
a - b
a ^ b
a.issubset(b)
1.3.2 dict相关操作
# 1. 添加元素
dict[key]=value
dict.setdefault(key,value)
# 2. 删除指定健值对
dict1={'a':1,'b':2}
del dict1['a']
# 清空字典
dict1.clear()
# 删除字典对象
del dict1
# 3. 字典的方法
#get(key,default=None)
dict1.get('d1','no')
# has_key(key) 如果key出现dict中返回True
dict1.has_key('a')
# 4. 更新字典 update()
dict1 = {'a': 1, 2: 'b', '3': [1, 2, 3]}
dict2 = {4: 'new1', 5: 'news'}
dict1.update(dict2)
print (dict1)
# 5. 将两个list合并成一个字典
dict1={list1[i]:list2[i] for i in range(len(list1))}
# 6. 提取字典中的目标数据
count1={key:value for key,value in counts.items() if value >=200}
# 7. 按照value进行排序
d = {'a':1,'b':4,'c':2}
字典是这个,然后要对字典按照value进行排序
方法一:
sorted(d.items(),key = lambda x:x[1],reverse = True)
方法二:
import operator
sorted(d.items(),key = operator.itemgetter(1))
Python遍历嵌套字典列表,取出某些字段的值
def traverse_take_field(data, fields, currentKey=None):
"""遍历嵌套字典列表,取出某些字段的值
:param data: 嵌套字典列表
:param fields: 列表,某些字段
:param values: 返回的值
:param currentKey: 当前的键值
:return: 列表
"""
if isinstance(data, list):
for i in data:
traverse_take_field(i, fields, currentKey)
elif isinstance(data, dict):
for key, value in data.items():
traverse_take_field(value, fields, key)
else:
values.append(data)
# if currentKey in fields:
# values.append(data)
data = {"info": "2班成绩单",
"grades": {
"小明":
[{"chinese": 60}, {"math": 80}, {"english": 100}],
"小红":
[{"chinese": 90}, {"math": 70}, {"english": 50}],
"小蓝":
[{"chinese": 80}, {"math": 80}, {"english": 80}],
},
"newGrades": {
"info": "新增数据",
"newChinese": 77
}}
if __name__ == '__main__':
values = []
fields = ["chinese", "newChinese"]
traverse_take_field(data, fields)
print(values)
1.4 zip函数
将可迭代的对象作为参数,将对象中的元素打包成一个个元组,然后返回由这些元组组成的列表,如果各个迭代器中的元素个数不一致,则返回列表长度与最短的对象相同,利用* 号操作符,可以将元祖解压为列表zip(*zipped) 可以是list数组,也可以是zip函数返回的对象
a=[1,2,3]
b=[4,5,6]
c=[4,5,6,7,8]
zipped=zip(a,b) #zip函数返回一个list对象,需要转换为list类型
iterator=zip(*zipped)
print(list(iterator))
1.5 集合的推导式
列表推导式、使用一句表达式构造一个新列表,可包括过滤、转换等操作。
#列表推导式方法
result2 = [i for i in range(10000) if i%2 == 0]
# 嵌套列表推导式:按照嵌套顺序理解
lists = [range(10), range(10, 20)]
evens = [item for lst in lists for item in lst if item % 2 == 0]
print(evens)
# 字典推导式
dict1 = {key : value for key, value in enumerate(reversed(range(10)))}
print (dict1)
# 集合推导式
set1 = {i for i in range(10)}
2. 函数
2.1 闭包
实现一个可变参数的求和,通常这样定义
def calc_sum(*args):
ax=0
for i in args:
ax+=i
return ax
如果不需要立即求和,而是在后面的代码中,根据需要在计算,可以不返回求和的结果,而是返回求和的函数
def lazy_sum(*args):
def sum():
ax=0
for i in args:
ax+=i
return ax
return sum
#当我们调用lazy_sum()时,返回的不是求和结果,而是求和的函数:
f = lazy_sum(1,2,3,5,7)
#调用函数f()时,才真正计算求和的结果
print(f())
"""
在这个函数lazy_sum()中又定义了sum,并且内部函数sum可以引用外部函数的参数和局部变量,当lazy_sum返回函数sum时,
相关变量和参数都保存在返回的函数中,这种称为‘闭包’closure
"""
另外需要注意的问题,返回的函数并没有立即执行,而是知道调用了f()才执行
注意:返回闭包时牢记一点:返回函数不要引用任何循环变量,或者后续会发生变化的变量
def count():
fs=[]
for i in range(1,4):
def f():
return i*i
fs.append(f)
return fs
f1,f2,f3=count()
"""
上面的例子中,每次循环,都创建了一个新的函数,然后,把创建的3个函数都返回,但是返回的函数都是9,原因在于
返回的函数引用了变量i,但它并没有立即执行,等到3个函数都返回时,他们所引用的变量已经变成了3,因此,最终结果为9
"""
f1()
#f2()
#f3()
#如果要引用循环变量,需要再创建一个函数,用该函数的参数绑定循环变量当前的值,无论该循环变量后续如何更改,已经绑定到函数参数的值不变
def count():
def f(j):
def g():
return j*j
return g
fs=[]
for i in range(1,4):
fs.append(f(i))
return fs
f1,f2,f3=count()
f1()
2.2 匿名函数
当我们在传入函数时,有些时候,不需要显示的定义函数,直接传入匿名函数更方便,返回值就是该函数的表达式
函数调用传参时,如果有位置参数时,位置参数必须在关键字参数(键=值)的前面,但关键字参数之间不存在先后顺序
# lambda 参数列表:表达式
str_lst = ['Welcome', 'to', 'Python', 'Data', 'Analysis', 'Course']
str_lst.sort(key=lambda x:len(x)) # sort by length
print (str_lst)
str_lst.sort(key=lambda x:x[-1]) # sort by the last letter
print (str_lst)
注意:
1. lambda 表达式的参数可有可无,函数的参数在lambda表达式中完全适用
2. lambda 表达式能接收任何数量的参数但只能返回一个表达式的值
fn=lambda:100
fn1=lambda a,b,c=100:a+b+c
fn2=lambda *args :args
def sum_num(a,b,f):
return f(a)+f(b)
result=sum_num(-1,2,abs)
2.3 函数参数
# 函数返回
def divide(a,b):
try:
return a*1.0/b
except ZeroDivisionError as e:
print(e)
#raise e
print(divide(0,1))
print(divide(1,0))
# 正常使用函数默认参数
#不推荐使用
def gen_list(a=[],b=None):
a.append(b)
return a
print (gen_list(b=2))
print(gen_list(b=3))
#导致函数默认值改变,多次调用相互影响,这里只是针对传递引用类型的参数,如果是数字、字符串等类型就不存在该问题
# 推荐使用
def gen_list(a=None,b=None):
if a is None:
a=[]
a.append(b)
return a
print(gen_list(b=2))
print(gen_list(b=3))
# 全局变量:在函数内部修改全局变量的值,需要在函数内部加一条语句: global 变量
a=100
def func():
global a
a=200
func()
print(a)
# 不定长参数:不定长参数也叫可变参数,用于不确定调用的时候会传递多少个参数(不传递也可)的场景,此时,可用包裹(packing)位置参数或者包裹关键字进行参数传递
# 包裹位置传递 *args
def user_info(*args):
print(args)
user_info('TOm',18)
#关键字传递 **kwargs
def user_info(**kwargs):
print(kwargs)
user_info(name="TOM",age="18")
# 引用:在python中,值是靠引用传递来的,我们可以用id()来判断两个变量是否为同一个值的引用,我们可以将id值理解为那块内存的地址标识
# int类型为不可变类型
a=1
b=a
print(id(a))
print(id(b))
a=2
#print(id(b))
# 列表是可变类型
aa=[10,20]
bb=aa
print(bb)
aa.append([30,40])
print(bb)
# 递归, 特点:函数内部自己调用自己;必须有出口
def sum_numbers(num):
# 1.出口
if num ==1:
return 1
# 2.当前数字+当前数字-1的累加和
return num+sum_numbers(num-1)
2.4多函数模
# 处理字符串
str_lst = ['$1.123', ' $1123.454', '$899.12312']
def remove_space(str):
str_no_space = str.replace(' ', '')
return str_no_space
def remove_dollar(str):
"""
remove $
"""
if '$' in str:
return str.replace('$', '')
else:
return str
def clean_str_lst(str_lst, operations):
"""
clean string list
"""
result = []
for item in str_lst:
for op in operations:
item = op(item)
result.append(item)
return result
clean_operations = [remove_space, remove_dollar]
result = clean_str_lst(str_lst, clean_operations)
print (result)
2.5 异常处理
"""
try:
可能发生错误的代码
except (异常类型):
如果出现异常执行的代码
"""
try:
print(1/0)
except:
print('error')
# 2. 捕获异常描述信息
try:
print(1/0)
except ZeroDivisionErroe as result: # except Exception as result:
print(result)
# 3. 异常的else
try:
print(1/0)
except Exception as result:
print(result)
else:
print('我是else,是没有异常的时候执行的代码')
# 4. 异常的finally
try:
f=open('text.txt','r')
except Exception as result:
f=open('text.txt','w')
else:
print('没有异常,我很开心')
finally:
f.close() # 表示无论是否有异常都要执行的代码
# 自定义异常,在python中,抛出自定义异常的语法为raise异常对
# 自定义异常类,继承Exception
class ShortInputError(Exception):
def __init__(self,length,min_len):
self.length=length
self.min_len=min_len
#设置抛出异常的描述性息
def __str__(self):
return "输入的长度是%是,不能少于%s个字符串"%(self.length,self.min_len)
def main():
try:
con=input('请输入密码:')
if len(con) <3:
raise ShortInputError(len(con),3)
except Exception as result:
print(result)
else:
print("密码输入完成")
2.6 高阶函数map、filter、reduce
map():将函数依次作用到序列的每一个元素,map()在python3返回的是迭代器,不是listreduce():把一个函数作用在一个序列上,reduce把结果继续和序列的下一个与元素做累积计算filter():也接收一个函数和一个序列,和map()不同,filter把传入的函数依次作用在每个元素元素上,然后根据返回值是True还是False决定是保留还是丢弃该元素
# map 函数
a = [1,2,3];b = [2,3,4]
c = list(map(lambda x,y:x+y,a,b))
# filter 函数
l=[x for x in range(0,10)]
l=list(filter(lambda x : x%2==0,l))
# reduce函数
from functools import reduce
str_lst = map(str, range(5)) # ['0', '1', ...]
def make_num(str1, str2):
return int(str1) * 10 + int(str2)
result = reduce(make_num, str_lst)
2.7 迭代器
- 定义:可以被next()函数调用并不断返回下一个值的对象称为迭代器:Iterator。
- 可迭代对象 Iterable,以直接作用于for 循环的数据类型有一下几种:
- 集合数据类型:
list,tuple,dict,set,str - generator ,包含生成器和带
yield的generator function
这些可以直接用于for循环的对象统称为可迭代对象:Iterable
2.8 生成器
生成器与列表生成式的本质区别就是:一个已经生成数据了,使用时,如果数据过大,会产生内存溢出,而生成器是只有循环时,才会生成数据
#1.把一个列表生成式的[]改成(),就创建了一个generator
g = (x * x for x in range(10))
# 使用了yield的函数就是生成器
def fib(times): #打印斐波拉契数列钱times位。
n=0
a,b=0,1
while n<times:
yield b #功能:每次执行到有yied的时候,会返回yield后面b的值给函数并且函数会暂停,直到下次调用或迭代终止。
a,b=b,a+b #再一次调用next方法时,程序从停止的地方开始执行
n+=1
return 'done'
for i in fib(10):
print(i)
2.9 装饰器
"""
这里的deco函数就是装饰器,它的参数是一个函数,然后返回值也是一个函数。其中作为参数的这个函数f()就在返回函数wrapper的内部执行。
然后在函数f()前面加上@deco,f()函数就相当于被注入了计时功能,现在只要调用f(),它就已经变身为“新的功能更多的函数”
"""
import time
def deco(f):
def wrapper(a,b):
start_time=time.time()
f(a,b)
end_time=time.time()
execution_time=(end_time-start_time)*1000
print("time is %d ms"%execution_time)
return wrapper
@deco
def f(a,b):
print('be on')
time.sleep(1)
print('result is %d'%(a+b))
if __name__=='__main__':
f(3,4)
# 2. 无固定参数的装饰器
def deco(f):
def wrapper(*args, **kwargs): #
start_time = time.time()
f(*args, **kwargs)
end_time = time.time()
execution_time= (end_time - start_time)*1000
print("time is %d ms" % execution_time)
return wrapper
@deco
def f(a,b):
print("be on")
time.sleep(1)
print("result is %d" %(a+b))
@deco
def f2(a,b,c):
print("be on")
time.sleep(1)
print("result is %d" %(a+b+c))
if __name__ == '__main__':
f2(3,4,5)
f(3,4)
# 3. 使用多个装饰器,装饰一个函数
def deco01(f):
def wrapper(*args, **kwargs):
print("this is deco01")
start_time = time.time()
f(*args, **kwargs)
end_time = time.time()
execution_time = (end_time - start_time)*1000
print("time is %d ms" % execution_time)
print("deco01 end here")
return wrapper
def deco02(f):
def wrapper(*args, **kwargs):
print("this is deco02")
f(*args, **kwargs)
print("deco02 end here")
return wrapper
@deco01
@deco02
def f(a,b):
print("be on")
time.sleep(1)
print("result is %d" %(a+b))
if __name__ == '__main__':
f(3,4)
3. 内建模块
3.1 datetime|time
日期和时间的库
# datetime模块下的datetime类
from datetime import datetime
3.2 hashlib
摘要算法又称哈希算法、散列算法,它通过一个函数,把任意长度的数据转换为一个长度固定的数据串(通常用16进制的字符串表示)
常用在:
任何允许用户登录的网站都会储存用户登录的用户名和口令,此时,便可以将口令用摘要算法替代
import hashlib
md5=hashlib.md5()
md5.update('how to use md5 in python hashlib?'.encode('utf-8'))
print(md5.hexdigest())
3.2 collections 模块
包含了一些特殊的容器,针对Python内置的容器,例如list、dict、set和tuple,提供了另一种选择;
-
namedtuple,可以创建包含名称的tuple; -
deque,类似于list的容器,可以快速的在队列头部和尾部添加、删除元素; -
Counter,dict的子类,计算可hash的对象; -
OrderedDict,dict的子类,可以记住元素的添加顺序; -
defaultdict,dict的子类,可以调用提供默认值的函数;
from collections import namedtuple,deque,Counter,defaultdict
# namedtuple是继承自tuple的子类。namedtuple创建一个和tuple类似的对象,而且对象拥有可访问的属性。
# 1. namedtuple 用来创建一个自定义的tuple对象,并且规定了tuple元素的个数,具备tuple的不变性,并可以用属性而不是索引来引用tuple的某个元素.
from collections import namedtuple
User = namedtuple('User', ['name', 'age', 'gender'])
u = User('villa', 33, 'male')
print(u.name)
print(u.age)
print(u.gender)
print(u)
Point =namedtuple('Point',['x','y']) # 第一个参数tuple_name, 第二个参数filter_name 返回值是类型,需要传参数
"""
这里其实是创建了一个类,但是class类有比较复杂,便可以通过nametuple来简化创建。第一个参数为类名,后面的list为类的属性
"""
p=Point(1,2) # 指定之后,属性值不能更改
print(p)
print(isinstance(p,tuple))
print(isinstance(p,Point))
# 2. deque: 解决list线性插入和删除效率低的问题,deque是为了高效的实现插入和删除操作的双向列表,适合用于队列和栈
q=deque([1,2,3,4])
q.append(5)
q.appendleft(0)
q.pop()
q.popleft()
# 3. Counter: 简单的计数器,例如统计字符的出现次数
c=Counter
print(c('hello')) # 可以是字符串,list
# 4. OrderedDict: 使用dict时,key是无序的,在对dict做迭代时,我们无法确定key的顺序,可以使用OrderdDict保持key的顺序
d=dict([('a',1),('aa',2),('c',3)]) #python中的字典是无序的,因为它是按照hash来存储的
d['d']=4
d['ff']=6
d['ef']=5
#先后顺序不保证
for key,value in d.items():
print(key,value)
od=OrderedDict([('a',1),('b',2),('c',3)]) #OrderedDict 的key会按照插入的顺序排序,而不是key本身排序
print(od)
# 5. defaultdict: 使用dict时,如果引用的key不存在,就会抛出keyError,如果希望key不存在时,返回一个默认值,就可以使用defaultdict
d=defaultdict(lambda:'N/A')
d['key1']='abc'
print(d['key1'])
print(d['key2'])
cnt={}
for char in ["a",'b','c','b','d','a','a']:
if char not in cnt:
cnt[char]=0
cnt[char]+=1
print(cnt)
# 方法二
cnt=defaultdict(int) # 指定类型int函数,int的默认值为0
for char in ["a",'b','c','b','d','a','a']:
cnt[char]+=1
print(cnt)
3.3 itertools
提供了非常有用的用于操作可迭代对象的函数
迭代器的特点是:惰性求值(Lazy evaluation),即只有当迭代至某个值时,它才会被计算,这个特点使得迭代器特别适合于遍历大文件或无限集合等,因为我们不用一次性将它们存储在内存中。
itertools 模块提供的迭代器函数有以下几种类型:
- 无限迭代器:生成一个无限序列,比如自然数序列 1, 2, 3, 4, …;
- 有限迭代器:接收一个或多个序列(sequence)作为参数,进行组合、分组和过滤等;
- 组合生成器:序列的排列、组合,求序列的笛卡儿积等;
import itertools
# 1. itertools 模块提供了三个函数(事实上,它们是类)用于生成一个无限序列迭代器:count(firstval=0, step=1)、cycle(iterable)、repeat(object [,times]
nums =itertools.count(1) #自然序列
# cycle 用于对iterable中的元素反复执行循环
cycle_string=itertools.cycle('ABC')
# repeat 用于反复生成一个object
for item in itertools.repeat([1,2,3,4],3):
print(item)
# 2. 合并器:chain() 与 izip(),chain()函数接收n个可迭代对象,然后返回一个他们的合集的迭代器,纵向合并;izip()函数接收n个可迭代对象,然后将其合并成tuples,横向合并,功能类似zip(),只是返回的是iterator,而不是list。
for i in itertools.chain([1,2,3],['a','b','c']): #chain() 可以把一组迭代对象串联起来,形成一个更大的迭代器
print (i)
for i, j in itertools.izip([1,2,3],['a','b','c']):
print (i)
# 3. 切分迭代器: islice(),函数接收一个迭代器,然后返回其切片,类似于list的slice切片操作。参数有start,stop和step,其中start和step参数时可选参数。
# 4. Map迭代器,imap()函数对迭代器进行转换,类似于python内置的map()函数。下例把range(5)乘以2。
print ("Multiples:")
for i in itertools.imap(lambda x,y:(x, y, x*y), range(5),range(5,10)):
print ('%d * %d = %d' % i)
# groupby() 把迭代器中的相邻重复元素挑出来放一起
for key,group in itertools.groupby('AAAABBBCCAAA'):
print(key,list(group))
3.4 OS 操作文件和目录
os.mkdir创建目录os.rmdir删除目录os.rename重命名os.walk遍历目录os.path.join连接目录和文件名os.path.split分割文件和文件名os.path.exists()判断路径是否存在os.getcwd()方法用于返回当前工作目录os.path.dirname():去掉脚本的文件名,返回目录os.path.dirname(os.path.realname(__ file__)):获得所引用模块所在的绝对路径,__ file __ 为内置属性os.remove()删除文件os.path.isdir()os.path.isfile()os.system():system函数可以将字符串转化成命令在服务器上运行os.listdir()获取目录列表
# 常用实例
import os
# 全局变量
cur_dir =os.getcwd()
new_dir=os.path.join(cur_dir,'tmp')
#判断new_dir是否存在,如何存在删除之后再创建,不存在,直接创建
if not os.path.exists(new_dir):
os.mkdir(new_dir) # 删除一个目录 os.rmdir(filepath)
else:
os.rmdir(new_dir)
os.mkdir(new_dir)
# 创建tmp.log 文件并写入内容
new_file=os.path.join(new_dir,'tmp.log')
with open(new_file,'w') as f:
f.write('test')
# 查找d:\dir_temp目录下的所有文件,以绝对路径输出文件
search_dir = 'd:\\dir_temp'
for root, dirs, files in os.walk(search_dir):
for name in files:
print (os.path.join(root, name))
# 操作文件和目录一部分在os模块中,一部分在os.path中
os.path.abspath('.') # 查看当前目录的绝对路径
path=os.path.join('path','newdir') # 在一个目录下创建一个新目录,首先把新目录的完整路径表示出来
os.path.split('/Users/desktop/testdir/file.txt') # 把一个路径拆分两部分,后一部分总是最后级别的目录或文件名
# /Users/desktop/testdir + file.txt
os.path.splitext() # 可以直接得到文件的扩展名
# /Users/desktop/testdir/file txt 返回的list
[x for x in os.listdir('.') if os.path.isdir(x)] # 列出当前目录下的所有目录
[ x for x in os.listdir('.') if os.path.isfile(x) and os.path.splitext(x)[1]=='.py'] # 列出所有的.py 文件
3.5 re 正则表达式
re.match()方法re.search()方法 re.search扫描整个字符串并返回第一个成功的匹re.sub()方法 用于替换字符串中的匹配re.compile()方法re.finditer作为迭代器返回
# re.match 函数 尝试从字符串的起始位置匹配一个模块,如果不是起始位置匹配成功的话,match() 就会返回None,re.match(pattern,string,flags=0),flags: 标志位,用于控制正则表达式的匹配方式,如:是否区分大小写,多行匹配等等
# 使用group(num)获取匹配的表达式
import re
line="Cats are smarter than dogs"
matchObj=re.match( r'(.*) are (.*?) .*',line ,re.M|re.I)
if matchObj:
print("matchObj.group():" ,matchObj.group())
print("matchObj.group(1):" ,matchObj.group(1))
print("matchObj.group(2):" ,matchObj.group(2))
# re.sub
phone="2004-959-559 # 这是一个国外电话号码"
#删除注释
num=re.sub(r"#.*","",phone)
print("电话号码是:",num)
#删除非数字的字符串
num=re.sub(r'\D',"",phone)
print("电话号码是:",num)
# compile 函数用于编译正则表达式,生成一个正则表达式pattern,供match和search这两个函数使用,re.compile(pattern,flags),pattern:一个字符串形式的正则表达式,flags : 可选,表示匹配模式,比如忽略大小写,多行模式等,具体参数为:
"""
1. re.I 忽略大小写
2. re.L 表示特殊字符集 \w, \W, \b, \B, \s, \S 依赖于当前环境
3. re.M 多行模式
4. re.S 即为 . 并且包括换行符在内的任意字符(. 不包括换行符)
5. re.S 即为 . 并且包括换行符在内的任意字符(. 不包括换行符)
"""
pattern =re.compile(r'\d+')
m=pattern.match('one12twothree34four',2,10) # 从'e'的位置开始匹配,没有匹配
# re.finditer 作为迭代器返回,在字符串中找到正则表达是所匹配的所有子串,并返回一个列表,如果没有找到匹配的,则返回空列表
result= pattern.findall("runoob 123 google 456",0,10)
print(result)
3.6 sys模块
sys.argv: 用来获取当前正在执行的命令行参数列表sys.argv[0]:是程序名,sys.argv[1]是第一个参数,以此类推sys.exit(n)sys.pathsys.stdin\stdout\stder
# 1. sys.argv,实现从程序外部向程序传递参数。
import sys
# 获取脚本名称
print('the name of this program is : %s ' % sys.argv[0])
# 获取参数列表
print('the command line argument are:')
for i in sys.argv:
print(i)
# 统计参数的个数
print(' there are %s arguments.'%(len(sys.argv)-1))
for index, arg in enumerate(sys.argv):
print("第%d个参数是: %s" % (index, arg))
# 2. sys.exit([arg]),程序中间的退出,arg=0为正常退出。
# 3. sys.path: 获取指定模块搜索路径的字符串集合,可以将写好的模块放在得到的某个路径下,就可以在程序中import时正确找到,当我们导入一个模块时:import xxx,
# 默认情况下python解析器会搜索当前目录、已安装的内置模块和第三方模块,搜索路径存放在sys模块的path中
#对于模块和自己写的脚本不在同一个目录下,在脚本开头加sys.path.append('xxx')
sys.path.append('引用模块的地址')
#不同目录下在a.py中导入b.py
sys.path.append('b模块的绝对路径')
import b
# 4. sys.stdin
#单行输入
line=sys.stdin.readline().strip() #获取输入的话,不要忘了去掉末尾的换行符,可以用strip( )函数
# 直接使用文件作为整体的输入
#for line in sys.stdin.readlines(): # 等效于 for line in sys.stdin:
# 使用方法,将文件重定向到输入中去就可以很方便的使 python test.py < 123.txt
# 5. sys.stdout
sys.stderr.write() #等效于 print() sys.stdout.write()和sys.stderr.write()均是向屏幕打印的语句
import time
import sys
for i in range(5):
print(i)
sys.stdout.flush()
time.sleep(1)
# 这个脚本的本意是每个一秒输出一个数字,但如果把这句话sys.stdout.flush() 去除的话。就只能等到程序执行完毕,屏幕上才会一次性输出 0,1,2,3,4,
# 6. flush() 方法是用来刷新缓冲区的,即将缓冲区中的数据立刻写入文件,同时清空缓冲区,不需要是被动的等待输出缓冲区写入。一般情况下,文件关闭后会自动刷新缓冲区,但有时你需要在关闭前刷新它,这时就可以使用 flush() 方法。
f.flush()
3.7 joblib
Joblib是一个可以将Python代码转换为并行计算模式的包,可以大大简化我们写并行计算代码的步骤。我们可以通过操作该包内的函数来实现目标代码的并行计算,从而提高代码运行效率
# 1. 首先,定义一个简单的函
from joblib import Parallel ,delayed
import time
def single(a):
time.sleep(1)
print(a)
return a
# 2. 使用for 循环运行10次,并记录时
start=time.time()
for i in range(10):
single(i)
Time=time.time()-start
print(str(Time)+'s')
# 3. 使用joblib库里面的Parallel函数及delayed函数来执行10次循环函数的操作,实现并行化处理。Parallel函数会创建一个进程池,以便在多进程中执行每一列表项。
start = time.time() # 记录开始的时间
res=Parallel(n_jobs=3,verbose=1)(delayed(single)(i) for i in range(10)) # 并行化处理, notebook 上函数中的print不打印
# n_jobs: int, default: None —— 设置并行执行任务的最大数量。
print(res)
Time = time.time() - start # 计算执行的时间
print(str(Time)+'s')
3.8 堆(heap)
是一个可以被看成近似完全二叉树的数组。树上的每一个结点对应数组的一个元素。除了最底层外,该树是完全充满的,而且是从左到右填充。又被称为优先队列(priority queue),但不是队列,在堆中,是按照元素的优先级取出元素的。
完全二叉树(complete binary tree)树的深度是log2
默认为顶堆,对每一个元素取负,可以构造大顶堆
left_son_id=father_id * 2
right_son_id=father_id * 2+1
father_id =son_id / 2
import heapq
# 1.heapq.heappush(heap,item) heap为定义堆,item 增加的元素;并且item可以为元组类型,Python按元素对元组进行排序,确保要排序元组的对象排在第一位
heap=[]
heapq.heappush(heap, 2)
# 2.heapq.heapify(list) 将列表转换为堆
list=[5,8,0,3,6,7,9,1,4,2]
heapq.heapify(list)
heapq.heappop(list)
list
# 3.heapq.heappop(heap) 删除最小的值
heap=[2, 4, 3, 5, 7, 8, 9, 6] # 默认为堆
heapq.heappop(heap)
# 4.heapq.heapreplace(heap, item) 删除最小元素值,添加新的元素值
heap=[2, 4, 3, 5, 7, 8, 9, 6]
heapq.heapreplace(heap, 11)
# 5.heapq.heappushpop(heap, item) 首判断添加元素值与堆的第一个元素值对比,如果大于则删除最小元素,然后添加新的元素值,否则不更改堆
heap=[2, 4, 3, 5, 7, 8, 9, 6]
heapq.heappushpop(heap, 9)
# 6.heapq.merge(...) 将多个堆合
# 7.heapq.nlargest (n, heap) 查询堆中的最大元素,n表示查询元素个数
heap=[2, 3, 5, 6, 4, 8, 7, 9]
heapq.nlargest (1, heap)
# 8.heapq.nsmallest(n, heap) #查询堆中的最小元素,n表示查询元素个数
heap=[2, 3, 5, 6, 4, 8, 7, 9]
heapq.nsmallest(2, heap)
3.9 argparse
argparse 是python内置的一个用于命令项生成选项与参数解析的模块,通过在程序中定义我们需要的参数,argparse将会从sys.argv中解析这些参数,并自动的生成帮助和使用信息
三个步骤:
-
创建
ArgumentParser()对象 -
调用
add_argument()方法添加参数 -
使用
parse_args()解析添加的参数
import argparse
parser=argparse.ArgumentParser()
parser.add_argument("dilename",default='text.txt')
parser.add_argument('integer',type=int,help='display an integer')
args=parser.parse_args()
print(args.integer)
# 基本框架
import argparse
def get_parser():
parser=argparse.ArgumentParser(description="Demo of argparse")
parser.add_argument('--name',default="word") #default 表示命令行没有设置该参数的时候,程序中用什么值来替代
return parser
if __name__=="__main__":
parser=get_parser()
args=parser.parse_args()
name=args.name
print("Hello {}" .format(name))
# 可选参数,"-"或"--" 开头定义的命令行参数,都属于可选参数,单个字母用- ,多个字母用 -- 位置参数 :必须配置的参数
parser = argparse.ArgumentParser()
parser.add_argument("--square", help="display a square of a given number", type=int)
parser.add_argument("--cubic", help="display a cubic of a given number", type=int)
parser.add_argument("--arch",required=True,choices=['alexnet','vgg']) # required:表示这个参数是否一定需要设置
# choices :参数值只能从集合选项里面选择
args = parser.parse_args()
if args.square:
print(args.square**2)
if args.cubic:
print(args.cubic**3)
# 混合参数, 定位参数和选项参数可以混合使用,看下面一个例子,给一个整数序列,输出它们的和或最大值(默认):
parser = argparse.ArgumentParser(description='Process some integers.')
parser.add_argument('integers', metavar='N', type=int, nargs='+',
help='an integer for the accumulator') # nargs是用来说明传入的参数个数,'+' 表示传入至少一个参数
parser.add_argument('--sum', dest='accumulate', action='store_const',
const=sum, default=max,
help='sum the integers (default: find the max)')
args = parser.parse_args()
print(args.accumulate(args.integers))
3.10 IO文本读写
# 1. 读文件
filepath='./lucy.txt'
with open(filepath,'r') as f: #open函数打开文件 r:读
print(f.read()) # ready一次性读取文件内容,把内容存放到内存中
f.readline() # 每次读取一行内容
f.readlines() #一次读取全部内容并按行返回list
# r: 表示文本文件
# rb:读取二进制文件,比如图像、视频
# 实际开发中通常使用
with open(filepath,'r') as f:
for line in f:
lst=line.strip().split()
# 2. seek() 作用:用来移动文件指针 , 文件对象.seek(偏移量,起始位置)
# 3. 写文件,同读文件,w:写入文本文件,wb:写入二进制文件
with open(filepath,'w' ) as f:
f.write('hello python')
3.11 pickle & json 字符串序列化
我们把变量从内存中变成可储存或传输的过程称为序列化
json:是文本序列化格式
pickle:是二进制序列化格式(特定于python)
# 序列化 pickle 模块
#首先,把一个对象序列化并写入文件
import pickle,json
d=dict(name='bob',age=20,score=90)
pickle.dumps(d)
'''
pickle.dumps() 方法把任意对象序列化成一个bytes,然后就可以把这个btypes 写入文件,或者用另一个方法pick.dump(d,f)直接序列化并写入
'''
# 使用pickle.load()直接反序列化出对象,完成从磁盘到内存
with open(filepath,'rb') as f:
d=pickle.dump(f)
print(d)
# JSON 如果我们要在不同的编程语言之间传递对象,就必须把对象序列化为标准化格式,json格式就是一个字符串,可以被所有的语言读取
# 把python对象变成一个json
d=dict(name='bob',age=20,score=90)
json.dumps(d) # dumps()返回一个标准的dtr,内容是标准的json,可以使用dump直接写入文件
fw.write(json.dumps(result)+"\n") # 将json格式的字符串写入文件
json.load() # json 反序列为 python 对象
line=json.loads(t_json) # 将对应的json字符串load下来,转化为正常格式
data={"status": "OK", "count": 2, "results": [{"age": 27, "name": "Oz", "lactose_intolerant": 'true'}, {"age": 29, "name": "Joe", "lactose_intolerant": 'false'}]}
print(json.dumps(data,indent=2))
# json 进阶,对python 中的类进行序列
class Student():
def __init__(self,name,age,score):
self.name=name
self.age=age
self.score=score
s=Student('Bob',20,90)
# json 无法直接对s实例对象进行json序列化,可选参数default可以把任何一个对象序列化为json对象,
#需要为Student专门写一个转换函数,再把函数传进去
def student2dict(std):
return {
'name':std.name,
'age':std.age,
'score':std.score
}
print(json.dumps(s,default=student2dict))
#也可以直接把任何class 的实例变为dict
json.dumps(s,default=lambda obj: obj.__dict__)
#同样json 先转化为dict,再到类实例
def dict2student(d):
return Student(d['name'],d['age'],d['score'])
json.loads(json_str,object_hook=dict2student)
3.12 operator
operator 中三个类
attrgetter:可以获取对象的属性,然后进行排序itemgettermethocaller
sorted(students,key=attrgetter('age'),reverse=True) #按照年龄进行排序
sorted(students,key=itemgetter(1),reverse=True) #按照年龄进行排序
3.13 networkx 创建图
3. 13.1 创建一个图
import networkx as nx
G=nx.Graph()
# 添加节点
G.add_node(1) # 一次添加一个节点
G.add_nodes_from([2,3]) # 添加一个节点列表
# 边
G.add_edge(1,2) # 可以通过一次添加一条边来增长
e=(2,3)
G.add_edge(*e) # update edge tuple*
G.add_edges_from([(1,2),(1,3)]) #也可以通过添加边列表
# 删除图中所有节点和边
G.clear()
添加新的节点和边,并且Networkx会自动的忽略任何已经存在的节点
G.add_edges_from([(1,2),(1,3)])
G.add_node(1)
G.add_edge(1,2)
print(G.number_of_nodes())
G.add_node("spam")
G.add_nodes_from("spam")
G.add_edge(3,"m")
print(G.number_of_nodes())
3.13.2 图结构的四个属性
G.nodes节点G.edges边G.adj or G.neighbors()邻接点G.degree图中节点的成程度集
# 查看是否有点1
G.has_node(1)
# 查看是否有边(1,2)
G.has_edge(1,2)
print(list(G.nodes))
print(list(G.edges))
print(list(G.adj[1]))
print(G.degree[1])
删除边和节点的函数:
Graph.remove_node()Graph.remove_nodes_from()Graph.remove_edge()Graph.remove_edges_from()
3.13.3 有向图: DiGraph()
dg = nx.DiGraph()
nodes1 = [
('Variable', {'name': 'avariable', 'table': 'tablename'}),
('Select', {'conditions': {'pro_code': 44}}),
('GroupBy', {'varname': 'gender'}),
('Mean', {}),
('Which1', {'level': 1}),
('Decimal1', {'place': 1})
]
nodes2 = [
('Which1', {'level': 2}),
('Decimal2', {'place': 1})
]
nodes3 = [
('Add', {})
]
dg.add_nodes_from(nodes1)
dg.add_nodes_from(nodes2)
dg.add_nodes_from(nodes3)
dg.add_edges_from([
('Variable', 'Select'),
('Select', 'GroupBy'),
('GroupBy', 'Mean'),
('Mean', 'Which1'),
('Mean', 'Which2'),
('Which1', 'Decimal1'),
('Which2', 'Decimal2'),
('Decimal1', 'Add'),
('Decimal2', 'Add'),
])
nx.draw(dg, with_labels=True)

3.13.4 属性
属性(如:权重,便签,颜色或者喜欢的python对象)可以附加到图形,节点或边上。每个图形,节点和边都可以在关联的属性字典中保存键/值属性对(键必须是可散列的)。默认情况下,这些都是空的,但是属性可以使用添加或更改add_edge,add_node或者命名的属性字典的直接操作
图形属性
G=nx.Graph(day="Friday")
print(G.graph)
# 修改属性
G.graph['day']="MOnday"
print(G.graph)
节点属性
添加节点属性使用add_node(),add_nodes_from() 或 G.nodes
G.add_node(1,time="5pm")
G.add_nodes_from([3],time="2pm")
print(G.nodes[1])
G.nodes[1]["room"]=714
print(G.nodes.data())
边属性
添加/更改边属性add_edge(),add_edges_from()或标符号
G=nx.DiGraph()
G.add_edge(1,2,weight=4.7)
G.add_edges_from([(3,4),(4,5)],color="red")
G.add_edges_from([(1,2,{'color':'blue'}),(2,3,{"weight":8})])
G.edges[3,4]["weight"]=4.2
nx.draw(G,with_labels=True)
3.13.5 从txt文件中读入有向图的函数
nx.read_edgelist(path,comments="#",delimiter=None,create_using=None,data=True,edgetype=None,encoding='utf-8')
# create_using为创建图的类型:有向图,无向图
当要读取的文件中的数据并不是按节点一的名称,节点二的名称,权重这样的三列顺序排列的时候,而是中间还有一些其他的列,比如节点属性等。但只希望读入指定列的数据时,先将指定列读入为DataFrame结构的数据,再将其通过nx.from_pandas_edgelist读入为图
df = pd.read_csv(network_path,delimiter='\t',usecols=[0,3])
dg = nx.from_pandas_edgelist(df,'node1','node2')
# 或者
df = pd.read_csv(network_path,delimiter='\t',names=['node1','node2'])
dg = nx.from_pandas_edgelist(df,'node1','node2')
textline = '1 2 3'
fh = open('test.edgelist','w')
d = fh.write(textline)
fh.close()
G = nx.read_edgelist('test.edgelist', nodetype=int, data=(('weight',float),))
G.nodes()
#[1, 2]
G.edges(data = True)
# [(1, 2, {'weight': 3.0})]
3.14 Queue 队列
python 的Queue模块中提供了FIFO(先入先出)队列Queue,LIFO(后入先出)队列LifoQueue,和优先级队列PriorityQueue。
Queue 模块中的常用方法:
Queue.qsize ()返回队列的大小Queue.empty ()如果队列为空,返回 True, 反之 FalseQueue.full ()如果队列满了,返回 True, 反之 FalseQueue.full与 maxsize 大小对应Queue.get ()调用队列对象的get()方法从队头删除并返回一个项目。Queue.get_nowait ()相当 Queue.get (False)Queue.put (item)调用队列对象的put()方法在队尾插入一个项目Queue.put_nowait (item)相当Queue.put (item, False)Queue.task_done ()在完成一项工作之后,Queue.task_done ()
函数向任务已经完成的队列发送一个信号Queue.join ()实际上意味着等到队列为空,再执行别的操作
4. 面向对象编程
由于类起到模版的作用,可以在创建实例的时候,把一些我们认为必须绑定的属性写进去,通过定义一个特殊的__init__方法,在创建实例的时候,就把属性绑定上去。
4.1 方法__new__() 和__init__()
self:表示实例本身,因此,在__init__方法内部,就可以把各种属性绑定到self
class Student():
def __init__(self,name,score):
self.name=name
self.score=score
def print_score(self):
print('%S:%s'%(self.name,self.score))
Python中存在于类中的构造方法__init__()负责将类实例化,而在__init__()执行之前,
__new__()负责制造这样的一个实例对象,以便__init__()去让该实例对象更加的丰富(为其添加属性等)。
4.2 方法__str__()
#当使用print输出对象的时候,默认打印对象的内存地址。如果类定义了__str__方法,那么就会打印这个方法中return的数据
class Wasker():
def __init__(self,width,height):
self.width=width
self.height=height
def __str__(self):
return "这是洗衣机的说明书"
4.3 访问权限
以__开头的实例变量,在python中变成了一个私有变量private
self.__name=name #实例仍然需要传参数,只是只能在内部使用
#但如果外部想要获取私有属性 ,可以给类增加get_name和get_score这样的方法
class Student(object):
def __init__(self,name,score):
self.__name=name
self.__score=score
def get_name(self):
return self.__name
def get_score(self):
return self.score
#如果又要允许外部代码修改socre,可以通过添加set_score方法:
def set_score(self,score):
self.__score=score
#目的:对参数做检查,防止传入无效的参数
class Student():
def __init__(self,name,score):
self.__name=name
self.__score=score
def set_score(self,score):
if 0<=score<=100:
self.__score=score
else:
raise ValueError('bad score')
4.4 继承和多态
#当子类和父类具有相同的方法是,子类的方法会覆盖父类的方法,这也就是获得了继承的另一个好处:多态
class People():
def __init__(self,name,age,weight):
self.name=name
self.age=age
self.__weight=weight
def speak(self):
print('{} 说:我今年{}岁了,体重是{:.2f}kg'.format(self.name,self.age,self.__weight))
class Student(People):
def __init__(self,name,age,weight,grade):
People.__init__(self,name,age,weight)
self.grade=grade
#重写父类方法
def speak(self):
print('{}说:我今年{}岁了,我在上海读{}年级。'.format(self.name,self.age,self.grade))
class Spearker():
def __init__(self,name,topic):
self.name=name
self.topic=topic
def speak(self):
print("我叫 %s,我是一名演说家,我今天演讲的主题是%s" %(self.name,self.topic))
#多重继承
class Sample(Spearker,Student):
def __init__(self,name,age,weight,grade,topic):
Student.__init__(self,name,age,weight,grade)
Spearker.__init__(self,name,topic)
test=Sample('tim',10,40.123,6,'python')
test.speak() #方法名同,默认调用的是在括号中排前地父类的方法
4.5 方法重写
class Parent: # 定义父类
def myMethod(self):
print('调用父类方法')
class Child(Parent): # 定义子类
def myMethod(self):
print('调用子类方法')
c = Child() # 子类实例
c.myMethod() # 子类调用重写方法
super(Child, c).myMethod() # 用子类对象调用父类已被覆盖的方法
4.6 静态方法
静态方法的特点,需要使用装饰器@staticmethod来进行修饰,静态方法既不需要传递类对象也不需要传递实例对象(形参没有self/cls)。
4.7 打印类的相关信息
class Point():
def __init__(self,x,y):
self.x=x
self.y=y
def __repr__(self):
return "Point({self.x},{self.y})".format(self=self)
p=Point(3,5)
print(p)
# 打印类的信息,也可以不用写repr方法,直接使用p.__dict__()
5. 多进程、多线程
5.1 多线程
"""
如何保持各线程之间的通信,在这里,我们使用队列Queue作为多线程之间通信的桥梁。
使用十个线程来执行run方法消化任务队列,run方法有两个参数,一个任务队列,一个保存结果的队列。
in_q.empty(),是对列的一个方法,它是检测队列是否为空,是一个布尔值,url = in_q.get(),这个操作是拿出队列的一个值出来,然后,把它从队列里删掉。
"""
# 一个关于queue.task_done()与queue.join()的实验
from threading import Thread
import time
import random
from queue import Queue
from collections import deque
# 创建队列,设置队列最大数限制为3个
queue = Queue(3)
# 生产者线程
class Pro_Thread(Thread):
def run(self):
# 原材料准备,等待被生产
tasks = deque([1, 2, 3, 4, 5, 6, 7, 8])
global queue
while True:
try:
# 从原材料左边开始生产
task = tasks.popleft()
queue.put(task)
print("生产", task, "现在队列数:", queue.qsize())
# 休眠随机时间
time.sleep(random.random())
# 如果原材料被生产完,生产线程跳出循环
except IndexError:
print("原材料已被生产完毕")
break
# 消费者线程
class Con_Thread(Thread):
def run(self):
global queue
while True:
if queue.not_empty:
# 通过get(),这里已经将队列减去了1
task = queue.get()
time.sleep(2)
# 这里可能队列数已经空了,但是消费者手里还有正在消费的队列
# 发出完成的信号,不发的话,join会永远阻塞,程序不会停止
queue.task_done()
print("消费", task)
else:
break
# r入口方法,主线程
def main():
Pro_1 = Pro_Thread()
# 把生产线程列为守护线程,否则主线程结束之后不会销毁该线程,程序不会停止,影响实验结果
Pro_1.setDaemon(True)
# 启动线程
Pro_1.start()
for i in range(2):
Con_i = Con_Thread()
# 把两个消费者线程列为守护线程,否则主线程结束之后不会销毁该线程,程序不会停止,影响实验结果
Con_i.setDaemon(True)
# 启动线程
Con_i.start()
global queue
# 这里休眠一秒钟,等到队列有值,否则队列创建时是空的,主线程直接就结束了,实验失败,造成误导
time.sleep(1)
# 接收信号,主线程在这里等待队列被处理完毕后再做下一步
queue.join()
# 给个标示,表示主线程已经结束
print("主线程结束")
if __name__ == '__main__':
main()
5.2 多进程
"""
如何保持各线程之间的通信,在这里,我们使用队列Queue作为多线程之间通信的桥梁。
使用十个线程来执行run方法消化任务队列,run方法有两个参数,一个任务队列,一个保存结果的队列。
in_q.empty(),是对列的一个方法,它是检测队列是否为空,是一个布尔值,url = in_q.get(),这个操作是拿出队列的一个值出来,然后,把它从队列里删掉。
"""
# 一个关于queue.task_done()与queue.join()的实验
from threading import Thread
import time
import random
from queue import Queue
from collections import deque
# 创建队列,设置队列最大数限制为3个
queue = Queue(3)
# 生产者线程
class Pro_Thread(Thread):
def run(self):
# 原材料准备,等待被生产
tasks = deque([1, 2, 3, 4, 5, 6, 7, 8])
global queue
while True:
try:
# 从原材料左边开始生产
task = tasks.popleft()
queue.put(task)
print("生产", task, "现在队列数:", queue.qsize())
# 休眠随机时间
time.sleep(random.random())
# 如果原材料被生产完,生产线程跳出循环
except IndexError:
print("原材料已被生产完毕")
break
# 消费者线程
class Con_Thread(Thread):
def run(self):
global queue
while True:
if queue.not_empty:
# 通过get(),这里已经将队列减去了1
task = queue.get()
time.sleep(2)
# 这里可能队列数已经空了,但是消费者手里还有正在消费的队列
# 发出完成的信号,不发的话,join会永远阻塞,程序不会停止
queue.task_done()
print("消费", task)
else:
break
# r入口方法,主线程
def main():
Pro_1 = Pro_Thread()
# 把生产线程列为守护线程,否则主线程结束之后不会销毁该线程,程序不会停止,影响实验结果
Pro_1.setDaemon(True)
# 启动线程
Pro_1.start()
for i in range(2):
Con_i = Con_Thread()
# 把两个消费者线程列为守护线程,否则主线程结束之后不会销毁该线程,程序不会停止,影响实验结果
Con_i.setDaemon(True)
# 启动线程
Con_i.start()
global queue
# 这里休眠一秒钟,等到队列有值,否则队列创建时是空的,主线程直接就结束了,实验失败,造成误导
time.sleep(1)
# 接收信号,主线程在这里等待队列被处理完毕后再做下一步
queue.join()
# 给个标示,表示主线程已经结束
print("主线程结束")
if __name__ == '__main__':
main()
1万+

被折叠的 条评论
为什么被折叠?



