知识点总结二
回顾:
1.什么是wsgi?
web服务网关接口,wsgi是一个协议,实现的模块有:wsgiref(django中),werkzeug(flask中)
实现其协议的本质上就是socket服务端用于接收用户请求,并处理。一般web框架基于wsgi实现,这样实现关注点分离。
werkzeug示例:
from werkzeug.wrappers import Response
from werkzeug.serving import run_simple
def run_server(environ, start_response):
response = Response('hello')
return response(environ, start_response)
if __name__ == '__main__':
run_simple('127.0.0.1', 8000, run_server)
Flask源码入口:
from werkzeug.wrappers import Response
from werkzeug.serving import run_simple
class Flask(object):
def __call__(self,environ, start_response):
response = Response('hello')
return response(environ, start_response)
def run(self):
run_simple('127.0.0.1', 8000, self)
app = Flask()
if __name__ == '__main__':
app.run()
2.Flask源码入口:
from werkzeug.wrappers import Response
from werkzeug.serving import run_simple
class Flask(object):
def __call__(self,environ, start_response):
response = Response('hello')
return response(environ, start_response)
def run(self):
run_simple('127.0.0.1', 8000, self)
app = Flask()
if __name__ == '__main__':
app.run()
3.flask提供的功能:
- 配置文件
- 所有配置都在app.config中
- app.config["xx"] = 123
- app.config.from_object("类的路径")
- 应用:importlib、getattr
- django中间件
- rest framework全局配置
session
闪现:基于session实现
路由:自定义的装饰器放下面
视图
响应和请求
模板
特殊的装饰器
中间件
新内容:
1.路由+视图
a. 路由设置的两种方式:
方式一:@app.route('/xxx')
def index():
return "index"
方式二: def index():
return "index"
app.add_url_rule("/xxx",None,index)
注意事项:
- 不能让endpoint重名
- 如果重名函数也一定要相同。
b. 参数
rule, URL规则
view_func, 视图函数名称
endpoint=None, 名称,用于反向生成URL,即: url_for('名称')
methods=None, 允许的请求方式,如:["GET","POST"]
strict_slashes=None, 对URL最后的 / 符号是否严格要求,
redirect_to=None, 重定向到指定地址
defaults=None, 默认值,当URL中无参数,函数需要参数时,使用defaults={'k':'v'}为函数提供参数
subdomain=None, 子域名访问
c.. CBV
import functools
from flask import Flask,views
app = Flask(__name__)
def wrapper(func):
@functools.wraps(func)
def inner(*args,**kwargs):
return func(*args,**kwargs)
return inner
class UserView(views.MethodView): 类需要继承views.MethodView类
methods = ['GET'] --- 指定请求方式
decorators = [wrapper,] ---类中装饰器的全局用法,也可以单独在某一个函数上加
def get(self,*args,**kwargs):
return 'GET'
def post(self,*args,**kwargs):
return 'POST'
app.add_url_rule('/user',None,UserView.as_view('uuuu'))
if __name__ == '__main__':
app.run()
d. 自定义正则
from flask import Flask,url_for
app = Flask(__name__)
# 步骤一:定制类
from werkzeug.routing import BaseConverter
class RegexConverter(BaseConverter):
"""
自定义URL匹配正则表达式
"""
def __init__(self, map, regex):
super(RegexConverter, self).__init__(map)
self.regex = regex
def to_python(self, value):
"""
路由匹配时,匹配成功后传递给视图函数中参数的值
:param value:
:return:
"""
return int(value)
def to_url(self, value):
"""
使用url_for反向生成URL时,传递的参数经过该方法处理,返回的值用于生成URL中的参数
:param value:
:return:
"""
val = super(RegexConverter, self).to_url(value)
return val
# 步骤二:添加到转换器
app.url_map.converters['reg'] = RegexConverter
"""
1. 用户发送请求
2. flask内部进行正则匹配
3. 调用to_python(正则匹配的结果)方法
4. to_python方法的返回值会交给视图函数的参数
"""
# 步骤三:使用自定义正则
@app.route('/index/<reg("\d+"):nid>')
def index(nid):
print(nid,type(nid))
print(url_for('index',nid=987))
return "index"
if __name__ == '__main__':
app.run()
2.session的实现原理
3.蓝图
目标:给开发者提供目录结构
其他:
- 自定义模板、静态文件
- 某一类url添加前缀
- 给一类url添加before_request
4.threading.local
作用:为每个线程创建一个独立的空间,使得线程对自己的空间中的数据进行操作(数据隔离)。
import threading
from threading import local
import time
obj = local()
def task(i):
obj.xxxxx = i
time.sleep(2)
print(obj.xxxxx,i)
for i in range(10):
t = threading.Thread(target=task,args=(i,))
t.start()
问题:
- 如何获取一个线程的唯一标记? threading.get_ident()
- 根据字典自定义一个类似于threading.local功能?
import time
import threading
DIC = {}
def task(i):
ident = threading.get_ident()
if ident in DIC:
DIC[ident]['xxxxx'] = i
else:
DIC[ident] = {'xxxxx':i }
time.sleep(2)
print(DIC[ident]['xxxxx'],i)
for i in range(10):
t = threading.Thread(target=task,args=(i,))
t.start()
- 根据字典自定义一个为每个协程开辟空间进行存取数据。
import time
import threading
import greenlet
DIC = {}
def task(i):
# ident = threading.get_ident()
ident = greenlet.getcurrent()
if ident in DIC:
DIC[ident]['xxxxx'] = i
else:
DIC[ident] = {'xxxxx':i }
time.sleep(2)
print(DIC[ident]['xxxxx'],i)
for i in range(10):
t = threading.Thread(target=task,args=(i,))
t.start()
- 通过getattr/setattr 构造出来 threading.local的加强版(协程)
import time
import threading
try:
import greenlet
get_ident = greenlet.getcurrent
except Exception as e:
get_ident = threading.get_ident
class Local(object):
DIC = {}
def __getattr__(self, item):
ident = get_ident()
if ident in self.DIC:
return self.DIC[ident].get(item)
return None
def __setattr__(self, key, value):
ident = get_ident()
if ident in self.DIC:
self.DIC[ident][key] = value
else:
self.DIC[ident] = {key:value}
obj = Local()
def task(i):
obj.xxxxx = i
time.sleep(2)
print(obj.xxxxx,i)
for i in range(10):
t = threading.Thread(target=task,args=(i,))
t.start()
面向对象里你见过的带双下划线的方法:
flask源码里用到了__getattr__ __setattr__(线程这里用到)
5.上下文管理
当请求到来时
数据在字典中根据线程或协程的唯一标识(ctx对象)来存放
视图函数 ,根据线程的唯一标识(ctx对象)
根据当前线程的唯一标记,将字典中的数据清除。
线程跟线程的数据隔离
请求到来时候:
ctx = RequestContext(self, environ) # self是app对象,environ请求相关的原始数据
ctx.request = Request(environ)
ctx.session = None
将包含了request/session的ctx对象放到“空调”
{
1232:{ctx:ctx对象}
1231:{ctx:ctx对象}
1211:{ctx:ctx对象}
1111:{ctx:ctx对象}
1261:{ctx:ctx对象}
}
视图函数:
from flask import reuqest,session
request.method
请求结束:
根据当前线程的唯一标记,将“空调”上的数据移除。
datetime.utcnow() 当前unix时间戳(1970年1月1日距当前多少秒)
scrapy_redis运行方式与scrapy有点区别:scrapy runspider myspider_redis.py
lpush myspider:start_urls http://..... 发起一个指令,如果不发指令爬虫会一直处于等待状态
Processes是将redis数据库中的数据取出来放在本地的数据库