Python面试题

Python面试题(持续更新)

1 Django请求的生命周期
url请求->执行遵循wsgi协议的模块(socket服务端)->中间件(路由匹配)->视图函数(业务处理,模板渲染)->中间件->wsgi返回

--v2版本回答
浏览器发起请求-->wsgi(socket服务端,接受用户请求,并初次封装)-->django(再次封装,这次封装成request对象)-->中间件(对请求的数据进行加工处理)
-->url路由-->视图函数(业务逻辑处理)-->中间件(对响应进行加工)-->wsgi-->浏览器

2 什么是wsgi
web服务网关接口
实现该协议的模块
- wsgiref # django
- werkzeug # flask, odoo

v2 版本回答:
a web服务网关接口, wsgi是一个协议
b 实现该协议的模块
- wsgiref
- werkzeug

c 实现其协议的模块本质上就是一个socket服务端, 用于接受用户请求, 并处理
d 一般web框架都是基于wsgi实现的, 这样实现关注点分离

v3 网上找到的
https://segmentfault.com/a/1190000011365430

v4 去看自己整理的

3 视图
- FBV url对应一个函数,通过函数编写业务逻辑
- CBV url对应一个类,通过类编写业务逻辑
注意!!!本质上是一样的!!!

4 django rest framework
实现了restful规范的django的接口框架

5 restful规范
1 根据method不同,进行不同操作(HTTP动词)

常用的HTTP动词有下面五个(括号里是对应的SQL命令)。

GET(SELECT): 从服务器取出资源(一项或多项)。
POST(CREATE):在服务器新建一个资源。
PUT(UPDATE): 在服务器更新资源(客户端提供改变后的完整资源)。
PATCH(UPDATE):在服务器更新资源(客户端提供改变的属性)。
DELETE(DELETE):从服务器删除资源。

还有两个不常用的HTTP动词。
HEAD:获取资源的元数据。
OPTIONS:获取信息,关于资源的哪些属性是客户端可以改变的。

2 面向资源编程(路径)
路径又称"终点"(endpoint),表示API的具体网址。

在RESTful架构中,每个网址代表一种资源(resource),所以网址中不能有动词,只能有名词,而且所用的名词往往与数据库的表格名对应。

3 在url上体现版本(版本)
应该将API的版本号放入URL。

4 在url上体现出是API接口(域名)
应该尽量将API部署在专用域名之下。
https://api.example.com

如果确定API很简单,不会有进一步扩展,可以考虑放在主域名下。
https://example.org/api/

5 https
API与用户的通信协议,总是使用HTTPs协议。

6 响应时设置状态码(状态码)
服务器向用户返回的状态码和提示信息,常见的有以下一些(方括号中是该状态码对应的HTTP动词)。
200 OK - [GET]:服务器成功返回用户请求的数据,该操作是幂等的(Idempotent)。
201 CREATED - [POST/PUT/PATCH]:用户新建或修改数据成功。
202 Accepted - [*]:表示一个请求已经进入后台排队(异步任务)
204 NO CONTENT - [DELETE]:用户删除数据成功。
400 INVALID REQUEST - [POST/PUT/PATCH]:用户发出的请求有错误,服务器没有进行新建或修改数据的操作,该操作是幂等的。
401 Unauthorized - [*]:表示用户没有权限(令牌、用户名、密码错误)。
403 Forbidden - [*] 表示用户得到授权(与401错误相对),但是访问是被禁止的。
404 NOT FOUND - [*]:用户发出的请求针对的是不存在的记录,服务器没有进行操作,该操作是幂等的。
406 Not Acceptable - [GET]:用户请求的格式不可得(比如用户请求JSON格式,但是只有XML格式)。
410 Gone -[GET]:用户请求的资源被永久删除,且不会再得到的。
422 Unprocesable entity - [POST/PUT/PATCH] 当创建一个对象时,发生一个验证错误。
500 INTERNAL SERVER ERROR - [*]:服务器发生错误,用户将无法判断发出的请求是否成功。

7 条件(过滤信息)
如果记录数量很多,服务器不可能都将它们返回给用户。API应该提供参数,过滤返回结果。
下面是一些常见的参数。
?limit=10:指定返回记录的数量
?offset=10:指定返回记录的开始位置。
?page=2&per_page=100:指定第几页,以及每页的记录数。
?sortby=name&order=asc:指定返回结果按照哪个属性排序,以及排序顺序。
?animal_type_id=1:指定筛选条件

8 返回值(返回结果)

针对不同操作,服务器向用户返回的结果应该符合以下规范。

GET /collection:返回资源对象的列表(数组)
GET /collection/resource:返回单个资源对象
POST /collection:返回新生成的资源对象
PUT /collection/resource:返回完整的资源对象
PATCH /collection/resource:返回完整的资源对象
DELETE /collection/resource:返回一个空文档

9 返回错误信息
如果状态码是4xx,就应该向用户返回出错信息。一般来说,返回的信息中将error作为键名,出错信息作为键值即可。
{
error: "Invalid API key"
}
-------------
{
'code':10001,
'error':'错误信息'
}

10 Hypermedia APi
RESTful API最好做到Hypermedia,即返回结果中提供链接,连向其他API方法,使得用户不查文档,也知道下一步应该做什么。

比如,当用户向api.example.com的根目录发出请求,会得到这样一个文档。

{"link": {
"rel": "collection https://www.example.com/zoos",
"href": "https://api.example.com/zoos",
"title": "List of zoos",
"type": "application/vnd.yourformat+json"
}}

--v2版本回答
restful是一个规范,规定API如何编写,通过它可以让我们api更加简洁可维护

如,最直观的:
1, method:
- get
- post
- put
- delete

原来都是在url中设置的

除此之外:
2- api
3- 版本
4- 资源
5- https
6- 条件
7- 状态码
8- 返回值
9- 错误信息
10- hypermedia link


6 Django rest framework框架(10个组件)
1 权限
2 认证
3 频率
4 序列化
5 路由
6 视图
7 分页
8 解析器
9 渲染器
10 版本

7 你写的drf接口类都继承过哪些类?
View APIView generics.GenericAPIVIew ViewSetMixin GenericViewSet ModelViewSet

混乱的继承关系!!!

1, ModelViewSet(mixins.CreateModelMixin,
mixins.RetrieveModelMixin,
mixins.UpdateModelMixin,
mixins.DestroyModelMixin,
mixins.ListModelMixin,
GenericViewSet):

2, GenericViewSet(ViewSetMixin, generics.GenericAPIView):

3,
ViewSetMixin(object):
GenericAPIView(views.APIView):

4, APIView(View):

补充一下视图三部曲!!!
mixins的玩法
from rest_framework import mixins
from rest_framework import generics

class AuthorView(mixins.CreateModelMixin, mixins.ListModelMixin, generics.GenericAPIView):
class AuthorDetailView(mixins.RetrieveModelMixin, mixins.UpdateModelMixin, mixins.DestroyModelMixin, generics.GenericAPIView):

generic的玩法
from rest_framework import mixins
from rest_framework import generics

class AuthorView(generics.ListCreateAPIView):
class AuthorDetailView(generics.RetrieveUpdateDestroyAPIView):

终极玩法
from rest_framework import viewsets

class AuthorViewSet(viewsets.ModelViewSet):

8 为什么会有跨域?
浏览器具有同源策略,所有才会出现跨域

如何绕过同源策略
- jsonp 其实是利用动态的src实现的,同源策略会阻止ajax请求,不阻止具有src属性的标签
缺点:只能发get请求
- cors 设置响应头

在django中的解决方案:
在中间件里给响应增加响应头

--v2版本回答
jsonp:动态创建一个script标签 (先定义一个函数, 服务器给我返回, 客户端调用这个函数)

cors:设置响应头
简单请求
复杂请求:请求是复杂请求时,会先发一个options请求预检
应用:本地开发前后端分离的时候使用到过

(nginx可以解决跨域)

9 你理解的http协议
- 建立在tcp之上
- 一次请求,一次响应然后断开(无状态,短连接)
- 请求和响应

host:www.luffy.com\r\n\r\ncontent-type:application/json\r\n\r\n请求体

--v2版本回答

1 http协议就是一个传输数据的格式
2 我原来学django框架,从socket服务端开始学起,自己创造了一个socket客户端来充当网络浏览器,当socket服务
3 更清楚的明白到底http协议是什么
- 请求头 请求体
- 响应头 响应体

4 一次请求响应后,断开连接

10 django中间件是什么?
本质上是一个类,可以在视图函数的前后自定义行为

11 使用中间件做过什么?
- 内置 csrf session
- 自定义 权限 认证 cors

登录认证: 第一次登录时,往session写入token,下次来的时候,校验token (好处:不再需要在每个函数中添加装饰器)
权限:当用户登录时,获取当前用户所有权限放入session中,然后用户下次再次访问其他页面时,获取当前url并在session中进行匹配.
如果没有匹配成功,返回无权限访问.

12 中间件有多少个方法?
5个
分别是
process_request(self,request) # 从wsgi拿到请求之后执行
process_view(self, request, view_func, view_args, view_kwargs) # 在urls.py中找到对应关系之后 在执行真正的视图函数之前
process_template_response(self,request,response) # 视图函数执行完,在执行视图函数返回的响应对象的render方法之前
process_exception(self, request, exception) # 视图函数中抛出异常的时候才执行
process_response(self, request, response) # 请求有响应的时候

13 如何实现的访问频率控制?
{
192.168.1.2:[234234234,234234228,234234210],
192.168.1.3:[234234234,234234228,234234210],
}

1 以用户的唯一标示为键,再以用户的访问记录的列表为值;
2 在用户下一次访问时判断列表的最后一个是否小于(当前时间-频率时间);如果小于,则剔除;
3 然后再判断列表的长度是否超过频率数,如超过,则return false,else true

匿名用户:无法控制,因为用户可以用代理IP

登录用户:用用户名做标示,(如果有很多账号,也无法限制)

14 序列化-->自定义字段
- source(o2o/fk/choice)
title = serializers.CharField(source='course.title')
course_img = serializers.CharField(source='course.course_img')
level = serializers.CharField(source='course.get_level_display')

- 自定义方法(m2m)

def get_recommends(self, obj):
"""钩子函数"""
# 获取推荐的所有课程
queryset = obj.recommend_courses.all()
return [{'id': row.id, 'title': row.title} for row in queryset]

再将自定的字段写到meta类的属性fields里
class Meta:
model = models.CourseDetail
# fields = '__all__'
fields = ['course', 'title', 'course_img', 'level', 'slogan', 'why', 'recommends', 'chapter']

--其他补充
ser = xx(queryset,many=True) # 实例化的是listserializer对象
ser = xx(obj,many=False) # 实例化的xx(本身)这个对象\

(到处都在用列表生成式)-->认证频率权限


15 常见的请求头
- Accept 客户机通过这个头,告诉服务器,它支持哪些数据类型
- User-Agent 说明客户机操作系统信息,以及浏览器信息 (区分不同的(请求)设备)
- Referer 客户机通过这个头,告诉服务器,客户机是从哪个页面来的 (可以做图片防盗链)
- Host 客户机通过这个头,告诉服务器,想访问服务器哪台主机
- Cookie 客户机通过这个头,可以带点数据给服务器

- Content-Type 客户机通过这个头告诉服务器,回送数据的类型

Accept-Charset::客户机通过这个头,告诉服务器,它支持的编码
Accept-Encoding: 客户机通过这个头,告诉服务器,支持哪种数据压缩格式
Accept-Language: 客户机采用的是哪个语言
If-Modified-Since:客户机通过这个头,告诉服务器,数据缓存的时间
Connection:表示是否需要持久连接。

16 常见的响应头
- Content-Type 服务器通过这个头告诉浏览器,回送数据的类型


Location:服务器通过这个头告诉浏览器去访问哪个页面,这个头通常配合302状态码使用
server: 服务器通过这个头,告诉浏览器服务器类型
Content-Encoding: 服务器通过这个头告诉浏览器,回送的数据采用的压缩格式
Content-Length: 服务器通过这个头告诉浏览器,回送的数据的大小长度
Content-Type: 服务器通过这个头告诉浏览器,回送数据的类型
Last-Modified: 服务器通过这个头告诉浏览器,缓存资源的最后修改时间
Refresh:服务器通过这个头告诉浏览器,定时刷新网页
Content-Disposition: attachment; filename=aaa.zip:服务器通过这个头告诉浏览器,以下载方式打开数据
ETag: W/"7777-1242234904000":缓存相关的头,为每一个资源配一个唯一的编号
---------------------
作者:阿祥小王子
来源:CSDN
原文:https://blog.csdn.net/danielzhou888/article/details/72954695
版权声明:本文为博主原创文章,转载请附上博文链接!

17 常见的请求方法
GET
POST
DELETE
PUT
PATCH
OPTIONS

18 常见的状态码
- 200 (成功) 服务器已成功处理了请求。 通常,这表示服务器提供了请求的网页。
- 301/302 重定向
- 403 (禁止)服务器拒绝请求。
- 404 (未找到)服务器找不到请求的网页。
- 500 (服务器内部错误) 服务器遇到错误,无法完成请求。

19 ORM补充
- only
result = models.User.objects.all().only('id', 'name') # 只取某几个字段

- defer
result = models.User.objects.all().defer('id', 'name') # 排除某几个字段
假设要查询的表有10条数据,则普通查询时【11次单表查询】
- selected_related (高性能) (如链表多,性能会越来越差,这时候prefetch_related就派上用场了) 【1次链表】
# 提前建立连表
result = models.User.objects.all().selected_related('dp') # 提前与dp建立连表

for item in result:
print(item.name, item.dp.title) # 这时候查询很快

--支持o2o/02m

- prefetch_related 【2次单表查询】
# select * from user
# 通过python代码(预先)获取:dp_id=[1,2]
# select * from depart where id in dp_id

--支持o2o/02m/m2m

- F Q 查询
from django.db.models import F, Q
# F 更新数据库字段
# Q 构造复杂条件

- 通过ORM写原生SQL(看博客)
- extra
- raw
- 原生sql

20 常见的请求体
- Form表单提交 --> &
POST www.example.com HTTP/1.1Content-Type: application/x-www-form-urlencoded;charset=utf-8title=test&sub%5B%5D=1&sub%5B%5D=2&sub%5B%5D=3

- ajax请求
\r\n\r\n name=alex&psd=123 .... # form格式
\r\n\r\n {"name": "alex", "psw": 123} # json格式

补充:django中获取请求体
- request.POST
- request.body

21 django rest framework的作用?
快速搭建基于restful规范的接口

22 drf框架
1 为什么要学?
a 前后端开发越来越火
b 公司有需求

2 怎么学?
看官方文档(但官方的文档很差)
看源码(从dispatch引出所有东西)

3 各种组件
- 路由: 可以通过as_view传参,根据请求方式不同执行相应的方法;可以在url中设置一个结尾,类似于.json
- 视图: 帮助开发者提供一些类, 并在类中提供了多个方法以供我们使用.
- 版本: 在路由url中设置version参数, 用户请求时候传入参数.在request.version中获取版本, 根据版本不同做不同的操作
- 认证: 写一个类并注册到认证类, 在类的authticate方法中编写认证逻辑.
认证成功(user, auth)
raise AuthicateFaild("...")
None
- 权限: 写一个类并注册到权限类, 在类的has_permission方法中编写权限逻辑
返回 True
返回 False
- 频率: 写一个类并注册到频率类, 在类的allow_request/wait方法中编写频率逻辑

在allow_request中编写业务逻辑, 如果allow_request中没有通过, wait里写再等多少秒就可以访问了.

allow_request的返回值
True
False --> wait就执行, wait的返回值决定还要等多少秒就可以再次访问

- 解析器: 就是对请求体中的数据进行解析, 根据content_type请求头, 选择不同的解析器(每一种解析器就是一个类)对请求体中的数据进行解析
- 分页: 对从数据库中获取到的数据进行分页处理
其实就是SQL-->limit offset
- 根据页码: ?page=1&size=10
- 根据索引: ?offset=60&limit=10
- 根据加密: ?page=<加密的页码>
- 序列化: 对queryset序列化以及对请求数据格式校验
- 渲染器: 根据url中传入的后缀, 决定将数据如何渲染到页面上

23 数据库中通过分页数据时, 页码越大速度越慢, 为什么以及怎么解决?
原因: 页码越大向后需要扫描的行数越多, 因为每次都是从0开始扫描

解决:
- 限制显示的页数
- 用SQL查询条件 where
select * from tb where id > 100000
# 这时候直接跳到100000往后扫

24 git开发时, 如果代码出现bug, 你们是如何解决?
1 创建一个bug分支, 然后进行bug处理, 处理完毕后, 合并到master分支
2 删除bug分支
3 回到dev分支继续开发

25 git rebase的作用?
保持提交记录的整洁.(体现在提交历史不会有分叉)

26 谁来做代码review?
- 组长
- 带你的人

27 如何做代码review?
创建review分支

28 原生ajax
- XMLHttpRequest

29 谈谈你对django和flask的认识?
django
是一个大而全的框架
通常用于大型Web应用由于内置组件足够强大所以使用Django开发可以一气呵成
优点是大而全,缺点也就暴露出来了,这么多的资源一次性全部加载,肯定会造成一部分的资源浪费
flask
是一个轻量级的框架
通常应用于小型应用和快速构建应用,其强大的三方库,足以支撑一个大型的Web应用
优点是精悍简单

django/flask都是基于wsgi协议实现的框架, 不同之处在于, django的请求通过参数传递, 而flask的请求和session通过上文传递的

- flask和django最大的不同点

django的请求通过参数传递, (session在request中)
而flask的请求和session通过上文传递的

21 什么是函数, 什么是方法
from types import FunctionType,MethodType

# obj = Foo()
# print(isinstance(obj.func,FunctionType)) # False
# print(isinstance(obj.func,MethodType)) # True 方法

print(isinstance(Foo.func,FunctionType)) # True 函数
print(isinstance(Foo.func,MethodType)) # False

函数: 参数都是自己传
方法: self会自己传

22 threading.local
通过线程唯一标示, 为线程开辟一块独立的内存空间

23 flask中g的生命周期?
请求到来时在wsgi_app中封装了g, 最后在给浏览器返回响应前删掉 (只在一次请求中存在)

24 flask中g和session的区别?
g只在一次请求中存在
session下一次请求来时还存在 (从浏览器的cookie中获取)

25 flask中的g和全局变量的区别?
g在请求来时才封装, 封装好了才存在
全局变量在程序启动就存在了

26 flask中before_request的执行时机:
local之后

27 g的作用?
在一次请求里生效的上下文

28 flask 中 local的作用
类似于local.threading , local是升级版

29 flask 中 localstack的作用
维护local中的__storage__中的栈 # [app_ctx, ]
__storage__ = {
1233: {stack: [app_ctx, ]},
...
}

31 flask中为什么导入request, 就可以使用?
每次执行request.xx方法时,会触发LocalProxy对象的__getattr__等方法,由方法每次动态的使用LocalStack去Local中获取数据。

32 面向对象的抽象类有什么用?
用于告知其他人以后继承时,需要实现那个方法,如:rest framework源码

33 面向对象中__getattr__和__setattr__的执行顺序?
getattr之前必须先setattr

34 flask的生命周期中有多少个local和localstack
2个local
- 请求上下文
- 应用上下文
2个localstack

35 为什么要创建两个Local?或两个LocalStack?
要做离线脚本, 编写离线脚本时只需要配置文件(不需要请求上下文), 而配置文件存在app中 (如: sqlalchemy的创建数据库), 所以要将app和request拆分开

36 为什么将local中的__storage__中的stack维护成一个栈?(结构见29)
web运行时(web runtime): # stack的栈中永远只有一个ctx对象
请求Local = {
1111:{
stack:[ctx1,]
},
1112:{
stack:[ctx2,]
},
1113:{
stack:[ctx3,]
}
}

应用Local = {
1111:{
stack:[app_ctx1,]
}
1112:{
stack:[app_ctx2,]
},
1113:{
stack:[app_ctx3,]
}
}

多app离线脚本时
from flask import current_app
app1 = create_app()
app_ctx1 = app1.app_context() # app_ctx = app/g

app2 = create_app()
app_ctx2 = app2.app_context() # app_ctx = app/g

with app_ctx1: # __enter__,通过LocalStack放入Local中
print(current_app) # app1
with app_ctx2:
print(current_app) # app2

print(current_app) # app1

"""
请求Local = {
}

应用Local = {
1111:{
stack:[app_ctx1,app_ctx2] # 这就是为什么维护成一个栈的原因, 因为多应用app时, stack会存在两个app_ctx
}
}
"""

37 数据库什么时候需要加锁?
- 计数
应用场景: 商品数量

38 简单梳理一下HTTP请求应答的流程:

请求流程 (以flask为例)
Web浏览器发送HTTP请求给Nginx
Nginx转发给uWSGI
uWSGI将其按照WSGI规范进行处理
uWSGI将HTTP请求给到Flask应用进行处理
Flask应用对HTTP请求进行处理
- 比如查询数据库获取信息
- 比如将信息拼装成HTML网页

Flask产生HTTP应答,按照WSGI规范给到uWSGI
uWSGI将HTTP应答以TCP方式发给Nginx
Nginx将HTTP应答以TCP方式发给Web浏览器

大白话 (以django为例)
Nginx:hello wsgi,我刚收到一个请求,你准备下然后让django来处理吧

uWSGI:好的nginx,我马上设置环境变量,然后把请求交给django

Django:谢谢WSGI,我处理完请求马上给你响应结果

uWSGI:好的,我在等着

Django:搞定啦,麻烦wsgi吧响应结果传递给nginx

uWSGI:太棒了,nginx,响应结果请收好,已经按照要求传递给你了

Nginx:好滴。我把响应交给用户。合作愉快


############# 网络编程与并发编程 ############

39 OSI5层模型
http://www.ruanyifeng.com/blog/2012/05/internet_protocol_suite_part_i.html
互联网的实现,分成好几层。每一层都有自己的功能,就像建筑物一样,每一层都靠下一层支持。

应用层 "应用层"的作用,就是规定应用程序的数据格式。

传输层 就是建立"端口到端口"的通信。相比之下,"网络层"的功能是建立"主机到主机"的通信。只要确定主机和端口,我们就能实现程序之间的交流。
网络层 它的作用是引进一套新的地址(IP地址),使得我们能够区分不同的计算机是否属于同一个子网络。这套地址就叫做"网络地址",简称"网址"。
数据链路层 有了ARP协议之后,我们就可以得到同一个子网络内的主机MAC地址,可以把数据包发送到任意一台主机之上了。
物理层 这就叫做"实体层",它就是把电脑连接起来的物理手段。

40 三次握手, 四次挥手
三次握手:
1客户端: 我要连你了
2服务端: 可以连
3客户端: 我连过去了

为什么三次?因为传输信道不可靠, 必须通过第三次确定是可靠的传输

四次挥手:
1 主机1向主机2发送, 我没有数据要发给你了
2 主机2告诉主机1, 我'同意'你的关闭请求
3 主机2向主机1发送, (我也没有数据传输了,) 请求关闭连接
4 主机1告诉主机2, 我'同意'你的关闭请求

为什么四次?因为主机1向主机2请求关闭时, 主机2可能正在向主机1传输数据, 需要传完才能关闭连接

41 TCP和UDP区别
TCP: 建立连接, 才开始收发数据 (可靠的连接)
UDP: 不建立连接, 直接发数据 (不可靠的连接)

42 路由器和交换机的区别?
交换机: 在局域网内建立连接 # 数据链路层
路由器: 用户在不同网络间数据的跨网络传输 # 网络层

43 ARP协议?
局域网广播
即(IP)地址解析协议, 用于实现从IP地址到MAC地址的映射, 即访问目标的IP对应的MAC地址
有了ARP协议之后,我们就可以得到同一个子网络内的主机MAC地址,可以把数据包发送到任意一台主机之上了。

44 DNS解析?
将域名解析成IP地址

45 http和https区别?
http是明文传输的
https是通过证书【加密】传输的

46 进程、线程、协程的区别?
https://juejin.im/post/5b0014b7518825426e023666

进程:
进程是系统资源分配的最小单位
一个进程中可以存在多个线程
通信问题: 由于进程间是隔离的,各自拥有自己的内存资源, 因此相对于线程比较安全, 所以不同进程之间的数据只能通过 IPC(Inter-Process Communication) 进行通信共享.

# 因此进程的创建和销毁都是相对于系统资源,所以是一种比较昂贵的操作。

线程:
线程属于进程
线程共享进程提供的内存地址空间
线程是CPU调度的最小单位
通信问题:   进程相当于一个容器,而线程而是运行在容器里面的,因此对于容器内的东西,线程是共同享有的,因此线程间的通信可以直接通过全局变量进行通信,但是由此带来的例如多个线程读写同一个地址变量的时候则将带来不可预期的后果,因此这时候引入了各种锁的作用,例如互斥锁等。


协程:
协程是属于线程的。协程程序是在线程里面跑的,因此协程又称微线程和纤程等
协程的调度切换是用户(程序员)手动切换的,因此更加灵活,因此又叫用户空间线程.
原子操作性。由于协程是用户(程序员)调度的,所以不会出现执行一半的代码片段被强制中断了,因此无需原子操作锁。

协程的实现:迭代器和生成器
一般配合IO操作

47 GIL锁
保证同一时刻, 一个进程中只有一个线程被CPU调度

# 只能一定程度上保证进程中的数据安全性, 想保证数据安全性,还得自己给数据加锁。
# 所以CPython想要使用CPU的多核,需开多进程;而且计算密集型,才需要多进程

为什么要有GIL锁?
为了能利用多核多线程的优势, 但又要保证线程之间数据完整性和状态同步, 就采用了最简单的加锁方式.

48 进程间如何数据共享?
- queue
- pipe
- manager

49 twisted和requests的区别?
requests是一个Python实现的可以伪造浏览器发送Http请求的模块。
- 封装socket发送请求

twisted是基于事件循环的异步非阻塞网络框架。
- 封装socket发送请求
- 单线程完成并发请求

50 http请求的本质?
请求头+请求体

51 线程和协程做IO操作时, 区别在哪?
线程会造成CPU资源浪费, 协程不会

52 什么是深度优先?什么是广度优先?
深度优先: 爬虫从一条链接爬到底
广度优先: 先爬取完第一层, 再爬第二层

53 scrapy中如何实现深度和广度优先?
深度优先: 后进先出
广度优先: 先进先出

54 scrapy中调度器和爬取队列和dupefilter的关系?
调度器: 通过调度器去爬取队列(或dupfilter)放/取任务(或记录)
爬取队列: 里面存放下载任务[Request, Request, ...]
dupfilter: 里面存放访问过的url唯一标示

55 scrapy-redis组件的作用?
1. dupefilter - URL去重规则(被调度器使用)
2. scheduler - 调度器
3. pipeline - 数据持久化
4. 定制起始URL

56 深度优先和广度优先如何实现?
基于栈和队列实现
基于有序集合(优先级队列)实现

57 什么是websocket?
是一套协议, 协议规定了:
- 连接时需要握手
- 发送数据时, 加密
- 连接之后不断开

58 websocket的意义?
解决了服务端主动向客户端发送数据的问题

59 websocket的缺点?
兼容性, 需要浏览器有websocket对象

60 哪些框架支持websocket?
- Flask gevent_websocket
- Django channel
- Tornado 框架自带

61 websocket应用场景?
实时响应页面(服务器主动向客户端发送消息)

62 什么是异步?
其实就是回调, 当一个任务完成后执行某个函数

接触:
1 爬虫, 下载完成后自动执行回调函数
2 ajax, 向后台发送请求, 请求完成后执行回调函数

63 什么是非阻塞?
- 其实就是不等待
- socket中设置setblocking(False), namesocket就不再阻塞了

64 IO多路复用的作用?
监听socket的状态:
- 是否连接成功
- 是否获取到了结果

IO多路复用的实现:
- select,只能监听1024个socket;内部会循环所有的socket去检测;
- poll,无个数限制,内部会循环所有的socket去检测;
- epoll,无个数限制,回调。

posted @ 2019-04-28 11:11  孙昌恒  阅读(213)  评论(0编辑  收藏  举报