0%

内容概要

  • 模板层(模板语法)

    1
    2
    3
    4
    5
    6
    7
    模板语法传值

    模板语法过滤器(内置方法)

    模板语法标签(流程控制)

    自定义过滤器和标签(了解)
  • 模板的导入与继承(面向对象)

内容详细

模板层之模板语法传值

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
https://www.cnblogs.com/Dominic-Ji/articles/11109067.html#_label15

注释
<!--HTML注释--> 浏览器能够查看
{#模板语法注释#} 浏览器查看不了

def reg(request):
# python基本数据类型
f = 1.1
i = 11
s = 'hello world'
l = [11,22,33,44]
d = {'username':"jason",'password':123}
t = (11,22,33,44,55,66,77)
se = {11,22,33,44,55,66}
b = True
# 函数(自动加括号调用函数 展示的是函数的返回值)
def func(args): # 模板语法不支持给函数传递额外的参数
print('from func')
return '下午有点困'
# 面向对象
class MyClass(object): # 自动加括号实例化产生对象
def get_obj(self):
return 'from obj'
@classmethod
def get_cls(cls):
return 'from cls'
@staticmethod
def get_func():
return 'from func'
obj = MyClass()
# 1.指名道姓的传(需要传的值较少)
# return render(request,'reg.html',{...})
# 2.一次性批量传(效率偏低) 为了教学方便我们使用locals居多
# return render(request, 'reg.html',locals())
return render(request,'reg.html',locals())

模板语法取值

1
2
3
4
取值只能使用句点符(.)
<p>{{ l.0 }}</p>
<p>{{ d.username }}</p>
<p>{{ d.hobby.3.addr }}</p>

模板语法之过滤器(内置方法)

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
<p>过滤器:  管道符 左侧当做过滤器的第一个参数 右侧有时候还可以支持再传一个参数(冒号开头)</p>
<p>统计长度:{{ l|length }}</p>
<p>自增运算:{{ i|add:123 }}、拼接操作:{{ s|add:'去你妹的' }}</p>
<p>日期转换:{{ ctime }}、{{ ctime|date:'Y-m-d' }}</p>
<p>文件大小:{{ file_size|filesizeformat }}</p>
<p>截取字符(三个点也算):{{ desc|truncatechars:6 }}</p>
<p>截取单词(三个点不算):{{ desc|truncatewords:3 }}</p>
<p>切片操作:{{ s|slice:'0:4' }}</p>
<p>默认值(判断布尔值):{{ b|default:'这个东西布尔值是False' }}</p>
<p>文本转义:{{ ht|safe }}</p>
<p>文本转义:{{ xss }}</p>
<p>文本转义:{{ ht1 }}</p>

后端转义
from django.utils.safestring import mark_safe
ht1 = '<h1>一级标题</h1>'
ht1 = mark_safe(ht1)

模板语法之标签(流程控制)

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
{{}}    变量相关(引用变量值)
{%%} 逻辑相关(流程控制 模块方法)

<p>标签: 流程控制</p>
{% for i in lll %}
{% if forloop.first %}
<p>这是我的第一次循环</p>
{% elif forloop.last %}
<p>这是我的最后一次循环</p>
{% else %}
<p>{{ i }}</p>
{% endif %}
{% empty %}
<p>该对象里面没有值</p>
{% endfor %}

自定义过滤器、标签、inclusion_tag

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
1.在应用下创建一个名字必须叫templatetags文件夹
2.在该文件夹内创建一个任意名称的py文件
3.在py文件内必须要书写两行固定的代码
from django.template import Library
register = Library()

# 自定义过滤器
@register.filter(is_safe=False,name='aaa')
def my_plus(a,b):
return a + b


# 自定义标签(自定义函数)
@register.simple_tag(name='bbb')
def func(a,b,c,d):
return '%s-%s-%s-%s'%(a,b,c,d)


# 自定义inclusion_tag(自行概括)
@register.inclusion_tag(filename='ccc.html',name='myinc')
def index(n):
new_l = ['第%s页'%i for i in range(1,n+1)]
return locals()

模板的继承(使用频率较高)

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
需求
页面主体不变 部分区域变化

模板继承
1.需要现在母版中使用block提前划定区域
{% block 区域名称 %}
{% endblock %}
2.在子版中先继承再局部替换
{% extends '模版.html' %}
{% block 区域名称 %}

{% endblock %}

总结
模板一般情况下都应该至少含有三个区域
css区域
content区域
js区域

补充
{{ block.super }} 调用模版内容

模板的导入

1
{% include 'myform.html' %}

自定义分页器

针对批量插入的数据,我们在前端展示的时候发现一个很严重的问题,一页展示了所有的数据,数据量太大,查看不方便

针对数据量大但又需要全部展示给用户观看的情况下,我们统一做法都是做分页处理

分页推导

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
首先我们需要明确的时候,get请求也是可以携带参数的,所以我们在朝后端发送查看数据的同时可以携带一个参数告诉后端我们想看第几页的数据

其次我们还需要知道一个点,queryset对象是支持索引取值和切片操作的,但是不支持负数索引情况

接下来我们就可以推导我们的自定义分页器步骤了

current_page = request.GET.get("page",1) # 获取用户想访问的页码 如果没有 默认展示第一页
try: # 由于后端接受到的前端数据是字符串类型所以我们这里做类型转换处理加异常捕获
current_page = int(current_page)
except Exception as e:
current_page = 1
# 还需要定义页面到底展示几条数据
per_page_num = 10 # 一页展示10条数据

# 需要对总数据进行切片操作 需要确定切片起始位置和终止位置
start_page = ?
end_page = ?
"""
下面需要研究current_page、per_page_num、start_page、end_page四个参数之间的数据关系
per_page_num = 10
current_page start_page end_page
1 0 10
2 10 20
3 20 30
4 30 40

per_page_num = 5
current_page start_page end_page
1 0 5
2 5 10
3 10 15
4 15 20
可以很明显的看出规律
start_page = (current_page - 1) * per_page_num
end_page = current_page* per_page_num
"""

数据总页面获取

当我问你下面几个问题的时候,你的内心肯定是鄙视的,不信的话那就请听题

问题1:总数据有100条,每页展示10条,总共需要几页?

答案:10条

问题2:总数据有101条,每页展示10条,总共需要几页?

答案:11条

问题3:如何通过代码算出到底需要多少条?

答案:去你妹的,不会!!!

内置方法之divmod

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
>>> divmod(100,10)
(10, 0) # 10页
>>> divmod(101,10)
(10, 1) # 11页
>>> divmod(99,10)
(9, 9) # 10页
# 余数只要不是0就需要在第一个数字上加一
我们可以判断元祖的第二个数字是否为0从而确定到底需要多少页来展示数据

book_queryset = models.Book.objects.all()
all_count = book_queryset.count() # 数据总条数
all_pager, more = divmod(all_count, per_page_num)
if more: # 有余数则总页数加一
all_pager += 1
至此分页器大致的功能及思路我们就已经大致清楚了

最后我们只需要利用start_page和end_page对总数据进行切片取值再传入前端页面就能够实现分页展示

book_list = models.Book.objects.all()[start_page:end_page]
return render(request,'booklist.html',locals())
接下来就是前端页面的代码编写了

{% for book in book_list %}
<p>{{ book.title }}</p>
{% endfor %}

现在我们实现了最简单的分页,但是前端没有按钮去让用户点击需要看第几页,所以我们需要渲染分页器相关代码,这里我们不做要求直接去bootstrap框架拷贝代码即可

终极大法

上面是自定义分页器开发流程的基本思路,我们不需要掌握代码的编写,只需要掌握基本用法即可

自定义分页器封装代码

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
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
class Pagination(object):
def __init__(self, current_page, all_count, per_page_num=2, pager_count=11):
"""
封装分页相关数据
:param current_page: 当前页
:param all_count: 数据库中的数据总条数
:param per_page_num: 每页显示的数据条数
:param pager_count: 最多显示的页码个数
"""
try:
current_page = int(current_page)
except Exception as e:
current_page = 1

if current_page < 1:
current_page = 1

self.current_page = current_page

self.all_count = all_count
self.per_page_num = per_page_num

# 总页码
all_pager, tmp = divmod(all_count, per_page_num)
if tmp:
all_pager += 1
self.all_pager = all_pager

self.pager_count = pager_count
self.pager_count_half = int((pager_count - 1) / 2)

@property
def start(self):
return (self.current_page - 1) * self.per_page_num

@property
def end(self):
return self.current_page * self.per_page_num

def page_html(self):
# 如果总页码 < 11个:
if self.all_pager <= self.pager_count:
pager_start = 1
pager_end = self.all_pager + 1
# 总页码 > 11
else:
# 当前页如果<=页面上最多显示11/2个页码
if self.current_page <= self.pager_count_half:
pager_start = 1
pager_end = self.pager_count + 1

# 当前页大于5
else:
# 页码翻到最后
if (self.current_page + self.pager_count_half) > self.all_pager:
pager_end = self.all_pager + 1
pager_start = self.all_pager - self.pager_count + 1
else:
pager_start = self.current_page - self.pager_count_half
pager_end = self.current_page + self.pager_count_half + 1

page_html_list = []
# 添加前面的nav和ul标签
page_html_list.append('''
<nav aria-label='Page navigation>'
<ul class='pagination'>
''')
first_page = '<li><a href="?page=%s">首页</a></li>' % (1)
page_html_list.append(first_page)

if self.current_page <= 1:
prev_page = '<li class="disabled"><a href="#">上一页</a></li>'
else:
prev_page = '<li><a href="?page=%s">上一页</a></li>' % (self.current_page - 1,)

page_html_list.append(prev_page)

for i in range(pager_start, pager_end):
if i == self.current_page:
temp = '<li class="active"><a href="?page=%s">%s</a></li>' % (i, i,)
else:
temp = '<li><a href="?page=%s">%s</a></li>' % (i, i,)
page_html_list.append(temp)

if self.current_page >= self.all_pager:
next_page = '<li class="disabled"><a href="#">下一页</a></li>'
else:
next_page = '<li><a href="?page=%s">下一页</a></li>' % (self.current_page + 1,)
page_html_list.append(next_page)

last_page = '<li><a href="?page=%s">尾页</a></li>' % (self.all_pager,)
page_html_list.append(last_page)
# 尾部添加标签
page_html_list.append('''
</nav>
</ul>
''')
return ''.join(page_html_list)

自定义分页器使用

后端

1
2
3
4
5
6
7
def get_book(request):
book_list = models.Book.objects.all()
current_page = request.GET.get("page",1)
all_count = book_list.count()
page_obj = Pagination(current_page=current_page,all_count=all_count,per_page_num=10)
page_queryset = book_list[page_obj.start:page_obj.end]
return render(request,'booklist.html',locals())

前端

1
2
3
4
5
6
7
8
9
10
<div class="container">
<div class="row">
<div class="col-md-8 col-md-offset-2">
{% for book in page_queryset %}
<p>{{ book.title }}</p>
{% endfor %}
{{ page_obj.page_html|safe }}
</div>
</div>
</div>

内容概要

  • 路由匹配
  • 无名有名分组
  • 反向解析
  • 无名有名分组反向解析(难理解)
  • 路由分发
  • 名称空间
  • 伪静态

内容详细

路由匹配

1
2
3
4
5
6
7
8
9
10
11
12
urls.py

url()方法第一个参数其实是一个正则表达式
第一个参数只要能够匹配到内容就算是符合匹配条件直接停止匹配执行视图函数


# 路由匹配
url(r'^test/$',views.test),
url(r'^testadd/$',views.testadd)

# 取消自动加斜杠(默认为True)
APPEND_SLASH = False

无名分组

1
2
3
在路由匹配的时候如果给正则表达式加上了括号
那么匹配到路由之后会将括号内正则表达式匹配的到内容当做位置参数传递给视图函数
url(r'^test/(\d+)/(\d+)/',views.test) # test(request,正则匹配的内容)

有名分组

1
2
3
在路由匹配的时候如果给正则表达式加上了括号并且命名
那么匹配到路由之后会将括号内正则表达式匹配的到内容当做关键字参数传递给视图函数
url(r'^testadd/(?P<user_id>\d+)/',views.testadd) # testadd(request,user_id=正则匹配的内容)

两者结合使用?

1
2
3
4
5
6
url(r'^testsub/(\d+)/(?P<sub_id>\d+)/',views.sub)
无名有名分组两者不能混合使用!!!

单独的情况下可以重复多次!!!
url(r'^testsub/(\d+)/(\d+)/')
url(r'^testsub/(?P<sub_id>\d+)/(?P<sub_id>\d+)/')

反向解析

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
"""
通过别名反向解析到一个结果
该结果可以访问到对应的视图函数
"""
前端页面反向解析
{% url 'index_view' %}
后端流程反向解析
from django.shortcuts import render, HttpResponse, redirect, reverse
def func(request):
# 第一种
_url = reverse('index_view')
print(_url)
# return redirect('/index/')
# 第二种
return redirect('index_view') # 还可以直接写别名

# 起别名不能冲突

无名有名反向解析

1
2
3
4
5
6
7
8
9
10
11
12
13
14
url(r'^login/(\d+)/',views.login,name='login_view')
reverse('login_view',args=(1,))
{% url 'login_view' 1 %}
# 数字只要能够让\d+匹配到就可以 在实际项目中一般都是动态获取


url(r'^reg/(?P<user_id>\d+)/',views.reg,name='reg_view')
reverse('reg_view',kwargs={'user_id':666})
{% url 'reg_view' user_id=123 %}

"""无论是无名分组还是有名分组的反向解析都可以使用一样的格式"""
url(r'^reg/(?P<user_id>\d+)/',views.reg,name='reg_view')
reverse('reg_view',args=(123,))
{% url 'reg_view' 123 %}

路由分发

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
当django的app特别多的时候 一个urls.py太过冗余
在django中每一个app都可以拥有自己的urls.py static文件夹 templates文件夹

路由分发
总路由不再做url与视图函数直接匹配
而是先根据url分辨出请求哪个业务
之后分配给下面的app再去匹配视图函数

复杂版本
from app01 import urls as app01_urls
from app02 import urls as app02_urls
url(r'^app01/',include(app01_urls)),
url(r'^app02/',include(app02_urls)),

简化版本
url(r'^app01/',include('app01.urls')),
url(r'^app02/',include('app02.urls')),

# 子路由还是按照之前总路由的写法

名称空间

1
2
3
4
5
6
7
8
9
10
11
12
13
多个应用在反向解析的时候出现了别名冲突的情况
django是无法做到一一对应的 >>>名称空间

url(r'^app01/',include('app01.urls',namespace='app01')),
url(r'^app02/',include('app02.urls',namespace='app02')),
reverse('app01:index_view')
reverse('app02:index_view')
{% url 'app01:index_view' %}
{% url 'app02:index_view' %}

# 名称空间其实也可以不需要使用
只需要确保多个应用之间别名也不冲突的情况
eg:起别名的时候加上应用名前缀

伪静态页面

1
2
3
4
5
6
7
静态页面
页面上的数据一般都是直接写死的
伪静态页面
目的是为了提升网页被SEO查询出来的概率

如何优化都干不过RMB玩家
"""所有的搜索引擎都是爬虫程序"""

内容概要

  • 小白必会三板斧
  • request对象方法初识
  • form表单上传文件
  • Jsonresponse
  • FBV与CBV

内容详细

小白必会三板斧

1
2
3
4
5
6
HttpResponse
render
redirect
# 视图函数必须返回一个HttpResponse对象

# 具体参考HttpResponse源码

request方法初识

1
2
3
4
5
6
7
8
9
10
11
12
13
'''
request对象:(
1:request.method(判断请求格式)
2:request.POST(.get、.getlist)
3:request.GET(.get、.getlist)
4:request.body(一串二进制)
5:request.files(接收文件)
6:request.path
7:request.path_info
8:request.get_full_path(接收完整url及?号后面的参数)
...

'''

form表单上传文件

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
必须要指定的参数
1.method='post'
2.enctype='/multipart/form-data/'
<form action="" method="post" enctype="multipart/form-data">
<input type="file" name="myfile" multiple>
<input type="submit" class="btn btn-primary btn-block">
</form>

def index(request):
if request.method == 'POST':
# print(request.POST)
# print(request.GET)
# print(request.FILES) # 获取文件数据
file_obj = request.FILES.get('myfile')
print(file_obj.name) # 获取文件名
with open(file_obj.name,'wb') as f:
for chunk in file_obj.chunks():
f.write(chunk)
return render(request,'index.html')

Jsonresponse

1
2
3
JsonRespanse:(
return JsonRespanse(dict)(默认只能传字典,需要传其他类型需要将safe参数改为False

FBV与CBVFBV 基于函数的视图

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
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
    FBV使用频率较低(基础阶段)
CBV 基于类的视图
CBV实际开发项目使用频率较高(高级阶段)
views.py视图层
视图函数
不仅仅可以是函数也可以是类
1.面向过程式编程
2.面向对象式编程

# FBV
def index(request):
return HttpResponse('')
# CBV
视图层代码
from django.views import View


class Mylogin(View):
def get(self,request):
return HttpResponse('get方法')
def post(self,request):
return HttpResponse('post方法')
路由层代码
url(r'^login/', views.Mylogin.as_view())
========================================================
CBV:
url(r'^index/',views.类名.as_view())

FBV:

url(r^index/',views.视图函数名)



1:as_view()里面返回了一个view函数(所以CBV本质也是FBV)

2:匹配路由调用view时,view里面生成了一个当前类(自己写的类)的对象,并返回了一个dispatch方法

3:dispatch方法里判断了当前请求的格式并将其变形成全小写的字符串,再判断当前请求格式是否合法,如果合法,就将该字符串通过反射方法将其调用并返回(get、post)---> def get:.../def post:...



CBV的本质也是和FBV一样,都是路由+视图函数内存地址



=====================================



CBV添加装饰器的三种方式:(先导入method_decorader模块)



1:放方法头上(指名道姓)



2:放他父类头上(可针对性的加装饰器,扩展性强)



3:重写dispatch方法(放dispatch头上)(作用于所有方法)



FBV:导入:from django.view.decorators.csrf import csrf_protect,csrf_exempt



csrf_protect:需要校验



csrf_exempt:忽视校验







正常添加装饰器即可







CBV:csrf_protect:可以正常按CBV三种添加方式



csrf_exempt:只能在dispath上加装饰器

CBV源码

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
# 切入点
url(r'^login/', views.Mylogin.as_view())
'''类名点名字还加括号 名字要么是绑定给类的方法 要么是无参函数'''

1.as_view()绑定给类的方法
@classonlymethod
def as_view(cls, **initkwargs)
2.CBV路由匹配本质与FBV一致
# CBV
url(r'^login/', views.Mylogin.as_view())
# CBV本质
# url(r'^login/', views.view)
3.匹配成功之后执行view函数代码
def view(request, *args, **kwargs):
self = cls(**initkwargs)
return self.dispatch(request, *args, **kwargs)
4.查看dispatch方法(对象查找属性和方法一定要严格按照顺序来)
def dispatch(self, request, *args, **kwargs):
# 判断当前请求方法是否符合八个默认的请求方法中
# 1.get
if request.method.lower() in self.http_method_names:
# getattr(对象,'get','拿不到的报错信息') >>> Mylogin里面的get方法
handler = getattr(self, request.method.lower(), self.http_method_not_allowed)
else:
handler = self.http_method_not_allowed
return handler(request, *args, **kwargs) # get(request,...)

img

pip安装源

1
2
3
4
5
6
7
8
"""
1、采用国内源,加速下载模块的速度
2、常用pip源:
-- 豆瓣:https://pypi.douban.com/simple
-- 阿里:https://mirrors.aliyun.com/pypi/simple
3、加速安装的命令:
-- >: pip install -i https://pypi.douban.com/simple 模块名
"""

永久配置安装源

  • Windows
1
2
3
4
5
"""
1、文件管理器文件路径地址栏敲:%APPDATA% 回车,快速进入 C:\Users\电脑用户\AppData\Roaming 文件夹中
2、新建 pip 文件夹并在文件夹中新建 pip.ini 配置文件
3、新增 pip.ini 配置文件内容
"""
  • Linux、MacOS
1
2
3
4
5
6
7
8
"""
1、在用户根目录下 ~ 下创建 .pip 隐藏文件夹,如果已经有了可以跳过
-- mkdir ~/.pip
2、进入 .pip 隐藏文件夹并创建 pip.conf 配置文件
-- cd ~/.pip && touch pip.conf
3、启动 Finder(访达) 按 cmd+shift+g 来的进入,输入 ~/.pip 回车进入
4、新增 pip.conf 配置文件内容
"""
  • 配置文件内容
1
2
3
4
5
6
7
8
"""
[global]
index-url = http://pypi.douban.com/simple
[install]
use-mirrors =true
mirrors =http://pypi.douban.com/simple/
trusted-host =pypi.douban.com
"""

虚拟环境搭建

  • 优点
    • 使不同应用开发环境相互独立
    • 环境升级不影响其他应用,也不会影响全局的python环境
    • 防止出现包管理混乱及包版本冲突
  • Windows
1
2
3
4
5
6
7
8
9
10
11
12
13
14
# 安装

# 建议使用pip3安装到python3环境下
pip3 install virtualenv
pip3 install virtualenvwrapper-win

# 配置环境变量

# 控制面板 => 系统和安全 => 系统 => 高级系统设置 => 环境变量 => 系统变量 => 点击新建 => 填入变量名与值
变量名:WORKON_HOME 变量值:自定义存放虚拟环境的绝对路径
eg: WORKON_HOME: D:\Virtualenvs

# 同步配置信息:
# 去向Python3的安装目录 => Scripts文件夹 => virtualenvwrapper.bat => 双击
  • Linux、MacOS
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
# 安装

# 建议使用pip3安装到python3环境下
pip3 install -i https://pypi.douban.com/simple virtualenv
pip3 install -i https://pypi.douban.com/simple virtualenvwrapper


# 配置

# 在 ~/.bash_profile 完成配置,virtualenvwrapper的默认默认存放虚拟环境路径是 ~/.virtualenvs
# WORKON_HOME=自定义存放虚拟环境的绝对路径,需要自定义就解注
VIRTUALENVWRAPPER_PYTHON=/usr/local/bin/python3
source /usr/local/bin/virtualenvwrapper.sh

# 在终端让配置生效:
-- source ~/.bash_profile
  • 使用
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
# 在终端工作的命令

# 1、创建虚拟环境到配置的WORKON_HOME路径下
# 选取默认Python环境创建虚拟环境:
-- mkvirtualenv 虚拟环境名称
# 基于某Python环境创建虚拟环境:
-- mkvirtualenv -p python2.7 虚拟环境名称
-- mkvirtualenv -p python3.6 虚拟环境名称

# 2、查看已有的虚拟环境
-- workon

# 3、使用某个虚拟环境
-- workon 虚拟环境名称

# 4、进入|退出 该虚拟环境的Python环境
-- python | exit()

# 5、为虚拟环境安装模块
-- pip或pip3 install 模块名

# 6、退出当前虚拟环境
-- deactivate

# 7、删除虚拟环境(删除当前虚拟环境要先退出)
-- rmvirtualenv 虚拟环境名称
  • pycharm的使用

img

  • 添加环境

img

  • 使用环境

img

全局解释器锁(GIL)

1
2
3
4
5
6
7
8
9
10
11
12
13
14
0 pypy(没有全局解释器锁) cpython(99.999999%)
-pypy python好多模块用不了,
1 全局解释器锁,GIL锁(cpython解释器的问题)
-当年python设计的时候,还是单核,没有多核的概念
-python需要做垃圾回收(gc)
-垃圾回收线程,进行垃圾回收
-设计了一个大锁(GIL锁),只有拿到这把锁的线程,才能执行
-同一时刻,在一个进程中,可以开多个线程,但是只能有一条线程在执行
-不能利用多核优势


只针对与cpython解释器(其他解释器,包括其他语言不这样)
2 如果是计算密集型:要开进程
3 如果是io密集型:要开线程

开启线程的两种方式

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
from threading import Thread
import time

#
# def task():
# time.sleep(1)
# print('我是子线程')
#
#
# if __name__ == '__main__':
# t=Thread(target=task)
# t.start()
# print('我是主线程')
#
#


###第二种方式

class MyThread(Thread):
def __init__(self,a):
self.a=a
super().__init__()
def run(self):
time.sleep(1)
print('我是子线程',self.a)

if __name__ == '__main__':
t=MyThread('aaaaa')
t.start()
print('我是主线程')

多线程与多进程比较

开启速度比较

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
#开线程消耗的资源,耗费的时间远远小于开进程
from threading import Thread
import time
import os
from multiprocessing import Process

def task():
time.sleep(0.1)
print('我是子线程')


if __name__ == '__main__':
####线程
# ctime=time.time()
# t=Thread(target=task)
# t.start()
# t.join() # 等待子线程执行完成主线程再执行
# print('我是主线程')
# print(time.time()-ctime)

##进程
ctime=time.time()
t=Process(target=task)
t.start()
t.join() # 等待子线程执行完成主线程再执行
print('我是主线程')
print(time.time()-ctime)

内存数据的共享问题

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
# 线程间数据共享
from threading import Thread
import time
import os
from multiprocessing import Process

def task():
global n
n=10
print(n)


if __name__ == '__main__':
####线程
n=100
t=Thread(target=task)
t.start()
t.join() # 等待子线程执行完成主线程再执行
print('我是主线程')
print(n)

Thread类的其他方法

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
60
61
62
63
64
65

from threading import Thread
import threading
import time
def task():
# time.sleep(0.01)
#在子线程中执行
# res = threading.currentThread()
# print(res)

res=threading.get_ident()
print('子线程:',res)
print('我是子线程')


if __name__ == '__main__':
t=Thread(target=task)
t1=Thread(target=task)

t.start()
t1.start()
# print(t.is_alive()) #看线程是否存活
#
# print(t.getName() ) # 获取线程的名字
# t.setName('lqz') # 设置线程民资
# print(t.getName() )
#
#
# print('主线程')
# time.sleep(0.02)
# print(t.is_alive())


# 主线程中执行,返回当前线程对象
# res=threading.currentThread()
# print(res)


# 返回当前进程中正在运行的子线程对象列表
# res=threading.enumerate()
# print(res)


# 返回当前正在运行的线程个数
# res=threading.activeCount()
# print(res)

# 线程id号
res=threading.get_ident()
print('主线程:',res)




'''
t.is_alive()
t.getName()
t.setName('lqz')

threading:模块下的一些方法
res=threading.currentThread()
res=threading.enumerate()
res=threading.activeCount()
res=threading.get_ident()
'''

join方法

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
# 等待子线程执行结束


from threading import Thread
import time

def task():
time.sleep(2)
print('我是子线程')


if __name__ == '__main__':
ll=[]
for i in range(1000):
t=Thread(target=task)

t.start()
ll.append(t)

for i in ll:
i.join()



# 主线程等待子线程执行完再执行
print('我是主线程,子线程全都执行完了')

守护线程

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
from threading import Thread
import time

def task():
time.sleep(2)
print('我是子线程')


if __name__ == '__main__':
t=Thread(target=task)
t.setDaemon(True) # 如果主线程执行结束,子线程也结束(不执行了)
t.start()

#只要主线程执行结束,子线程也结束
print('主线程执行结束')

同步锁(互斥锁)

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
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
## 多个线程操作同一个数据(变量),会出现并发安全的问题
# from threading import Thread,Lock
# import time
# import random
# def task():
# global n
#
#
# ### 临界区(加锁)
# time.sleep(random.random())
# temp=n
# time.sleep(random.random())
# temp=temp-1
# n=temp
#
# ##模拟不出来,因为太快了,没有cup的切换(io,时间片到了),模拟io,让cpu切换
#
# # n-=1
#
#
# if __name__ == '__main__':
# n=10
# ll=[]
# for i in range(10):
# t=Thread(target=task)
# t.start()
# ll.append(t)
#
# for i in ll:
# i.join()
#
#
# print(n)



###出现了并发安全的问题,加锁解决

from threading import Thread,Lock
import time
import random
def task_lock(lock):
global n


### 临界区(加锁)
with lock:
time.sleep(random.random())
temp=n
time.sleep(random.random())
temp=temp-1
n=temp

##模拟不出来,因为太快了,没有cup的切换(io,时间片到了),模拟io,让cpu切换

# n-=1

def task_no_lock():

global n
time.sleep(random.random())
temp=n
time.sleep(random.random())
temp=temp-1
n=temp



if __name__ == '__main__':
n=10
lock=Lock()
ll=[]
for i in range(10):
# t=Thread(target=task_lock,args=[lock,])
t=Thread(target=task_no_lock,args=[lock,])
t.start()
ll.append(t)
t.join()

# for i in ll:
# i.join()


print(n)


'''
互斥锁和join的区别
如果使用互斥锁:只锁临界区,只有临界区是串行,其他地方还是并发的
如果使用join,整个过程全变成串行执行
'''

信号量

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
### 信号量可以理解为多把锁,同时允许多个线程来更改数据

from threading import Thread,Semaphore

import time
import random
def task(sm,i):
sm.acquire()
print('%s:这个人在上厕所'%i)
time.sleep(random.random())
print('%s:这个人拉完了'%i)
sm.release()



sm=Semaphore(5)
for i in range(40):
t=Thread(target=task,args=[sm,i])
t.start()

Event事件

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
Event事件:
一些线程需要等到其他线程执行完成之后才能执行,类似于发射信号
比如一个线程等待另一个线程执行结束再继续执行


# 一些线程需要等到其他线程执行完成之后才能执行,类似于发射信号
# 比如一个线程等待另一个线程执行结束再继续执行

from threading import Thread, Event

import time
import random


def girl(event):
print('赵丽颖现在在结婚状态')
time.sleep(1)
# 离婚了,发送信号
print('赵丽颖离婚了')
event.set() # 发送一个信号


def boy(i, event):
print('屌丝%s:在等赵丽颖的离婚信号'%i)
event.wait() # 收不到信号之前,一直卡在这
print('屌丝%s号,收到了离婚信号,开始追' % i)


event = Event()
t = Thread(target=girl, args=[event, ])
t.start()

for i in range(10):
t1 = Thread(target=boy, args=[i, event])
t1.start()



## 写两条线程,一条线程读一个文件的头2分之一,另一个线程读这个文件的后2分之一,但是必须第一个线程读完,发送信号后,第二个线程才能读

总结

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
1 GIL锁:全局解释器锁,在解释器之上的一把大锁,线程必须获得这把锁,才能执行,只针对与cpython解释器
2 GIL和线程锁有什么区别?有了GIL锁,为什么还要线程锁?
-本身GIL和线程锁,都是线程级别的锁,GIL是内置的,解释器里的
-线程锁:开发者定义的
3 多核cpu:
如果是计算密集型:开进程
io密集型:开线程

4 开启线程的两种方式(对比进程)
5 进程和线程的比较
-进程id比较
-开启效率的比较
-共享变量

6 Thread类的其他方法,threading模块下的其他方法

7 线程join(等待子线程执行完成)
8 守护线程(如果主线程执行完成,子线程也结束)
9 互斥锁,同步锁:为了保证并发情况下数据安全,把对数据的操作过程变成串行,牺牲了效率,保证了数据安全
10 信号量,Event


加入链接循环的套接字服务端

1
2
3
4
5
6
7
8
9
10
11
import socket
sever = socket.socket()
sever.bind(('127.0.0.1', 8087))
sever.listen(5)
while True:
sock, addr = sever.accept()
data = sock.recv(1024)
print(data)
sock.send(data.upper())
sock.close()
sever.close()

加入通信循环的套接字服务端

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
import socket
server=socket.socket(family=socket.AF_INET, type=socket.SOCK_STREAM)

server.bind(('127.0.0.1',81))


server.listen(1)

print('开始等待客户端连接了')
while True:

sock,addr=server.accept()
print(sock)
print('客户端的地址是:',addr)


# 等待客户端发送过来的数据,如果客户端没有发送数据,会一直等着
while True:
try:
data=sock.recv(1024)
if len(data)==0: # 这个表示客户端正常断开了,结束通信循环
break
print(data.decode('utf-8'))
sock.send(data.upper())
except Exception as e:
print(e) #客户端非正常断开,需要异常捕获
break


sock.close()
server.close()

基于UDP的套接字客户端和服务端(了解)

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
import socket

# udp的服务端
server = socket.socket(type=socket.SOCK_DGRAM)

# 监听地址和端口
server.bind(('127.0.0.1', 82))

# 不需要listen,直接建立链接
print('等待客户端发送数据:')
while True:
#recvfrom回返回数据和客户端的地址
data,addr = server.recvfrom(1024)
print(data)
print(addr)
server.sendto(data.upper(), addr)

server.close()

操作系统的发展史

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
1 手工操作——穿孔卡片
-用户独占全机
-CPU的利用不充分
2 批处理
-把一个操作整个写到磁带中,以后要进行这个操作,直接拿着磁带,读入即可
-脱机批处理
-联机批处理

3 多道程序系统(重要)
-当一道程序因I/O请求而暂停运行时,CPU便立即转去运行另一道程序
-各道程序轮流地用CPU,并交替运行
4 分时系统(重要)
-多个程序在运行,时间片的概念,cpu执行完固定的时间,就会转去另一个程序
5 通用操作系统
多道批处理系统,分时


io操作:(通通不占用cpu)(重要)
键盘输入,从硬盘拿数据,从网络加载数据--->都叫输入
显示在显示器,写到硬盘,从网络发送数据--->都叫输出

进程基础

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
狭义定义:进程是正在运行的程序的实例(an instance of a computer program that is being executed)。

广义定义:进程是一个具有一定独立功能的程序关于某个数据集合的一次运行活动。它是操作系统动态执行的基本单元,在传统的操作系统中,进程既是基本的分配单元

(重要)↓↓↓
# 进程是资源分配的最小单位,线程是cpu执行的最小单位
# 一个程序运行,最少有一个进程
# 一个进程里最少有一条线程

(重要)↓↓↓
# 进程和程序的区分
-程序可以作为一种软件资料长期存在,而进程是有一定生命期的。
-程序是永久的,进程是暂时的。

(重要)↓↓↓
# 进程的状态()
-就绪态:可以被cpu调度执行了,还没有执行,排着队
-运行态:在cpu中运行,正在运行(如果到了时间片,也会被调度出去,调度出去的程序是就绪态)
-阻塞态:io操作,把数据加载到内存中

img

并发和并行

1
2
3
4
5
6
7
并发:(你在跑步,鞋带开了,停下跑步,系鞋带,系完以后,继续跑步,在一个时间段内来看,你干了多个事)
-单核下的并发
:在一个时间段内,处理多件事

并行:(你在跑步,你用随身听在听着,同一时刻,在干多个事)
-只有多核才涉及到并行
:在某一个时刻,有多件事同时被处理

开启多进程

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
from multiprocessing import Process


#如果在win下开多进程,必须写main,否则报错(注意)
import time
def wirte_file(s):

time.sleep(5)
with open('a.txt','a') as f:
f.write(s)
f.write('\n')

if __name__ == '__main__':
time.sleep(5)
# wirte_file()
# 开启多进程的第一个方式
p=Process(target=wirte_file,args=['lqz is nb'])
# 执行该进程
p.start()


# 又开了一个进程
p1 = Process(target=wirte_file,args=['egon is dsb'])
# 执行该进程
p1.start()

总结

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
1 基于socket写TCP的客户端和服务端
-加入链接循环
-加入通信循环
-客户端主动断开---->服务端一直收空
-客户端强制断开---->服务端会报错
-同一时刻,只能有一个客户端与服务端交互

2 基于UDP的客户端和服务端
-数据不可靠,应用场景比较少

3 并发编程(进程,线程,协程)

4 进程:
-资源分配的最小单位,线程是cpu调度的最小单位
-食谱:程序
-拿到食谱做法的过程,叫进程
-进程:程序运行的过程,一堆数据+做一件事的过程的集合

5 python中如何开启进程
-p=Process(target=task)
-p.start()

死锁问题(递归锁,可重入锁)

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
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
1 所谓死锁:是指两个或两个以上的进程或线程在执行过程中,因争夺资源而造成的一种互相等待的现象,若无外力作用,它们都将无法推进下去。此时称系统处于死锁状态或系统产生了死锁,这些永远在互相等待的进程称为死锁进程,如下就是死锁


2 可重入锁,递归锁
# from threading import Thread, Lock
# import time
# import random
#
#
# def eat1(lock_1, lock_2, name):
# lock_1.acquire()
# print('%s:拿到了筷子' % name)
# time.sleep(random.random())
# lock_2.acquire()
# print('%s:拿到了面条' % name)
# print('开始吃面')
# time.sleep(random.random())
# lock_2.release()
# print('%s放下了面条' % name)
# lock_1.release()
# print('%s放下了筷子' % name)
#
#
# def eat2(lock_1, lock_2, name):
# lock_2.acquire()
# print('%s:拿到了面条' % name)
# time.sleep(random.random())
# lock_1.acquire()
# print('%s:拿到了筷子' % name)
# print('开始吃面')
# time.sleep(random.random())
# lock_1.release()
# print('%s放下了筷子' % name)
# lock_2.release()
# print('%s放下了面条' % name)
#
#
# if __name__ == '__main__':
# lock_1 = Lock()
# lock_2 = Lock()
# for i in ['张三', '李四', '王五']:
# t = Thread(target=eat1, args=[lock_1, lock_2, i])
# t.start()
# for i in ['赵6', '往7', '傻逼']:
# t = Thread(target=eat2, args=[lock_1, lock_2, i])
# t.start()



### 解决死锁问题 RLock:可重入,可以重复acquire,获得几次,就要释放几次
from threading import Thread, Lock,RLock
import time
import random


def eat1(lock_1, lock_2, name):
lock_1.acquire()
print('%s:拿到了筷子' % name)
time.sleep(random.random())
lock_2.acquire()
print('%s:拿到了面条' % name)
print('开始吃面')
time.sleep(random.random())
lock_2.release()
print('%s放下了面条' % name)
lock_1.release()
print('%s放下了筷子' % name)


def eat2(lock_1, lock_2, name):
lock_2.acquire()
print('%s:拿到了面条' % name)
time.sleep(random.random())
lock_1.acquire()
print('%s:拿到了筷子' % name)
print('开始吃面')
time.sleep(random.random())
lock_1.release()
print('%s放下了筷子' % name)
lock_2.release()
print('%s放下了面条' % name)


if __name__ == '__main__':
lock_1 = RLock()
lock_2 = lock_1

# lock_1 = Lock()
# lock_2 = lock_1
for i in ['张三', '李四', '王五']:
t = Thread(target=eat1, args=[lock_1, lock_2, i])
t.start()
for i in ['赵6', '往7', '傻逼']:
t = Thread(target=eat2, args=[lock_1, lock_2, i])
t.start()

线程队列

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
1 线程Queue,解决线程间数据共享的问题
2 线程间数据共享可以使用共享变量(可能会存在并发安全的问题)

from threading import Thread
from queue import Queue,LifoQueue,PriorityQueue # 线程queue
# Queue:先进先出
#LifoQueue:后进先出
#PriorityQueue:优先级队列




# import time
# def task(queue):
# time.sleep(3)
# queue.put('lqz')
#
#
#
# if __name__ == '__main__':
# queue=Queue()
# t=Thread(target=task,args=[queue,])
# t.start()
#
#
# res=queue.get()
# print(res)


# Queue:先进先出
# LifoQueue:后进先出
# PriorityQueue:优先级队列


if __name__ == '__main__':
# quque1=Queue()
#
# quque1.put(1)
# quque1.put(2)
# print(quque1.get())



# quque2=LifoQueue()
# quque2.put(1)
# quque2.put(2)
# print(quque2.get())


quque3=PriorityQueue()
quque3.put((1,'lqz'))
quque3.put((100,'egon'))
# 数字越小,优先级越高
print(quque3.get())

进程池,线程池(重点)

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
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
# 线程池,进程池都在这个模块下concurrent.futures


import time
import os

import random
from concurrent.futures import ProcessPoolExecutor,ThreadPoolExecutor

# def task(n):
# print(os.getpid(), '开始执行了')
# time.sleep(random.random())
# return n * n
#
#
#
# def callback(result):
# print(result)
# if __name__ == '__main__':
# # 开进程池去执行
# # ProcessPoolExecutor实例化得到一个对象
# pool_p=ProcessPoolExecutor(3)
# # ll=[]
# # for i in range(10):
# # # 把任务提交到进程池执行
# # f=pool_p.submit(task,n=i)
# # ll.append(f)
# #
# # # 等待所有子进程执行完成,主进程在执行
# # pool_p.shutdown()
# #
# # for l in ll:
# # res=l.result() # 取到当前进程执行任务的返回值
# # print(res)
# #
# # print('我是主进程')
#
# # map取代for循环的,第一个参数是要执行的任务,第二个参数,是一个可迭代对象,迭代一次的结果,会传给任务
#
#
#
# # for i in range(10):
# # f=pool_p.submit(task,n=i)
# # 等同于上面
# # pool_p.map(task,range(10))
# # pool_p.shutdown()
# # print('主进程')
#
#
# ## 回调
# for i in range(10):
# pool_p.submit(task,n=i).add_done_callback()


def task(n):
print(os.getpid(), '开始执行了')
time.sleep(1)
return n * n


def callback(result):
print(result.result())


if __name__ == '__main__':
pool_p = ProcessPoolExecutor(3)
for i in range(10):
pool_p.submit(task, n=i).add_done_callback(callback)

'''
submit
shutdown
result
map(了解)
add_done_callback:回调
'''

协程介绍

协程是:程序级别的切换,单线程下实现并发
python的线程属于内核级别的,即由操作系统控制调度(如单线程遇到io或执行时间过长就会被迫交出cpu执行权限,切换其他线程运行)
单线程内开启协程,一旦遇到io,就会从应用程序级别(而非操作系统)控制切换,以此来提升效率(!!!非io操作的切换与效率无关)

greenlet模块(初级模块,实现了保存状态加切换)

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
# 安装第三方模块:在命令行下
# pip3 install greenlet
# pip3 uninstall greenlet 卸载第三方模块
# pip3 list # 列出当前解释器环境下安装的第三方模块


from greenlet import greenlet



def eat(name):
print(name,'在吃了一口')
g2.switch(name)

print(name,'在吃了第二口')
g2.switch()


def play(name):
print(name, '玩了一下')
g1.switch()

print(name, '玩了第二下')


g1=greenlet(eat)
g2=greenlet(play)

g1.switch('egon')


### 写两个task,一个计算从1+1w,另一个计算从1乘以到1w,统计一下,切换执行时间快还是不切换快

gevent模块

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
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
## gevent模块,协程模块,遇到io可以自动切换


# pip3 install gevent

import gevent
import time

# def eat(name):
# print(name, '在吃了一口')
# # 遇到了io
# gevent.sleep(2)
#
# print(name, '在吃了第二口')
#
#
# def play(name):
# print(name, '玩了一下')
# # 遇到了io,是gevent的io
# gevent.sleep(3)
# print(name, '玩了第二下')
#
#
# res1 = gevent.spawn(eat, 'egon')
# res2 = gevent.spawn(play, 'egon')
#
#
# ctime=time.time()
# # res1.join()
# # res2.join() # 等地任务执行完成再执行下面那句
#
# gevent.joinall([res1,res2]) # 相当于上面那两句
# print('主线程')
# print(time.time()-ctime)




###使用原来的time的io,不会切,并且变成了串行
# def eat(name):
# print(name, '在吃了一口')
# time.sleep(2)
#
# print(name, '在吃了第二口')
#
#
# def play(name):
# print(name, '玩了一下')
# time.sleep(3)
# print(name, '玩了第二下')
#
#
# res1 = gevent.spawn(eat, 'egon')
# res2 = gevent.spawn(play, 'egon')
#
#
# ctime=time.time()
#
# gevent.joinall([res1,res2]) # 相当于上面那两句
# print('主线程')
# print(time.time()-ctime)


### time的io也要切换

# 猴子补丁:把原来的io全都替换成gevent的io
from gevent import monkey;monkey.patch_all()



def eat(name):
print(name, '在吃了一口')
time.sleep(2)

print(name, '在吃了第二口')


def play(name):
print(name, '玩了一下')
time.sleep(3)
print(name, '玩了第二下')


res1 = gevent.spawn(eat, 'egon')
res2 = gevent.spawn(play, 'egon')


ctime=time.time()

gevent.joinall([res1,res2]) # 相当于上面那两句
print('主线程')
print(time.time()-ctime)

asyncio

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
# 内置模块   python 3.4 推出这个模块,python作者主导的
import asyncio
import time
import threading
# 这个函数是协程函数
async def task():
res=threading.current_thread().getName()
print(res)
print('xxx')
await asyncio.sleep(2)
print('协程执行完成')

async def task2():
res=threading.current_thread().getName()
print(res)
print('2222')
await asyncio.sleep(3)
print('222协程执行完成')



ctime=time.time()
loop=asyncio.get_event_loop()



tasks=[task(),task2()]



loop.run_until_complete(asyncio.wait(tasks))
loop.close()
print(time.time()-ctime)

进程Queue介绍

1
2
3
4
5
6
7
8
1 进程间数据隔离,两个进程进行通信,借助于Queue

2 进程间通信:IPC
-借助于Queue实现进程间通信
-借助于文件

-借助于数据库
-借助于消息队列:rabbitmq,kafka....

基本使用

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

from multiprocessing import Process,Queue


if __name__ == '__main__':
# maxsize表示Queue的大小是多少,能放多少东西
queue=Queue(3)
## 放数据
queue.put('zhangsan')
queue.put('liss')
queue.put('wwwww')

queue.put('wwwww',timeout=0.1)

# queue.put_nowait('sdafsd')
#
# res=queue.get()
# print(res)
# res=queue.get()
# print(res)
res=queue.get()
# print(res)
# # 卡住
# # res=queue.get()
# res=queue.get_nowait()
# print(res)



'''
# 实例化得到一个对象,数字表示queue的大小(默认不传参,可以当成无限大,但其实有最大值)
queue=Queue(3)
# 放值
# block:是否阻塞
# timeout:等待的时间
queue.put()
# 取值
# block:是否阻塞
# timeout:等待的时间
queue.get()

# 不等待,如果满了,就报错
queue.put_nowait()

# 去取值,如果没有值,直接报错
res=queue.get_nowait()

# 查看这个queue是否满
queue.full()

# 查看queue是否是空的
queue.empty()

# 查看queue中有几个值
queue.qsize()
'''

通过Queue实现进程间通信

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22

from multiprocessing import Process,Queue


import os
import time

def task(queue):
print('我这个进程%s开始放数据了'%os.getpid())
time.sleep(10)
queue.put('lqz is handsome')
print('%s我放完了' % os.getpid())


if __name__ == '__main__':
#不写数字,表示可以任意长度
queue=Queue()
p=Process(target=task,args=[queue,])
p.start()

res=queue.get() #会卡在这
print(res)

批量生产数据放入Queue再批量取出

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
from multiprocessing import Process,Queue
import os

def get_task(queue):
res=queue.get()
print('%s这个进程取了数据:%s'%(os.getpid(),res))


def put_task(queue):
queue.put('%s:放了数据'%os.getpid())

if __name__ == '__main__':
queue=Queue(1)
p1=Process(target=put_task,args=[queue])
p2=Process(target=put_task,args=[queue])
p1.start()
p2.start()


p3=Process(target=get_task,args=[queue])
p4=Process(target=get_task,args=[queue])
p3.start()
p4.start()

生产者消费者模型(重点)

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
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
from multiprocessing import Process, Queue
# import os
#
# import time
# import random
# def producer(queue):
# # 生产的东西,放到Queue中
# for i in range(10):
# data = '%s这个厨师,整了第%s个包子' % (os.getpid(), i)
# print(data)
# # 模拟一下延迟
# time.sleep(random.randint(1,3))
# queue.put('第%s个包子'%i)
#
#
# def consumer(queue):
# # 消费者从queue中取数据,消费(吃包子)
# while True:
#
# res=queue.get()
# # 模拟一下延迟
# time.sleep(random.randint(1, 3))
# print('%s这个消费者,吃了%s'%(os.getpid(),res))
#
#
#
# if __name__ == '__main__':
# queue=Queue(3)
# p=Process(target=producer,args=[queue,])
# p.start()
#
# p1=Process(target=consumer,args=[queue,])
# p1.start()


###### 改良(生产者以及不生产东西了,但是消费者还在等着拿)
# import os
#
# import time
# import random
# def producer(queue):
# # 生产的东西,放到Queue中
# for i in range(10):
# data = '%s这个厨师,整了第%s个包子' % (os.getpid(), i)
# print(data)
# # 模拟一下延迟
# time.sleep(random.randint(1,3))
# queue.put('第%s个包子'%i)
# # 生产完了,在queue中放一个None
# queue.put(None)
#
#
# def consumer(queue):
# # 消费者从queue中取数据,消费(吃包子)
# while True:
#
# res=queue.get()
# if not res:break # 如果去到空,说明打烊了(生产者不生产了),退出
# # 模拟一下延迟
# time.sleep(random.randint(1, 3))
# print('%s这个消费者,吃了%s'%(os.getpid(),res))
#
#
#
# if __name__ == '__main__':
# queue=Queue(3)
# p=Process(target=producer,args=[queue,])
# p.start()
#
# p1=Process(target=consumer,args=[queue,])
# p1.start()


#### 把put none 放在主进程中执行
import os

# import time
# import random
# def producer(queue):
# # 生产的东西,放到Queue中
# for i in range(10):
# data = '%s这个厨师,整了第%s个包子' % (os.getpid(), i)
# print(data)
# # 模拟一下延迟
# time.sleep(random.randint(1,3))
# queue.put('第%s个包子'%i)
#
#
#
# def consumer(queue):
# # 消费者从queue中取数据,消费(吃包子)
# while True:
#
# res=queue.get()
# if not res:break # 如果去到空,说明打烊了(生产者不生产了),退出
# # 模拟一下延迟
# time.sleep(random.randint(1, 3))
# print('%s这个消费者,吃了%s'%(os.getpid(),res))
#
#
#
# if __name__ == '__main__':
# queue=Queue(3)
# p=Process(target=producer,args=[queue,])
# p.start()
#
# p1=Process(target=consumer,args=[queue,])
# p1.start()
#
# # 如果把put None放在这,会有问题
# # 主进程会先执行这句话,消费进程读到None,直接结束,生产者进程没有结束,于是生产一直在生产,消费已经不消费了
# # 直到Queue满了,就一直卡在这了
# # queue.put(None)
#
# ### 现在就要放在这,你把问题解决
# p.join()
# queue.put(None)

多个生产者多个消费者的生产者消费者模型

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
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
# 多个生产者在生产,多个消费者在消费
# import time
# import random
# def producer(queue,food):
# # 生产的东西,放到Queue中
# for i in range(10):
# data = '%s这个厨师,做了第%s个%s' % (os.getpid(), i,food)
# print(data)
# # 模拟一下延迟
# time.sleep(random.randint(1,3))
# queue.put('第%s个%s'%(i,food))
#
#
# def consumer(queue):
# # 消费者从queue中取数据,消费(吃包子)
# while True:
# res=queue.get()
# if not res:break # 如果去到空,说明打烊了(生产者不生产了),退出
# # 模拟一下延迟
# time.sleep(random.randint(1, 3))
# print('%s这个消费者,吃了%s'%(os.getpid(),res))
#
#
#
# if __name__ == '__main__':
# queue=Queue(3)
# ##起了三个生产者
# p1=Process(target=producer,args=[queue,'包子'])
# p2=Process(target=producer,args=[queue,'骨头'])
# p3=Process(target=producer,args=[queue,'泔水'])
# p1.start()
# p2.start()
# p3.start()
#
#
#
# # 起了两个消费者
# c1=Process(target=consumer,args=[queue,])
# c2=Process(target=consumer,args=[queue,])
# c1.start()
# c2.start()
#
# ##等三个生产者都生产完,放三个None
# p1.join()
# p2.join()
# p3.join()
# queue.put(None)
# queue.put(None)
# queue.put(None)

##如果消费者多,比生产者多出来的消费者不会停

import time
import random


def producer(queue, food,name):
# 生产的东西,放到Queue中
for i in range(10):
data = '%s:这个厨师,做了第%s个%s' % (name, i, food)
print(data)
# 模拟一下延迟
time.sleep(random.randint(1, 3))
queue.put('第%s个%s' % (i, food))


def consumer(queue,name):
# 消费者从queue中取数据,消费(吃包子)
while True:
try:
res = queue.get(timeout=20)
# 模拟一下延迟
time.sleep(random.randint(1, 3))
print('%s这个消费者,吃了%s' % (name, res))
except Exception as e:
print(e)
break


if __name__ == '__main__':
queue = Queue(3)
# 起了三个生产者
p1 = Process(target=producer, args=[queue, '包子','egon'])
p2 = Process(target=producer, args=[queue, '骨头','lqz'])
p3 = Process(target=producer, args=[queue, '泔水','jsason'])
p1.start()
p2.start()
p3.start()

# 起了两个消费者
c1 = Process(target=consumer, args=[queue, '孟良'])
c2 = Process(target=consumer, args=[queue,'池劲涛' ])
c3 = Process(target=consumer, args=[queue,'池劲涛' ])
c4 = Process(target=consumer, args=[queue,'池劲涛' ])
c1.start()
c2.start()
c3.start()
c4.start()

进程间数据共享(了解)

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

from multiprocessing import Process,Manager,Lock

# 魔法方法:类内以__开头__结尾的方法,都叫魔法方法,某种情况下会触发它的执行
'''
__init__ :类()触发
__new__:
__getattr__
__setattr__
__getitem__
__setitem__

'''

# def task(dic,lock):
# # lock.acquire()
# # dic['count']-=1
# # lock.release()
# with lock:
# dic['count'] -= 1
#
# if __name__ == '__main__':
# lock = Lock()
# with Manager() as m:
# # 如果直接定义dict,这个dict在多个进程中其实是多份,进程如果改,只改了自己的
# #如果定义的是m.dict({'count': 100}),多个进程之间就可以共享这个数据
# dic = m.dict({'count': 100})
#
# p_l = []
# for i in range(100):
# p = Process(target=task, args=(dic, lock))
# p_l.append(p)
# p.start()
# for p in p_l:
# p.join()





def task(dic,lock):
with lock:
dic['count'] -= 1

if __name__ == '__main__':
lock = Lock()
dic={'count':100}
p_l = []
for i in range(100):
p = Process(target=task, args=(dic, lock))
p_l.append(p)
p.start()
for p in p_l:
p.join()



print(dic)

线程概念

如果把我们上课的过程看成一个进程的话,那么我们要做的是耳朵听老师讲课,手上还要记笔记,脑子还要思考问题,这样才能高效的完成听课的任务。

而如果只提供进程这个机制的话,上面这三件事将不能同时执行,同一时间只能做一件事,听的时候就不能记笔记,也不能用脑子思考,这是其一;

如果老师在黑板上写演算过程,我们开始记笔记,而老师突然有一步推不下去了,阻塞住了,他在那边思考着,而我们呢,也不能干其他事,即使你想趁此时思考一下刚才没听懂的一个问题都不行,这是其二

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
# 进程是资源分配的最小单位,线程是CPU调度的最小单位。每一个进程中至少有一个线程。


from threading import Thread
from queue import Queue
import os
import time


def task():
time.sleep(3)
print('我是子线程执行的')
print(os.getpid())


if __name__ == '__main__':
# 启动线程

ctime = time.time()
t = Thread(target=task)
t.start()
# task()
time.sleep(3)
print(os.getpid())
print(time.time() - ctime)

总结

1
2
3
4
5
6
7
8
9
10
11
12
13
14
1 Queue:进程间通信
-实例化得到一个对象
-对象.put()
-对象.get()

2 生产者消费者模型

3 通过共享变量来共享数据(进程间数据是隔离的)
-Manager实现多个进程操作同一个变量
-加锁

4 线程,每个进程下最少有一个线程,cup调度的最小单位

5 python如何开启线程

进程调度算法(了解)

1
2
3
4
# 先来先服务调度算法
# 短作业优先调度算法
# 时间片轮转法
# 多级反馈队列

同步异步,阻塞非阻塞(了解)

1
2
3
4
5
6
7
8
9
1 同步调用:提交了以后,一直等待结果返回
2 异步调用:提交了以后,返回一个标志,等执行完成后,有消息通知
3 同步,异步:指的是消息通知机制

4 阻塞,非阻塞:程序在等待调用结果的状态
5 同步阻塞:打电话要买书,如果电话没挂,我也一直在等待,
6 同步非阻塞:打电话买书,电话没挂,我一边干别的事,一边听一下电话
7 异步阻塞:打电话买书,电话先挂掉,过一会老板会回回来(回调),老板给回电话的过程一直在等待
8 异步非阻塞:打电话买书,电话先挂掉,过一会老板会回回来(回调),老板给回电话的过程中,在干别的事

Process类的参数(重点)

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
from multiprocessing import Process


def task(name,age):
print(name)
print(age)


if __name__ == '__main__':
# p=Process(target=task,args=['lqz',18])
# p=Process(target=task,kwargs={'age':19,'name':'lqz'},name='process01')
p=Process(target=task,kwargs={'age':19,'name':'lqz'})
p2=Process(target=task,kwargs={'age':19,'name':'lqz'})
p.start()
p2.start()

print(p.name)
print(p2.name)
# target=None, 你要执行的任务,函数
# name=None, 进程名
# args=(), 以位置参数给任务(函数)传递参数
# kwargs={} 以关键字的形式给任务(函数)传递参数

Process类的方法,属性(重点)

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

from multiprocessing import Process
import time

def task(name,age):
time.sleep(10)
print(name)
print(age)


if __name__ == '__main__':
p=Process(target=task,kwargs={'age':19,'name':'lqz'})


p.start() #启动进程,必须调用start
# p.run() # 实际进程在执行的时候,执行的是run方法,但是调用run不会开进程,后面我们另一种开启进程的方案使用到它

# p.join() # 等待子进程执行完成


print(p.is_alive()) #True

p.terminate() # 杀死p这个进程,通知操作系统去杀死该进程
time.sleep(0.1)

print(p.is_alive()) #可能还是True
print('ssss')

print(p.is_alive()) #就是False

print(p)


掌握↓↓↓
'''
p.start() #启动进程,必须调用start
p.run() # 实际进程在执行的时候,执行的是run方法,但是调用run不会开进程,后面我们另一种开启进程的方案使用到它
p.join() # 等待子进程执行完成
p.terminate() # 杀死p这个进程,通知操作系统去杀死该进程,并不是立即结束
p.is_alive() #进程是否还存活
'''

属性

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
from multiprocessing import Process
import time

def task(name,age):
time.sleep(10)
print(name)
print(age)


if __name__ == '__main__':
p=Process(target=task,kwargs={'age':19,'name':'lqz'})
# p.start()
# print(p.name) # 进程名字
# p.daemon=True #主进程结束,子进程也结束,必须在start之前调用
p.start()
print(p.pid) # 进程id号
time.sleep(10)

掌握↓↓↓
'''
print(p.name) # 进程名字
print(p.pid) # 进程id号
p.daemon=True #主进程结束,子进程也结束,必须在start之前调用
'''

主进程和子进程的进程号

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
from multiprocessing import Process
import time
import os
def task(name,age):
# 如果在任务中取出进程id号,需要使用os模块
print('当前进程(子进程)id号是:',os.getpid()) #当前进程id号
print('当前进程父进程的id号是:',os.getppid()) # 当前进程父进程的id号
time.sleep(10)

print(name)
print(age)


if __name__ == '__main__':
p=Process(target=task,kwargs={'age':19,'name':'lqz'})
p.start()
print('p这个进程的id号是:',p.pid) # 进程id号
print('当前进程id(主进程)号是:', os.getpid()) # 当前进程id号
print('当前进程父进程(pycharm)的id号是:', os.getppid()) # 当前进程父进程的id号
time.sleep(10)

掌握↓↓↓
'''
如果有p对象,就是用p.pid获取进程id号
如果没有p对象,就是用os模块的
os.getpid() #当前进程id号
os.getppid() #父进程id号
'''

同时开启多个进程

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

from multiprocessing import Process
import time
import os
# def task(name,age):
# time.sleep(10)
# print(name)
# print(age)
# def task1():
# time.sleep(2)
# print("我是task1")
#
#
# if __name__ == '__main__':
# p=Process(target=task,kwargs={'age':19,'name':'lqz'})
# p.start()
#
# p1=Process(target=task1)
# p1.start()
#
# print('主进程')

import time

def task1(i):
time.sleep(2)
print("我是task1",i)


if __name__ == '__main__':
ctime=time.time()
ll=[]
for i in range(5):
p1=Process(target=task1,args=[i,])
p1.start()
p1.join() #等待子进程执行完成
# ll.append(p1)

# for p in ll:
# p.join()
[p.join()for p in ll]
print('主进程')
print(time.time()-ctime)


掌握↓↓↓
'''
开启多个进程
如果想等待多个进程同时执行完,先把进程开启完成,再统一join
'''

开启进程的另一种方案

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
### 通过继承Process类的方式来实现,重写run方法,run方法就是你要执行的任务,实例化得到对象,调用start方法开启进程

class MyProcess(Process):
def __init__(self,name1,age):
self.name1=name1
self.age=age
# 这个必须写
super().__init__()

def run(self) :
time.sleep(2)
print(self.name)
print(self.name1)
print(self.age)

if __name__ == '__main__':
p=MyProcess('lqz',19)
p.start() #调用p.start(),不要调用run
print('主进程')

进程之间数据隔离

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
from multiprocessing import Process
import time
import os


def task():
print('我是task')
global n
n=100
print('子进程的:',n)



if __name__ == '__main__':
# 在主进程中定义了一个n=10
n=10

###如果这样写,n会被改掉
# task()
# print(n)

##如果这样写,n不会被改掉
p=Process(target=task)
p.start()
print('主进程的:',n)

高并发的TCP服务端

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
import socket
from multiprocessing import Process




def talk(sock,addr):
print('客户端连接成功',addr)
while True:
try:
data = sock.recv(1024)
if len(data) == 0:
break
print('从客户端收到了:', data)
sock.send(data.upper())
except Exception as e:
print(e)
break
sock.close()
if __name__ == '__main__':
server = socket.socket()
server.bind(('127.0.0.1', 81))
server.listen(5)
while True:
print('等待客户端连接')
sock, addr = server.accept()
p=Process(target=talk,args=[sock,addr])
p.start()
server.close()
from multiprocessing import Process
import time
import os


def task():
print('我是task')
global n
n=100
print('子进程的:',n)



if __name__ == '__main__':
# 在主进程中定义了一个n=10
n=10

###如果这样写,n会被改掉
# task()
# print(n)

##如果这样写,n不会被改掉
p=Process(target=task)
p.start()
print('主进程的:',n)

进程同步(进程锁)(次重点)

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
import time
## 不加锁

import json
from multiprocessing import Process,Lock


## 查询票余额
def check(i):
with open('ticket', 'rt', encoding='utf-8') as f:
res = json.load(f)
print('%s:在查询票,票还剩%s张' % (i, res['count']))
if res['count'] > 0:
return True


## 抢票,票的余额减一

def buy(i):
with open('ticket', 'rt', encoding='utf-8') as f:
res = json.load(f)
time.sleep(1) # 模拟查票延迟
if res['count'] > 0:
print('%s现在要买了,票数还是大于0'%i)
res['count'] -= 1
time.sleep(2) # 模拟买票延迟
with open('ticket', 'wt', encoding='utf-8') as f1:
json.dump(res, f1)
print('%s这个人购票成功' % i)
else:
print('%s这个人购票失败,票不够了,买不了了' % i)


def task(i,lock):
res = check(i)
if res:
lock.acquire()
buy(i)
lock.release()


##模拟10个人买票

if __name__ == '__main__':
lock=Lock()
for i in range(10):
p = Process(target=task, args=[i,lock ])
p.start()

总结

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
1 进程调度算法
-先来先服务
-短作业有限
-时间片轮转
-多级反馈队列

2 同步,异步;阻塞,非阻塞
-同步和异步:指的是回调方式,如果有回调,就是异步,如果同步等待就是同步
-阻塞,非阻塞:指等待消息结果时的状态,如果在等的过程中,干了别的事,就是非阻塞,如果一直等就是阻塞
3 进程类Process类,实例化的时候的参数:
-target:要执行的任务
-args:以位置形式给任务传值
-kwargs:以关键字给任务传值
-name:进程名字(如果不写,会有个默认名字)

4 进程对象的属性和方法
-属性
-name
-pid:进程id号,如果没有这个对象,需要借助os.getpid(),os.getppid()
-daemon:守护进程,如果设置为True,主进程挂掉,这个进程也会挂掉,需要在p.start之前执行
-方法:
-is_alive() 进程是否存活
-terminate() 停止该进程
-join() 等待子进程执行结束,主进程再继续执行
-start() 子进程要执行,必须调用它
-run() 真正的任务

5 开启进程的另一种方式(类的继承)
写一个类,继承Process类
重写run方法,就是你的任务
实例化得到我们写的类,调用这个类对象的.start()

6 进程间数据是隔离的

7 进程锁
-多个进程操作同一个数据(文件中的数据,而不是内存中的数据:)
-lock=Lock()
-加锁:lock.acquire()
-解锁:lock.release()