上节回顾

  1. # 1 蓝图
  2. - 第一步:导入
  3. - 第二步:实例化得到对象,可以指定statictemplates
  4. - 第三步:app中注册蓝图,注册蓝图时,可以指定前缀
  5. - 第四步:使用蓝图,注册路由,注册请求扩展
  6. # 2 g对象
  7. -当次请求的全局对象,在当次请求中可以放值和取值
  8. -跟session的区别是
  9. # 3 flask中使用mysql数据库
  10. -pymysql,在一个视图函数中,创建一个连接对象,操作,操作完就关闭连接
  11. -连接对象不要用单例,可能会出现数据错乱问题
  12. # 4 数据库连接池
  13. -dbutils
  14. -使用步骤:
  15. 1 第一步:导入,实例化得到一个对象, pool
  16. 2 第二步:pool做成单例,以模块导入
  17. 3 第三步:从pool中拿到一个连接 pool.connection()
  18. 3 第四步:使用连接获得游标,使用游标操作数据库
  19. # 5 压测接口,验证是否使用了连接池
  20. # 6 django中没有数据库连接池,可以使用第三方,也可以手写数据库连接池。

今日内容

1 请求上下文分析(源码:request原理)

1.1 导出项目的依赖

  1. # 之前 pip freeze >requirments.txt 把当前解释器环境下的所有第三方依赖都导出来
  2. # 使用第三方模块,更精确的导出依赖 pipreqs
  3. 第一步:安装 pip3 install pipreqs
  4. 第二步:使用命令,导出项目依赖 pipreqs ./
  5. -win由于编码问题会出错:pipreqs ./ --encoding=utf8
  6. -maclinx没有问题
  7. 第三步:就会在项目根路径下生成:requirements.txt
  8. '''
  9. pipreqs:基于项目中的import导入生成requirements文件
  10. '''

导出解释器的所有依赖,意味着可能导出项目不使用的无用依赖。使用pipreqs,可以实现对项目使用到的依赖精确导出。

1.2 函数和方法

  1. # 只要会自动传值,就是方法,函数,有几个值就要传几个值,否则报错
  2. # 函数就是普通的函数,有几个参数就要传几个参数
  3. # 方法:绑定给对象的方法,绑定给类的方法,绑定给谁的,由谁来调用,会自动把自身传入
  4. # 类的绑定方法,对象可以来调用,会自动把类传入
  5. # 对象的绑定方法,类可以来调用? 类可以调用,但是它就变成了普通函数,有几个值,就要传几个值,没有自动传值了
  6. # MethodType检查一个对象,是不是方法
  7. # FunctionType检查一个对象,是不是函数
  8. # isinstance 判断一个对象,是不是一个类的对象
  9. # issubclass 判断一个类,是不是另一个类的子类
  10. from types import MethodType, FunctionType
  11. class Foo(object):
  12. def fetch(self):
  13. pass
  14. @classmethod
  15. def test(cls):
  16. pass
  17. @staticmethod
  18. def test1():
  19. pass
  20. # a=Foo()
  21. # print(isinstance(a,Foo))
  22. # print(isinstance('a',Foo))
  23. #
  24. # class Foo2(Foo):
  25. # pass
  26. # class Foo3():
  27. # pass
  28. # print(issubclass(Foo2,Foo))
  29. # print(issubclass(Foo3,Foo))
  30. def add():
  31. pass
  32. # 类来调用对象的绑定方法,
  33. print(isinstance(Foo.fetch, MethodType)) # False 类来调用对象的绑定方法,该方法就变成了普通函数
  34. obj = Foo()
  35. print(isinstance(obj.fetch, MethodType)) # True 对象来调用自己的绑定方法,fetch就是方法
  36. print(isinstance(Foo.fetch, FunctionType)) # True 类来调用对象的绑定方法,该方法就变成了普通函数
  37. print(isinstance(add, FunctionType)) # True 就是个普通函数
  38. print(isinstance(add, MethodType)) # False 就是个普通函数
  39. print(isinstance(Foo.test, MethodType)) # True test 是绑定给类的方法,类来调用,就是方法
  40. print(isinstance(obj.test, MethodType)) # True 对象调用类的绑定方法,还是方法
  41. print(isinstance(Foo.test1, MethodType)) # False 是普通函数
  42. print(isinstance(obj.test1, MethodType)) # False 是普通函数
  43. print(isinstance(obj.test1, FunctionType)) # True,静态方法,就是普通函数,对象和类都可以调用,有几个值就传几个值
  44. # 关于issubclass
  45. class A:
  46. pass
  47. class B:
  48. pass
  49. class C:
  50. pass
  51. class D(A, B):
  52. pass
  53. print(issubclass(D, (C,)))
  54. print(issubclass(D, (A, C)))
  55. print(issubclass(D, (A, C)))
  56. '''
  57. issubclass(x, (A, B, ...)) 相当于 issubclass(x, A) or issubclass(x, B) or ...
  58. '''

1.3 threading.local对象

  1. # local 对象
  2. # 并发编程时,多个线程操作同一个变量,会出现并发安全的问题,咱们需要加锁
  3. # 使用local对象,多线程并发操作时,不需要加锁,不会出现数据错乱threading.local
  4. # 其他语言中也有这个东西ThreadLocal,java中面试会被经常问到,python没人问
  5. # 本质原理:
  6. 多个线程修改同一个数据,复制多份变量给每个线程用,为每个线程开辟一块空间进行数据存储
  7. 每个线程操作自己的那部分数据

flask的request对象,就是重写了一个local类,代码示例:

  1. # # 数据错乱
  2. # # 原因是io操作导致立切换到下一个线程,一直切换到最后一个线程,将全局变量赋值为9
  3. # import time
  4. # from threading import get_ident, Thread
  5. #
  6. # '''get_ident C代码
  7. # thread_get_ident(PyObject *self, PyObject *Py_UNUSED(ignored))
  8. # {
  9. # unsigned long ident = PyThread_get_thread_ident();
  10. # if (ident == PYTHREAD_INVALID_THREAD_ID) {
  11. # PyErr_SetString(ThreadError, "no current thread ident");
  12. # return NULL;
  13. # }
  14. # return PyLong_FromUnsignedLong(ident);
  15. # }
  16. # '''
  17. #
  18. # num = 0
  19. #
  20. #
  21. # def task(args):
  22. # global num
  23. # num = args
  24. # time.sleep(1) # 添加io操作
  25. # print(f'当前的线程号:{get_ident()},num={num}')
  26. #
  27. #
  28. # if __name__ == '__main__':
  29. # for i in range(10):
  30. # t = Thread(target=task, args=(i,))
  31. # t.start()
  32. # # 使用锁解决并发安全问题
  33. # import time
  34. # from threading import Thread, get_ident, Lock
  35. #
  36. # l = Lock()
  37. #
  38. # num = 0
  39. #
  40. #
  41. # def task(args):
  42. # print(1000) # 这行代码是并发执行的
  43. # global num
  44. # l.acquire() # 后面的代码是同步执行的
  45. # num = args
  46. # time.sleep(1)
  47. # print(f'当前线程号:{get_ident()},num={num}')
  48. # l.release()
  49. #
  50. #
  51. # if __name__ == '__main__':
  52. # for i in range(10):
  53. # t = Thread(target=task, args=(i,))
  54. # t.start()
  55. # # 使用threading.local对象 不会出现并发安全问题
  56. # import time
  57. # from threading import local, get_ident, Thread
  58. #
  59. # nb_obj = local()
  60. # nb_obj.num = 0
  61. #
  62. #
  63. # def task(args):
  64. # nb_obj.num = args
  65. # # time.sleep(1)
  66. # print(f'线程号:{get_ident()},num={nb_obj.num}')
  67. #
  68. #
  69. # if __name__ == '__main__':
  70. # for i in range(10):
  71. # t = Thread(target=task, args=(i,))
  72. # t.start()
  73. # threading对象原理
  74. import time
  75. try: # 对协程的支持
  76. from greenlet import getcurrent as get_ident
  77. except Exception as e:
  78. from threading import get_ident, Thread
  79. class Local:
  80. def __init__(self):
  81. object.__setattr__(self, 'storage', {})
  82. # self.storage = {} 会导致__setattr__一直调用自己 --> 递归
  83. def __setattr__(self, key, value): # obj.key = value
  84. ident = get_ident()
  85. if ident in self.storage: # 如果这个线程使用过local对象
  86. self.storage[ident][key] = value # 这个线程只能修改自己的 {线程1:{key:value},线程2:{key:value}}
  87. else: # 如果线程没有使用过local对象
  88. self.storage[ident] = {key: value}
  89. def __getattr__(self, key):
  90. ident = get_ident()
  91. print(self.storage)
  92. return self.storage[ident][key] # 根据线程号去自己的字段里面取数据
  93. obj = Local()
  94. obj.num = 1
  95. def task(args):
  96. obj.num = args
  97. time.sleep(1)
  98. print(f'线程号:{get_ident()},num={obj.num}')
  99. if __name__ == '__main__':
  100. for i in range(10):
  101. t = Thread(target=task, args=(i,))
  102. t.start()

1.4 偏函数

  1. # 可以提前传值
  2. from functools import partial
  3. def add(a,b,c):
  4. print(a)
  5. print(b)
  6. print(c)
  7. return a+b+c
  8. # print(add(2,3,4)) # 传少了报错
  9. # 可以先传入一个参数 后续再传入其他参数
  10. # 借助于偏函数,先提前给他把第一个参数传入,后面知道了后面俩参数,再传后面俩
  11. add=partial(add,2)
  12. #
  13. # # 干了很多事
  14. #
  15. print(add(3,4))

1.5 flask 整个生命执行流程(1.1.4版本为例)

  1. # 本质是去字典里面,拿不同线程stack对应的ctx中的request。
  2. from flask import Flask, request
  3. app = Flask(__name__)
  4. @app.route('/')
  5. def index():
  6. return 'xxx'
  7. if __name__ == '__main__':
  8. app.run()
  9. # 内部调用了werkzeug 的 run_simple('localhost', 4000, hello),请求如果来了,会执行hello()
  10. # run_simple('localhost', 4000, hello)
  11. # run_simple可以启动一个符合WSGI协议的服务器。
  12. # run_simple(host, port, self) # 请求来了,会执行 self(),self 就是app对象,app()
  13. # 在run_simple中会调用我们传入的self对象,这一行为会触发Flask类的__call__
  14. # 请求来了---》app()----->Flask.__call__--->self.wsgi_app(environ, start_response)
  15. def wsgi_app(self, environ, start_response):
  16. # environ:http请求拆成了字典
  17. # ctx对象:RequestContext类的对象,对象里有:当次的request对象,app对象,session对象
  18. ctx = self.request_context(environ)
  19. error = None
  20. try:
  21. try:
  22. #ctx RequestContext类 push方法
  23. ctx.push()
  24. # 匹配成路由后,执行视图函数
  25. response = self.full_dispatch_request()
  26. except Exception as e:
  27. error = e
  28. response = self.handle_exception(e)
  29. except:
  30. error = sys.exc_info()[1]
  31. raise
  32. return response(environ, start_response)
  33. finally:
  34. if self.should_ignore_error(error):
  35. error = None
  36. ctx.auto_pop(error)
  37. # RequestContext :ctx.push
  38. def push(self):
  39. # _request_ctx_stack = LocalStack() ---》push(ctx对象)--》ctx:request,session,app --》传入LocalStack()对象
  40. _request_ctx_stack.push(self)
  41. '''
  42. _request_ctx_stack.push(self)
  43. 相当于:LocalStack().push('ctx对象')
  44. 说明:对象调用方法,会把自己传进去,所以push方法第一个参数是LocalStack对象,第二次参数是ctx(RequestContext类的对象)
  45. '''
  46. # session相关的
  47. if self.session is None:
  48. session_interface = self.app.session_interface
  49. self.session = session_interface.open_session(self.app, self.request)
  50. if self.session is None:
  51. self.session = session_interface.make_null_session(self.app)
  52. # 路由匹配相关的
  53. if self.url_adapter is not None:
  54. self.match_request()
  55. # LocalStack() push --->obj 是ctx对象
  56. def push(self, obj):
  57. #self._local _local 就是咱们刚刚自己写的Local的对象---》LocalStack的init初始化的_local---》self._local = Local()---》Local对象可以根据线程协程区分数据
  58. rv = getattr(self._local, "stack", None)
  59. # 一开始没有值
  60. if rv is None:
  61. rv = []
  62. self._local.stack = rv # self._local.stack 根据不同线程用的是自己的数据
  63. rv.append(obj) # self._local.stack.append(obj)
  64. # {'线程id号':{stack:[ctx]},'线程id号2':{stack:[ctx]}}
  65. return rv
  66. '''
  67. 每个线程都有自己的Local对象,它们之间不会相互干扰。所以Local对象不是单例的,它是线程局部的。
  68. '''
  69. # from flask import request
  70. # 再往后执行,就会进入到路由匹配,执行视图函数
  71. # request = LocalProxy(partial(_lookup_req_object, "request"))
  72. # LocalProxy 代理类---》method---》代理类去当前线程的stack取出ctx,取出当时放进去的request
  73. 视图函数中:print(request.method)
  74. # print(request) 执行LocalProxy类的__str__方法
  75. # request.method 执行LocalProxy类的__getattr__
  76. def __getattr__(self, name): #name 是method
  77. # self._get_current_object() 就是当次请求的request
  78. return getattr(self._get_current_object(), name)
  79. # 通过反射获取请求对象中的方法
  80. # LocalProxy类的方法_get_current_object
  81. def _get_current_object(self):
  82. if not hasattr(self.__local, "__release_local__"):
  83. return self.__local()
  84. try:
  85. return getattr(self.__local, self.__name__)
  86. except AttributeError:
  87. raise RuntimeError("no object bound to %s" % self.__name__)
  88. # self.__local 是在 LocalProxy 类实例化的时候传入的local
  89. # 在这里实例化的:request = LocalProxy(partial(_lookup_req_object, "request"))
  90. # local 是 partial(_lookup_req_object, "request")
  91. #_lookup_req_object ,name=request
  92. def _lookup_req_object(name):
  93. top = _request_ctx_stack.top # 取出了ctx,是当前线程的ctx
  94. if top is None:
  95. raise RuntimeError(_request_ctx_err_msg)
  96. return getattr(top, name) #从ctx中反射出request,当次请求的request
  1. 请求上下文执行流程(ctx):
  2. -0 flask项目一启动,有6个全局变量
  3. -_request_ctx_stackLocalStack对象
  4. -_app_ctx_stack LocalStack对象
  5. -request LocalProxy对象
  6. -session LocalProxy对象
  7. -1 请求来了 app.__call__()---->内部执行:self.wsgi_app(environ, start_response)
  8. -2 wsgi_app()
  9. -2.1 执行:ctx = self.request_context(environ):返回一个RequestContext对象,并且封装了request(当次请求的request对象),session,flash,当前app对象
  10. -2.2 执行: ctx.push():RequestContext对象的push方法
  11. -2.2.1 push方法中中间位置有:_request_ctx_stack.push(self),selfctx对象
  12. -2.2.2 _request_ctx_stack对象的类中找push方法(LocalStack中找push方法)
  13. -2.2.3 push方法源码:
  14. def push(self, obj):
  15. #通过反射找self._local,在init实例化的时候生成的:self._local = Local()
  16. #Local(),flask封装的支持线程和协程的local对象
  17. # 一开始取不到stack,返回None
  18. rv = getattr(self._local, "stack", None)
  19. if rv is None:
  20. #走到这,self._local.stack=[],rv=self._local.stack
  21. self._local.stack = rv = []
  22. # 把ctx放到了列表中
  23. #self._local={'线程id1':{'stack':[ctx,]},'线程id2':{'stack':[ctx,]},'线程id3':{'stack':[ctx,]}}
  24. rv.append(obj)
  25. return rv
  26. -3 如果在视图函数中使用request对象,比如:print(request)
  27. -3.1 会调用request对象的__str__方法,request类是:LocalProxy
  28. -3.2 LocalProxy中的__str__方法:lambda x: str(x._get_current_object())
  29. -3.2.1 内部执行self._get_current_object()
  30. -3.2.2 _get_current_object()方法的源码如下:
  31. def _get_current_object(self):
  32. if not hasattr(self.__local, "__release_local__"):
  33. #self.__local() 在init的时候,实例化的,在init中:object.__setattr__(self, "_LocalProxy__local", local)
  34. # 用了隐藏属性
  35. #self.__local 实例化该类的时候传入的local(偏函数的内存地址:partial(_lookup_req_object, "request"))
  36. #加括号返回,就会执行偏函数,也就是执行_lookup_req_object,不需要传参数了
  37. #这个地方的返回值就是request对象(当此请求的request,没有乱)
  38. return self.__local()
  39. try:
  40. return getattr(self.__local, self.__name__)
  41. except AttributeError:
  42. raise RuntimeError("no object bound to %s" % self.__name__)
  43. -3.2.3 _lookup_req_object函数源码如下:
  44. def _lookup_req_object(name):
  45. #name是'request'字符串
  46. #top方法是把第二步中放入的ctx取出来,因为都在一个线程内,当前取到的就是当次请求的ctx对象
  47. top = _request_ctx_stack.top
  48. if top is None:
  49. raise RuntimeError(_request_ctx_err_msg)
  50. #通过反射,去ctx中把request对象返回
  51. return getattr(top, name)
  52. -3.2.4 所以:print(request) 实质上是在打印当此请求的request对象的__str__
  53. -4 如果在视图函数中使用request对象,比如:print(request.method):实质上是取到当次请求的reuquest对象的method属性
  54. -5 最终,请求结束执行: ctx.auto_pop(error),把ctx移除掉
  55. 其他的东西:
  56. -session:
  57. -请求来了opensession
  58. -ctx.push()---->也就是RequestContext类的push方法的最后的地方:
  59. if self.session is None:
  60. #self是ctx,ctx中有个app就是flask对象, self.app.session_interface也就是它:SecureCookieSessionInterface()
  61. session_interface = self.app.session_interface
  62. self.session = session_interface.open_session(self.app, self.request)
  63. if self.session is None:
  64. #经过上面还是None的话,生成了个空session
  65. self.session = session_interface.make_null_session(self.app)
  66. -请求走了savesession
  67. -response = self.full_dispatch_request() 方法内部:执行了before_first_requestbefore_request,视图函数,after_requestsavesession
  68. -self.full_dispatch_request()---->执行:self.finalize_request(rv)-----》self.process_response(response)----》最后:self.session_interface.save_session(self, ctx.session, response)
  69. -请求扩展相关
  70. before_first_requestbefore_requestafter_request依次执行
  71. -flask有一个请求上下文,一个应用上下文
  72. -ctx:
  73. -是:RequestContext对象:封装了requestsession
  74. -调用了:_request_ctx_stack.push(self)就是把:ctx放到了那个位置
  75. -app_ctx:
  76. -是:AppContext(self) 对象:封装了当前的appg
  77. -调用 _app_ctx_stack.push(self) 就是把:app_ctx放到了那个位置
  78. -g是个什么鬼?
  79. 专门用来存储用户信息的g对象,g的全称的为global
  80. g对象在一次请求中的所有的代码的地方,都是可以使用的
  81. -代理模式
  82. -requestsession就是代理对象,用的就是代理模式

2 wtforms(了解)

  1. # django 有forms组件
  2. - 生成前端模板
  3. - 校验数据
  4. - 渲染错误信息
  5. # flask 中使用第三方的wtforms 实现像django的forms一样的功能
  6. -第一步:导入,定义一个类,继承forms
  7. -第二步:模板中, for循环生成模板
  8. -第三步:视图函数中,使用form校验数据
  9. -wtforms是前后端结合使用的。前后端分离校验数据会使用别的第三方模块。
  10. # py代码
  11. from flask import Flask, render_template, request, redirect
  12. from wtforms import Form
  13. from wtforms.fields import simple
  14. from wtforms import validators
  15. from wtforms import widgets
  16. app = Flask(__name__, template_folder='templates')
  17. app.debug = True
  18. class LoginForm(Form):
  19. # 字段(内部包含正则表达式)
  20. name = simple.StringField(
  21. label='用户名',
  22. validators=[
  23. validators.DataRequired(message='用户名不能为空.'),
  24. validators.Length(min=6, max=18, message='用户名长度必须大于%(min)d且小于%(max)d')
  25. ],
  26. widget=widgets.TextInput(), # 页面上显示的插件
  27. render_kw={'class': 'form-control'}
  28. )
  29. # 字段(内部包含正则表达式)
  30. pwd = simple.PasswordField(
  31. label='密码',
  32. validators=[
  33. validators.DataRequired(message='密码不能为空.'),
  34. validators.Length(min=8, message='用户名长度必须大于%(min)d'),
  35. validators.Regexp(regex="^(?=.*[a-z])(?=.*[A-Z])(?=.*\d)(?=.*[$@$!%*?&])[A-Za-z\d$@$!%*?&]{8,}",
  36. message='密码至少8个字符,至少1个大写字母,1个小写字母,1个数字和1个特殊字符')
  37. ],
  38. widget=widgets.PasswordInput(),
  39. render_kw={'class': 'form-control'}
  40. )
  41. @app.route('/login', methods=['GET', 'POST'])
  42. def login():
  43. if request.method == 'GET':
  44. form = LoginForm()
  45. return render_template('login.html', form=form)
  46. else:
  47. form = LoginForm(formdata=request.form)
  48. if form.validate():
  49. print('用户提交数据通过格式验证,提交的值为:', form.data)
  50. else:
  51. print(form.errors)
  52. return render_template('login.html', form=form)
  53. if __name__ == '__main__':
  54. app.run()
  55. # html代码
  56. <!DOCTYPE html>
  57. <html lang="en">
  58. <head>
  59. <meta charset="UTF-8">
  60. <title>Title</title>
  61. </head>
  62. <body>
  63. <h1>登录</h1>
  64. <form method="post" novalidate>
  65. <p>{{form.name.label}}: {{form.name}} {{form.name.errors[0] }}</p>
  66. <p>{{form.pwd.label}} {{form.pwd}} {{form.pwd.errors[0] }}</p>
  67. <input type="submit" value="提交">
  68. </form>
  69. </body>
  70. </html>
  1. # 1 为什么有了gil锁还要互斥锁
  2. # 2 进程,线程和协程
  3. 代码如何实现
  4. 你在哪里用过
  5. # 3 什么是鸭子类型
  6. # 看flask 源码---》 1.1.4 版本

补充

  1. # 进程:进程是资源分配的最小单位,一个应用程序运行,至少会开启一个进程
  2. # 线程:线程是cpu调度的最小单位,cpu执行的最小单位
  3. # 协程:单线程下实现并发,代码层面遇到io,自己切换

【flask】flask请求上下文分析 threading.local对象 偏函数 flask1.1.4生命执行流程 wtforms的更多相关文章

  1. flask上下文管理之threading.local

    Flask之上下文管理 知识储备之问题情境: request中的参数: 单进程单线程 单进程多线程-->reqeust 会因为多个请求,数据发生错乱.--->可以基于threading.l ...

  2. Flask补充--threading.local对象

    目录 Local 局部变量 全局变量 使用threading.local() 自定义threading.local 函数版 面向对象版 通过setattr和getattr实现 每个对象有自己的存储空间 ...

  3. flask上下文管理相关 - threading.local 以及原理剖析

    threading.local 面向对象相关: setattr/getattr class Foo(object): pass obj = Foo() obj.x1 = 123 # object.__ ...

  4. python 全栈开发,Day139(websocket原理,flask之请求上下文)

    昨日内容回顾 flask和django对比 flask和django本质是一样的,都是web框架. 但是django自带了一些组件,flask虽然自带的组件比较少,但是它有很多的第三方插件. 那么在什 ...

  5. threading.local学习

    多线程抢占问题 import time import threading obj = 5 def task(arg): global obj obj = arg time.sleep(1) print ...

  6. flask之请求与响应、闪现(阅后即焚)、请求扩展(before,after)、中间件、LOCAL对象、偏函数、

    目录 1.flask请求与响应 2.闪现 3.请求扩展 4.中间件 5.LOCAL对象 6.偏函数 templates 1.flask请求与响应 from flask import Flask,req ...

  7. flask 源码专题(十一):LocalStack和Local对象实现栈的管理

    目录 04 LocalStack和Local对象实现栈的管理 1.源码入口 1. flask源码关于local的实现 2. flask源码关于localstack的实现 3. 总结 04 LocalS ...

  8. 04 flask源码剖析之LocalStack和Local对象实现栈的管理

    04 LocalStack和Local对象实现栈的管理 目录 04 LocalStack和Local对象实现栈的管理 1.源码入口 1. flask源码关于local的实现 2. flask源码关于l ...

  9. 线程锁、threading.local(flask源码中用的到)、线程池、生产者消费者模型

    一.线程锁 线程安全,多线程操作时,内部会让所有线程排队处理.如:list/dict/Queue 线程不安全 + 人(锁) => 排队处理 1.RLock/Lock:一次放一个 a.创建10个线 ...

  10. flask框架(七)——蓝图、请求上下文、g对象、信号、flask_session

    蓝图 作用:对程序进行目录结构划分 不使用蓝图情况下,自己分文件 目录结构: -templates -views -__init__.py -user.py -order.py -app.py app ...

随机推荐

  1. springCore完整学习教程2,入门级别

    上集说到:2. 3,咱们从2.3集开始 2. Externalized Configuration 2.3. External Application Properties Spring Boot会自 ...

  2. [USACO2022OPEN S] COW Operations S

    题目描述 Bessie 找到了一个长度不超过 \(2 \cdot 10^5\) 且仅包含字符 'C','O' 和 'W' 的字符串 \(s\).她想知道是否可以使用以下操作将该字符串变为单个字母 'C ...

  3. Mybatis-Flex之基础搭建

    1.是什么? MyBatis-Flex 是一个优雅的 MyBatis 增强框架,它非常轻量.同时拥有极高的性能与灵活性.我们可以轻松的使用 Mybaits-Flex 链接任何数据库,其内置的Query ...

  4. 加速计算,为何会成为 AI 时代的计算力“新宠” 审核中

    随着科技的发展,处理大量数据和进行复杂计算的需求越来越高,人工智能.大数据和物联网等领域更是如此,传统的计算方式已经无法满足这些需求.因此,加速计算作为一种现代计算方式,成了必要的手段.加速计算具有前 ...

  5. Halo新年灯笼-halonewyaer

    title: Halo新年灯笼 date: 2021-12-29 15:49:34.665 updated: 2022-03-10 16:00:53.189 url: https://www.yby6 ...

  6. [Luogu 4912 帕秋莉的魔法] 题解报告

    算法:DP, 背包,动态规划 简化版题目: 给定 \(n\) 个物品,物品的价值为 \(v_1 - v_n\),物品的体积为 \(w_1 - w_n\).需要选择一些物品,使它们的体积和为 \(V\) ...

  7. Java 并发编程(六)并发容器和框架

    传统 Map 的局限性 HashMap JDK 1.7 的 HashMap JDK 1.7 中 HashMap 的实现只是单纯的 "数组 + 链表 " 的组合方式,具体的组成如下: ...

  8. Head First 的学习之道

    <Head First 设计模式>是一本好书,正如书的封面上说的那样,这是一本重视大脑的学习指南.里面提到了一些学习方法,可以尝试下,看看哪些对你有用: 1. 慢一点,理解的越多,需要记得 ...

  9. 常见的Java中SQL注解的用法

    @Select:用于查询操作,标注在方法上,指定相应的SQL查询语句. @Select("SELECT * FROM table_name WHERE condition") Li ...

  10. html2pdf

    nodejs 生成pdf比较靠谱,使用chrome核心渲染: puppeteer / phantom 爬虫都好用 good