0%

1.python在除法和C语言中的一点区别

​ 在Python3中,除法有 “/” 以及 “//” 两种,这两个有着明显的区别,具体区别看代码:

1
2
print(12//10)
print(12/10)

这两行代码的输出如下:

1
2
1
1.2

当被除数是负数的时候,又是另一种情况:

1
2
3
4
5
6
7
8
print(-12/10)      #不补整
print(int(-12/10)) #向正方向进行补整
print(-13//10) #向负方向进行补整

output:
-1.2
-1
-2

​ 因此,综合前面的正负两种情况,我们可以看出当我们想要达到和C++同样的向上取整,只能使用int(a/b)方式。

2.python在求余时和C的一点区别

​ 对于正数求余运算,python和C++完全相同,但是对于负数求余运算,python和C++存在着较大的差别,下面我们通过例子来说明二者的差别。

1
2
3
4
5
6
7
8
9
#C++
count>>-123%10;
output:
-3
#python
print(-123%10)

output:
-7 #这里是向下取10的余数

​ 为了实现和C++相同效果的取余运算,我们只能采用如下方式进行取余运算

1
2
3
4
if a>=0
print(a%10)
else:
print(a%-10)

​ 机试中,排序算法是主要面临的一类算法,很久都没有接触机试的题了,解决的时候感觉有点思路不是很清楚了,因此写了这一片博客,来整理下常见的排序算法以及各种常见算法的效率稳定性等特点。

在机试中常用的排序算法主要包含下面几种:

​ 1.插入排序

​ 2.选择排序

​ 3.快速排序(最常用的排序)

​ 4.冒泡排序

​ 5.归并排序

​ 6.桶排序

下面我将具体介绍各种排序算法的一些特点:

排序算法 平均时间复杂度 最坏时间复杂度 空间复杂度 是否稳定
冒泡排序 O(n2) O(n2) O(1) 稳定
选择排序 O(n2) O(n2) O(1) 不稳定
直接插入排序 O(n2) O(n2) O(1) 稳定
希尔排序 O(n2) O(O3/2)
归并排序 O(nlogn) O(nlogn) O(n) 稳定
快速排序 O(nlogn) O(n2) O(logn) 不稳定
堆排序 O(nlogn) O(nlogn) O(1) 不稳定

时间复杂度辅助记忆:

  • 冒泡、选择、直接 排序需要两个for循环,每次只关注一个元素,平均时间复杂度为O(n2)(一遍找元素O(n),一遍找位置O(n))
  • 快速、归并、希尔、堆基于二分思想,log以2为底,平均时间复杂度为O(nlogn)(一遍找元素O(n),一遍找位置O(logn))

1.插入排序

​ 每次从头到尾选择一个元素,并且将这个元素和整个数组中的所有已经排序的元素进行比较,然后插入到合适的位置。

​ 注意:插入排序的核心点就是两两比较时从后向前进行比较,如果比插入值大,那么将其向后移动,直到找到比插入值小的。

1
2
3
4
5
6
7
8
9
10
def insertion_sort(arr):
length = len(arr)
for i in range(1,length): #从第一个元素开始依次进行排序
tmp = arr[i]
j = i
while arr[j-1]>tmp and j>0: #从当前元素从后向前向前开始遍历,寻找第一个比当前元素更小的元素
arr[j] = arr[j-1] #再找比当前小的元素位置的同时,只要扫描到的位置比当前元素大,那么将该元素后移一维
j -= 1
arr[j] = tmp
return arr

稳定性:稳定

时间复杂度:O(n^2)

空间复杂度:O(1)

为什么插入排序是稳定的排序算法?

​ 当前从头到尾选择元素进行排序时,当选择到第i个元素时,前i-1个元素已经排好了续,取出第i个元素,从i-1开始向前开始比较,如果小于,则将该位置元素向后移动,继续先前的比较,如果不小于,那么将第i个元素放在当前比较的元素之后。

2.选择排序

​ 选择排序主要采用了从头到尾依次确定各个位置的方式来进行排序,首先遍历一次整个数组,如果遇到比第一个元素小的元素那么交换位置,一次遍历完成那么第一个位置就已经是整个数组中最小的元素了,经过n次遍历,确定全部位置的元素。

1
2
3
4
5
6
7
8
9
def selection_sort(arr):
length = len(arr)
for i in range(length):
for j in range(i,length):
if arr[i]>arr[j]:
tmp = arr[i]
arr[i] = arr[j]
arr[j] = tmp
return arr

稳定性:不稳定

时间复杂度:O(n^2)

空间复杂度:O(1)

3.冒泡排序

​ 冒泡排序额是实现是不停地进行两两比较,将较大的元素换到右侧,然后继续进行两两比较,直到比较完全全部元素,每进行完一轮两两比较,确定一个元素的位置。例如:第一轮两两比较确定最大的值,第二轮比较确定次大元素。

1
2
3
4
5
6
7
8
9
10
11
def bubble_sort(arr):
length = len(arr)

for i in range(0,length):
for j in range(1,length-i):
if arr[j]<arr[j-1]:
tmp = arr[j]
arr[j] = arr[j-1]
arr[j-1] = tmp

return arr

稳定性:稳定

时间复杂度:O(n^2)

空间复杂度:O(1)

冒泡排序在原始冒泡排序算法的基础上还能做哪些优化?

​ 1.设置是否已经排好序的flag。如果在某一轮的便利中没有出现任何的交换发生,这说明已经都排好序,那么直接将flag置True,每轮结束时检测flag,如果为True则直接返回

​ 2.某一轮的结束为止为j,但这一轮最后一次交换发生在lastSwap位置,那么说明lastSwap到j之间已经排好序,下次遍历的结束点就不需要再到j—而是直接到lastSwap即可。

4.希尔排序

​ 希尔排序是一种插入排序的改良算法,简单的插入排序不管元素怎么样,都从头到尾一步一步的进行元素比较,如果遇到逆序序列如:[5,4,3,2,1,0]数组末端的0要回到原始位置需要n-1次的比较和移动。而希尔排序使用跳跃式分组的策略,通过某个增量将数组元素划分为若干组,然后在各个组内进行插入排序,随后逐步缩小增量,继续按照组进行排序,直至增量为1。

​ 希尔排序通过这种策略使的整个数组在初始阶段宏观上基本有序,小的基本在前,大的基本在后,然后缩小增量相当于进行微调,不会过多的设计元素移动。

基本思想:把记录按照下标的一定增量进行分组,对每组使用直接插入排序算法进行排序;随着增量逐渐减少,魅族包含的元素个数越来越多,当增量减至1时,整个文件被分成一组,算法终止。

稳定性:不稳定

平均时间复杂度:O()

最坏时间复杂度 : O()

空间复杂度:O( )

5.快速排序

​ 快速排序的的主要思想是先找到一个任意一个元素作为基准元素pivot(一般都采用第一个元素作为基准),然后从右向左搜索,如果发现比pivot小,那么和pivot交换,然后从右向左进行搜索,如果发现比pviot大,那么进行交换,遍历一轮后pivot左边的元素都比它小,右边的元素都比他大,此时pivot的位置就是排好序后他也应该在的位置。然后继续用递归算法分别处理pivot左边的元素和右边的元素。

对于大的乱序数据快速排序被认为是最快速的排序方式
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
#方式一:递归
def quick_sort(arr,l,r):
if(l<r):
q = mpartition(arr,l,r)
quick_sort(arr,l,q-1) #前面经过一次mpartion后q位置已经排好序,因此递归时两部分跳过q位置
quick_sort(arr,q+1,r)

return arr


def mpartition(arr,l,r):
"""
递归子函数,povit放到指定位置
return l:最终标志元素被放置的位置,本轮确定了的元素位置
"""
poviot = arr[l]

while l<r:
while l<r and arr[r]>=poviot:
r -= 1
if l<r:
arr[l] = arr[r]
l += 1

while l<r and arr[l]<poviot:
l += 1
if l<r:
arr[r] = arr[l]
r -= 1

arr[l] = poviot

return l
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
#方式二:非递归,利用栈

def partition(nums,low,high):
#确定nums数组中指定部分low元素的位置,左边都比它小,右边都比他大

pivot = nums[low]
high_flag = True #这里之所以设置这两个flag是为了确保交叉进行,否则可能会出现最大索引值处没有值或者最大索引值处一直付给各个low
low_flag = False

while low<high and low<len(nums) and high<len(nums):
if high_flag:
if nums[high]<pivot:
nums[low]=nums[high]
high_flag = False
low_flag = True
else:
high -= 1
if low_flag:
if nums[low]>pivot:
nums[high] = nums[low]
low_flag = False
high_flag = True
else:
low += 1
nums[low] = pivot

return low



def quick_sort(nums):
low = 0
high = len(nums)-1
stack = [] #存储每次遍历起始索引和结束索引

if low<high:
#先手动将找到第一个节点的最终位置,将原数组分为左右两个数组,分别左右索引入栈
mid = partition(nums,low,high)
if low<mid-1:
stack.append(low)
stack.append(mid-1)
if high>mid+1:
stack.append(mid+1)
stack.append(high)

#取出之前入栈的一个数组,来进行确定最终位置,分为左右两个子数组,分别左右索引入栈的操作,重复直到所有元素都已经排好序
while stack:
#这里写的是属于右半部都排好后左半部
r = stack.pop()
l = stack.pop()
mid = partition(nums,l,r)
if l<mid-1:
stack.append(l)
stack.append(mid-1)
if r>mid+1:
stack.append(mid+1)
stack.append(r)

return nums

稳定性:不稳定(排序过程中不停地交换元素位置造成了排序算法不稳定)

时间复杂度:

平均时间O(nlogn)

最坏情况:O(n^2)

空间复杂度:O(nlogn)

6.归并排序

​ 该算法采用经典的分治(divide-and-conquer)策略(分治法将问题(divide)成一些小的问题然后递归求解,而治(conquer)的阶段则将分的阶段得到的各答案”修补”在一起,即分而治之)。

​ 每次合并操作的平均时间复杂度为O(n),而完全二叉树的深度为|log2n|。总的平均时间复杂度为O(nlogn)。而且,归并排序的最好,最坏,平均时间复杂度均为O(nlogn)。

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
def MergeSort(lists):
if len(lists) <= 1:
return lists
num = int(len(lists) / 2)

left = MergeSort(lists[:num])
right = MergeSort(lists[num:])

return Merge(left, right)

def Merge(left, right):
r, l = 0, 0
result = []
while l < len(left) and r < len(right):
if left[l] <= right[r]:
result.append(left[l])
l += 1
else:
result.append(right[r])
r += 1
result += list(left[l:])
result += list(right[r:])
return result

7.堆排序

​ 见堆排序

7.桶排序

1.为什么要使用Attention机制?

​ Attention机制最初起源于seq2seq中,经典的encoder-decoder做机器翻译时,通常是是使用两个RNN网络,一个用来将待翻译语句进行编码输出一个vector,另一个RNN对上一个RNN网络的输出进行解码,也就是翻译的过程。但是经典的encoder-decoder模式最大的缺点在于:不管输入多么长的语句,最后输出的也只是最后一个vector,这个向量能否有效的表达该语句非常值得怀疑,而Attention机制正是利用了RNN整个过程中的各个输出来综合进行编码

原始序列模型的不足:

​ 1.从编码器到解码器的语境矩阵式大小是固定的,这是个瓶颈问题

​ 2.难以对长的序列编码,并且难以回忆长期依赖

2.Attention原理

1.首先在RNN的过程中保存每个RNN单元的隐藏状态(h1….hn)

2.对于decoder的每一个时刻t,因为此时有decoder的输入和上一时刻的输出,所以我们可以的当前步的隐藏状态St

3.在每个t时刻用St和hi进行点积得到attention score

4.利用softmax函数将attention score转化为概率分布

​ 利用下面的公式进行概率分布的计算:

5.利用刚才的计算额Attention值对encoder的hi进行加权求和,得到decoder t时刻的注意力向量(也叫上下文向量)

6.最后将注意力向量和decoder t时刻的隐藏状态st并联起来做后续步骤(例如全连接进行分类)

3.Attention计算方式

​ 前面一节中,我们的概率分布来自于h与s的点积再做softmax,这只是最基本的方式。在实际中,我们可以有不同的方法来产生这个概率分布,每一种方法都代表了一种具体的Attention机制。在各个attention中,attention的计算方式主要有加法attention乘法attention两种。

3.1 加法attention

​ 在加法attention中我们不在使用st和hi的点乘,而是使用如下计算:

​ 其中,va和Wa都是可以训练的参数。使用这种方式产生的数在送往softmax来进行概率分布计算

3.2 乘法attention

​ 在乘法attention中使用h和s做点乘运算:

​ 显然乘法attention的参数更少,计算效率更高。

4.self-attention

​ 思想:在没有任何额外信息情况下,句子使用self-attention机制来处理自己,提取关键信息

在attention机制中经常出现的一种叫法:

​ query:在一个时刻不停地要被查询的那个向量(前面的decodert时刻的隐藏状态st)。

​ key: 要去查询query计算个query相似关度的向量(前面的encoder在各个时刻的隐藏状态hi)

​ value: 和softmax得到的概率分布相乘得到最终attention上下文向量的向量(前面的encoder在各个时刻的隐藏状态hi)

这里我们可以明显知道,任意attention中key和value是相同的

​ attention就是key、value、和query都来自同一输入的(也是相同的)

1._slots_

​ 用于指定class 实例能够指定的属性

注意:_slots_只对当前类起作用,对其子类无效

1
2
3
4
5
6
7
8
9
10
import traceback
class Myclass(object):
__slots__ = ["name","set_name]

s = MyClass()
s.name = "john" #这里可以进行正常的赋值,因为包含在__slots__中
try:
s.age = 2 #这里不能进行正常赋值
except AttributeError:
traceback.print_exc()

Output:

2.@property属性

​ @property 可以实现比较方便的属性set、get设置

1.使用@property相当于讲将一个函数变为get某个属性值
2.@属性名称.setter可以实现设置一个属性的set条件

​ 使用上面的两种修饰符,可以实现

​ 1.对写入属性的限制,只有符合规范的才允许写入

​ 2.设置只读属性,只能够读取,不能写入,只能从其他属性处计算出

下面的就是对score属性的写操作进行了一些限制,将double_score属性设置为只读属性

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42

class MyClass(object):

@property
def score(self):
return self._score

@score.setter
def score(self,value):
#不是int类型时引发异常
if not isinstance(value,int):
raise ValueError("not int") #raise的作用是显示的引发异常
#超出范围时引发异常
elif (value<0) or (value>100):
raise ValueError("score must in 0 to 100")

self._score = value

@property
def double_score(self):
return self._score*2


s = MyClass()
s.score = 3
print(s.score)
try:
s.score = 2300
except ValueError:
traceback.print_exc()

try:
s.score = "dfsd"
except ValueError:
traceback.print_exc()

print(s.double_score)

try:
s.double_score = 2
except Exception:
traceback.print_exc()

描述器,主要是用来读写删除类的行为

函数可以直接使用_name_属性来获取函数名称

1
2
3
4
5
6
7
def now():
print("2012")

print(now.__name__)

output:
"now"

1.常量名称全部大写

2.代码长度一行不能超过80字符

3.类名使用驼峰式命名,一般不超过3

4.函数名称

​ 因为系统移植过程中一直出现python3程序向python2转化的问题,因此这里记录下我在程序移植过程中遇到过的坑。

1.python2和python3的url编码解码函数接口

2.python2和python3向文件中写入中文时指定编码方式

​ 对于python3来说,要在写入文件时指定编码方式是十分简单的,只需要下面的方式即可:

1
2
with open(filename,'a',encoding='utf-8') as f:
f.write("中文")

​ 但对于python2,要在写入文件时,手动添加utf-8文件的前缀

1
2
3
4
5
6
import sys
reload(sys)
sys.setdefaultencoding('utf-8')
with open(r'd:\sss.txt','w') as f:
  f.write(unicode("\xEF\xBB\xBF", "utf-8"))#函数将\xEF\xBB\xBF写到文件开头,指示文件为UTF-8编码。
  f.write(u'中文')

3.python2和python3外置函数区别

在python3中外置函数文件可以直接进行调用,如在下面的文件结构

1
2
|--main.py
tools——

在python2中外置函数文件目录下必须要有init.py 空文件,否则无法进行加载

1
2


4.python2和python3文件中中文问题

​ 在python3中,输出和备注等一切位置都可以直接使用中文,不需要任何额外的代码,在python2中,必须要在包含中文的python文件中加入

1
#coding=utf-8

​ 才能出现中文,否则报错。

1.查看模型结构

1
2


  在使用python处理多线程或者循环次数较多时,常常会因为系统爆出一些警告信息而影响结果的查看,比如下面的警告:

十分影响美观,造成结果混乱,很难找到有效的信息,下面我们使用python自带的warning设置,设置过滤warn级别的告警

1
2
import warnings
warnings.filterwarnings("ignore")

结果变为:

​ python多进程之前一直在在写一些小程序,这次正好需要写一个比较正式的多进行处理,正好系统的总结下python多进行的一些核心知识。

​ 首先python中常用的提高效率的方式主要主要包括多线程、多进程两种,但是在python中的多线程并不是正正的多线程,只有在网络请求密集型的程序中才能有效的提升效率,在计算密集型、IO密集型都不是很work甚至会造成效率下降,因此提升效率的方式就主要以多进程为主。

​ python中多进程需要使用python自带包multiprocessing,multiprocessing支持子进程、通信和共享数据、执行不同形式的同步,提供了Process、Queue、Pipe、Lock等组件

1
from multiprocessing import Lock,Value,Queue,Pool

创建进程类

1.单个进程类的创建

1.创建单进程

1
2


使用进程池创建多进程

​ Pool类可以提供指定数量的进程供用户调用,当有新的请求提交到Pool中时,如果池还没有满,就会创建一个新的进程来执行请求。如果池满,请求就会告知先等待,直到池中有进程结束,才会创建新的进程来执行这些请求。

​ 常用方法:

1.apply

​ 用于传递不定参数,同python中的apply函数一致,主进程会被阻塞直到函数执行结束(不建议使用,并且3.x以后不在出现)。

2.apply_async

​ 和apply类似,非阻塞版本,支持结果返回后进行回调

3.map

​ 函数原型:map(func, iterable[, chunksize=None])

​ Pool中的map和python内置的map用法基本上一致,会阻塞直到返回结果

4.map_async

​ 函数原型:map_async(func, iterable[, chunksize[, callback]])

​ 和map用法一致,但是它是非阻塞的

5.close

​ 关闭进程池,使其不再接受新的任务

6.terminal

​ 结束工作进程,不再处理未处理的任务

7.join

​ 主进程阻塞等待子进程退出,join方法要在close或terminal方法后面使用

1
2
3
4
from mutiprocess import Pool

process_nums = 20
pool = Pool(process_nums)

使用Lock来避免冲突

​ lock主要用于多个进程之间共享资源时,避免资源访问冲突,主要包括下面两个操作:

​ 1.look.acquire() 获得锁

​ 2.lock.release() 释放锁

​ 下面是不加锁时的程序:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
import multiprocessing
import time
def add(number,value,lock):
print ("init add{0} number = {1}".format(value, number))
for i in xrange(1, 6):
number += value
time.sleep(1)
print ("add{0} number = {1}".format(value, number))

if __name__ == "__main__":
lock = multiprocessing.Lock()
number = 0
p1 = multiprocessing.Process(target=add,args=(number, 1, lock))
p2 = multiprocessing.Process(target=add,args=(number, 3, lock))
p1.start()
p2.start()
print ("main end")

​ 结果为:

1
2
3
4
5
6
7
8
9
10
11
12
13
main end
init add1 number = 0
init add3 number = 0
add1 number = 1
add3 number = 3
add1 number = 2
add3 number = 6
add1 number = 3
add3 number = 9
add1 number = 4
add3 number = 12
add1 number = 5
add3 number = 15

​ 两个进程交替的来对number进行加操作,下面是加锁后的程序:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
import multiprocessing
import time
def add(number,value,lock):
lock.acquire()
try:
print ("init add{0} number = {1}".format(value, number))
for i in xrange(1, 6):
number += value
time.sleep(1)
print ("add{0} number = {1}".format(value, number))
except Exception as e:
raise e
finally:
lock.release()

if __name__ == "__main__":
lock = multiprocessing.Lock()
number = 0
p1 = multiprocessing.Process(target=add,args=(number, 1, lock))
p2 = multiprocessing.Process(target=add,args=(number, 3, lock))
p1.start()
p2.start()
print ("main end")

​ 结果为:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
main end
init add1 number = 0 #add1优先抢到锁,优先执行
add1 number = 1
add1 number = 2
add1 number = 3
add1 number = 4
add1 number = 5
init add3 number = 0 #add3被阻塞,等待add1执行完成,释放锁后执行add3
add3 number = 3
add3 number = 6
add3 number = 9
add3 number = 12
add3 number = 15
#注意观察上面add3部分,虽然在add1部分已经将number加到了5,但是由于number变量只是普通变量,不能在各个进程之间进行共享,因此add3开始还要从0开始加

使用Value和Array来进行内存之中的共享通信

​ 一般的变量在进程之间是没法进行通讯的,multiprocessing 给我们提供了 Value 和 Array 模块,他们可以在不通的进程中共同使用。

1.Value多进程共享变量

​ 将前面加锁的程序中的变量使用multiprocessing提供的共享变量来进行

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
import multiprocessing
import time
def add(number,add_value,lock):
lock.acquire()
try:
print ("init add{0} number = {1}".format(add_value, number.value))
for i in xrange(1, 6):
number.value += add_value
print ("***************add{0} has added***********".format(add_value))
time.sleep(1)
print ("add{0} number = {1}".format(add_value, number.value))
except Exception as e:
raise e
finally:
lock.release()

if __name__ == "__main__":
lock = multiprocessing.Lock()
number = multiprocessing.Value('i', 0)
p1 = multiprocessing.Process(target=add,args=(number, 1, lock))
p2 = multiprocessing.Process(target=add,args=(number, 3, lock))
p1.start()
p2.start()
print ("main end")

​ 输出结果为:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
#add3开始时是在add1的基础上来进行加的
main end
init add1 number = 0
***************add1 has added***********
add1 number = 1
***************add1 has added***********
add1 number = 2
***************add1 has added***********
add1 number = 3
***************add1 has added***********
add1 number = 4
***************add1 has added***********
add1 number = 5
init add3 number = 5
***************add3 has added***********
add3 number = 8
***************add3 has added***********
add3 number = 11
***************add3 has added***********
add3 number = 14
***************add3 has added***********
add3 number = 17
***************add3 has added***********
add3 number = 20

2.Array实现多进程共享内存变量

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
import multiprocessing
import time
def add(number,add_value,lock):
lock.acquire()
try:
print ("init add{0} number = {1}".format(add_value, number.value))
for i in xrange(1, 6):
number.value += add_value
print ("***************add{0} has added***********".format(add_value))
time.sleep(1)
print ("add{0} number = {1}".format(add_value, number.value))
except Exception as e:
raise e
finally:
lock.release()

def change(arr):
for i in range(len(arr)):
arr[i] = -arr[i]

if __name__ == "__main__":
lock = multiprocessing.Lock()
number = multiprocessing.Value('i', 0)
arr = multiprocessing.Array('i', range(10))
print (arr[:])
p1 = multiprocessing.Process(target=add,args=(number, 1, lock))
p2 = multiprocessing.Process(target=add,args=(number, 3, lock))
p3 = multiprocessing.Process(target=change,args=(arr,))
p1.start()
p2.start()
p3.start()
p3.join()
print (arr[:])
print ("main end")

​ 输出结果为:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
[0, 1, 2, 3, 4, 5, 6, 7, 8, 9]
init add3 number = 0
***************add3 has added***********
[0, -1, -2, -3, -4, -5, -6, -7, -8, -9]
main end
add3 number = 3
***************add3 has added***********
add3 number = 6
***************add3 has added***********
add3 number = 9
***************add3 has added***********
add3 number = 12
***************add3 has added***********
add3 number = 15
init add1 number = 15
***************add1 has added***********
add1 number = 16
***************add1 has added***********
add1 number = 17
***************add1 has added***********
add1 number = 18
***************add1 has added***********
add1 number = 19
***************add1 has added***********
add1 number = 20

使用Queue来实现多进程之间的数据传递

​ Queue是多进程安全队列,可以使用Queue来实现进程之间的数据传递,使用的方式:

1.put 将数据插入到队列中

​ 包括两个可选参数:blocked和timeout

​ (1)如果blocked为True(默认为True),并且timeout为正值,该方法会阻塞队列指定时间,直到队列有剩余,如果超时,会抛出Queue.Full

​ (2)如果blocked为False,且队列已满,那么立刻抛出Queue.Full异常

2.get 从队列中读取并删除一个元素

​ 包括两个可选参数:block和timeout

​ (1)blocked为True,并且timeout为正值,那么在等待时间结束后还没有取到元素,那么会抛出Queue.Empty异常

​ (2)blocked为False,那么对列为空时直接抛出Queue.Empty异常

python实现多进程最优方式

​ python中自带的joblib包自带了