一文读懂flask

一文读懂flask

在实际工作中遇到了需要在通过gunicorn启动flask应用,并在gunicorn启动的每个woker之前为每个woker初始化ros节点信息,但在改造的过程中遇到通过worker数指定节点名称时多次初始化节点时只有一个woker的节点能够初始化成功,因此激发了我去了解gunicorn是如何启动flask应用?并且收到请求后事如何将请求分发给flask应用?以及是如何做到多个woker同时监听同一个端口,又是如何做到只有一个worker处理请求的?接下来会通过分析flaskgunicorn的源码来进一步了解这些问题都是如何解决的,在这个过程中将会直观地看到flask应用是如何为每一个请求维护请求上下文、应用上下文的生命周期,同时也会介绍flask是如何调用钩子函数,包括before_first_requestbefore_requestafter_requestteardown_requestteardown_appcontext的调用时机。

1、flask应用启动过程-开发模式

1.1 flask的入口函数(run方法)

flask自带了一个开发过程中使用的wsgi服务,在启动的时候通常是实例化Flask类,然后调用实例的run()方法,在这里就不去关注加载配置文件的过程, 我们看一下run方法具体执行了哪些步骤:

class Flask(Scaffold):"""The flask object implements a WSGI application and acts as the centralobject.  It is passed the name of the module or package of theapplication.  Once it is created it will act as a central registry forthe view functions, the URL rules, template configuration and much more.The name of the package is used to resolve resources from inside thepackage or the folder the module is contained in depending on if thepackage parameter resolves to an actual python package (a folder withan :file:`__init__.py` file inside) or a standard module (just a ``.py`` file).For more information about resource loading, see :func:`open_resource`."""......def run(self,host: t.Optional[str] = None,port: t.Optional[int] = None,debug: t.Optional[bool] = None,load_dotenv: bool = True,**options: t.Any,) -> None:# Change this into a no-op if the server is invoked from the# command line. Have a look at cli.py for more information.............server_name = self.config.get("SERVER_NAME")sn_host = sn_port = Noneif server_name:sn_host, _, sn_port = server_name.partition(":")  # 根据server name获取监听的主机和端口if not host:if sn_host:host = sn_hostelse:host = "127.0.0.1"if port or port == 0:port = int(port)elif sn_port:port = int(sn_port)else:port = 5000  # 默认端口,未指定端口且server name中未获取到端口信息时使用默认端口options.setdefault("use_reloader", self.debug)options.setdefault("use_debugger", self.debug)options.setdefault("threaded", True)cli.show_server_banner(self.env, self.debug, self.name, False)  # 打印flask banner信息from werkzeug.serving import run_simpletry:run_simple(t.cast(str, host), port, self, **options)  # 通过该方法启动wsgi服务端finally:# reset the first request information if the development server# reset normally.  This makes it possible to restart the server# without reloader and that stuff from an interactive shell.self._got_first_request = False

由以上代码可以看出,在flask的run方法中,主要是根据配置项获取启动server所需的必要参数,主要包含以下几个步骤:

  • 根据配置文件获取server_name配置项,并根据配置项获取host和port信息
  • 根据是否指定host和port信息,来获取是否使用默认端口和监听本地地址
  • 通过调用werkzeug.serving包中的run_simple方法启动server

1.2 启动服务端(run_simple方法)

在这里我们需要关注一下run_simple的入参和处理过程,以下是run_simple方法的具体实现过程:

def run_simple(hostname: str,port: int,application: "WSGIApplication",use_reloader: bool = False,use_debugger: bool = False,use_evalex: bool = True,extra_files: t.Optional[t.Iterable[str]] = None,exclude_patterns: t.Optional[t.Iterable[str]] = None,reloader_interval: int = 1,reloader_type: str = "auto",threaded: bool = False,processes: int = 1,request_handler: t.Optional[t.Type[WSGIRequestHandler]] = None,static_files: t.Optional[t.Dict[str, t.Union[str, t.Tuple[str, str]]]] = None,passthrough_errors: bool = False,ssl_context: t.Optional[_TSSLContextArg] = None,
) -> None:......  # 省略了端口校验、静态文件加载、debug模式加载等过程srv = make_server(hostname,port,application,threaded,processes,request_handler,passthrough_errors,ssl_context,fd=fd,)  # 工厂函数创建相应的server,主要包括ThreadedWSGIServer,ForkingWSGIServer,BaseWSGIServer三种服务端,在这里我们主要分析BaseWSGIServer的实现流程if not is_running_from_reloader():srv.log_startup()_log("info", _ansi_style("Press CTRL+C to quit", "yellow"))if use_reloader:from ._reloader import run_with_reloaderrun_with_reloader(srv.serve_forever,extra_files=extra_files,exclude_patterns=exclude_patterns,interval=reloader_interval,reloader_type=reloader_type,)else:srv.serve_forever()

这里BaseWSGIServer存在一个继承关系:

BaseWSGIServer->server.HTTPServer->socketserver.TCPServer->socketserver.BaseServer

在make_server中根据配置项最终创建了一个BaseWSGIServer的实例,所以我们直接看BaseWSGIServer__init__方法:

class BaseWSGIServer(HTTPServer):"""A WSGI server that that handles one request at a time.Use :func:`make_server` to create a server instance."""multithread = Falsemultiprocess = Falserequest_queue_size = LISTEN_QUEUEdef __init__(self,host: str,port: int,app: "WSGIApplication",handler: t.Optional[t.Type[WSGIRequestHandler]] = None,passthrough_errors: bool = False,ssl_context: t.Optional[_TSSLContextArg] = None,fd: t.Optional[int] = None,) -> None:if handler is None:handler = WSGIRequestHandler  # 指定请求处理类# If the handler doesn't directly set a protocol version and# thread or process workers are used, then allow chunked# responses and keep-alive connections by enabling HTTP/1.1.if "protocol_version" not in vars(handler) and (self.multithread or self.multiprocess):handler.protocol_version = "HTTP/1.1"self.host = hostself.port = portself.app = appself.passthrough_errors = passthrough_errorsself.address_family = address_family = select_address_family(host, port)server_address = get_sockaddr(host, int(port), address_family)# Remove a leftover Unix socket file from a previous run. Don't# remove a file that was set up by run_simple.if address_family == af_unix and fd is None:server_address = t.cast(str, server_address)if os.path.exists(server_address):os.unlink(server_address)# Bind and activate will be handled manually, it should only# happen if we're not using a socket that was already set up.super().__init__(server_address,  # type: ignore[arg-type]handler,bind_and_activate=False,)if fd is None:# No existing socket descriptor, do bind_and_activate=True.try:self.server_bind()  # 绑定server,监听指定socketself.server_activate()  # 开启请求监听except BaseException:self.server_close()raiseelse:# Use the passed in socket directly.self.socket = socket.fromfd(fd, address_family, socket.SOCK_STREAM)self.server_address = self.socket.getsockname()if address_family != af_unix:# If port was 0, this will record the bound port.self.port = self.server_address[1]if ssl_context is not None:if isinstance(ssl_context, tuple):ssl_context = load_ssl_context(*ssl_context)elif ssl_context == "adhoc":ssl_context = generate_adhoc_ssl_context()self.socket = ssl_context.wrap_socket(self.socket, server_side=True)self.ssl_context: t.Optional["ssl.SSLContext"] = ssl_contextelse:self.ssl_context = Noneclass TCPServer(BaseServer):address_family = socket.AF_INETsocket_type = socket.SOCK_STREAMrequest_queue_size = 5allow_reuse_address = Falsedef __init__(self, server_address, RequestHandlerClass, bind_and_activate=True):"""Constructor.  May be extended, do not override."""BaseServer.__init__(self, server_address, RequestHandlerClass)self.socket = socket.socket(self.address_family,self.socket_type)if bind_and_activate:try:self.server_bind()self.server_activate()except:self.server_close()raisedef server_bind(self):"""Called by constructor to bind the socket.May be overridden."""if self.allow_reuse_address:self.socket.setsockopt(socket.SOL_SOCKET, socket.SO_REUSEADDR, 1)self.socket.bind(self.server_address)  # 绑定socketself.server_address = self.socket.getsockname()def server_activate(self):"""Called by constructor to activate the server.May be overridden."""self.socket.listen(self.request_queue_size)  # 监听请求

从代码中可以看出,在BaseWSGIServer类实例化的过程中主要执行了一下几个步骤:

  • 指定请求处理类,在不指定的情况下默认使用WSGIRequestHandler

  • 绑定server,监听指定socket

  • 开启请求监听

1.3 启动请求处理循环(srv.serve_forever())

在监听socket以后,需要启动一个无线循环来处理收到的请求,在flask中是通过调用BaseServerserve_forever()方法来实现的,接下来看一下这个方法的具体实现:

class BaseServer:......def serve_forever(self, poll_interval=0.5):"""Handle one request at a time until shutdown.Polls for shutdown every poll_interval seconds. Ignoresself.timeout. If you need to do periodic tasks, do them inanother thread."""self.__is_shut_down.clear()try:# XXX: Consider using another file descriptor or connecting to the# socket to wake this up instead of polling. Polling reduces our# responsiveness to a shutdown request and wastes cpu at all other# times.with _ServerSelector() as selector:selector.register(self, selectors.EVENT_READ)  # 通过操作系统的select机制注册上述监听的socket的文件描述符,并指定监听的事件while not self.__shutdown_request:ready = selector.select(poll_interval)  # 收到指定的事件,未收到事件时将在这里开启无限循环监听# bpo-35017: shutdown() called during select(), exit immediately.if self.__shutdown_request:breakif ready:self._handle_request_noblock()  # 开始处理请求self.service_actions()finally:self.__shutdown_request = Falseself.__is_shut_down.set()

serve_forever方法中主要执行了下面三个步骤,用来实现监听收到请求的事件,并调用相应的方法处理收到的请求

  • 通过操作系统的select机制注册上述监听的socket的文件描述符,并指定监听的事件

  • 收到指定的事件

  • 收到ready信号后,开始处理请求

在开发模式中,flask是通过系统提供的select系统调用监听socket,当收到注册的事件后,触发调用请求处理函数来同步处理收到的请求,到这里为止flask便完成了服务启动过程,接下来一起看一下flask是如何处理收到的请求的

2.开发模式下flask是如何处理收到的请求的

2.1 收到请求

在了解了flask是如何启动的,并且完成端口监听与事件注册以后,就是等待收到请求,在收到注册的事件后,会通过调用_handle_request_noblock方法来处理收到的请求,接下来看一下这个方法的具体实现

class BaseServer:......def _handle_request_noblock(self):"""Handle one request, without blocking.I assume that selector.select() has returned that the socket isreadable before this function was called, so there should be no risk ofblocking in get_request()."""try:request, client_address = self.get_request()  # 获取收到的请求信息和请求来源的客户端地址except OSError:returnif self.verify_request(request, client_address):  # 校验请求信息和client信息try:self.process_request(request, client_address)  # 处理请求except Exception:self.handle_error(request, client_address)self.shutdown_request(request)except:self.shutdown_request(request)raiseelse:self.shutdown_request(request)......def verify_request(self, request, client_address):"""Verify the request.  May be overridden.Return True if we should proceed with this request."""return Truedef process_request(self, request, client_address):"""Call finish_request.Overridden by ForkingMixIn and ThreadingMixIn."""self.finish_request(request, client_address) # 调用方法实例化创建BaseWSGIServer对象时指定的请求处理类,开始处理请求self.shutdown_request(request)......def finish_request(self, request, client_address):"""Finish one request by instantiating RequestHandlerClass."""self.RequestHandlerClass(request, client_address, self)  # 实例化请求处理类class TCPServer(BaseServer):......def get_request(self):"""Get the request and client address from the socket.May be overridden."""return self.socket.accept()  # 调用socket的accept方法从网络栈中获取收到的请求数据

在这个方法中,主要实现了从网络栈中获取收到的请求信息,并通过实例化请求处理类WSGIRequestHandler来处理接收到的请求,那么接下来就开始进一步了解在这个类中是如何实现请求处理的,这个类也存在下面的继承关系

WSGIRequestHandler->BaseHTTPRequestHandler->socketserver.StreamRequestHandler->socketserver.BaseRequestHandler

首先从WSGIRequestHandler这个类的初始化过程来分析看做了那些动作,由于这些类只有最顶级的父类实现了__init__方法,所以从socketserver.BaseRequestHandler这个类开始

class BaseRequestHandler:def __init__(self, request, client_address, server):self.request = request  # 收到的请求信息self.client_address = client_address  # 请求的客户端地址self.server = server  # 这个参数是前面传递的BaseWSGIServer的实例self.setup()  # 初始化响应所需的对象信息,以及设置超时时间以及buffer大小等try:self.handle()  # 开始执行请求处理流程finally:self.finish()class StreamRequestHandler(BaseRequestHandler):......def setup(self):self.connection = self.requestif self.timeout is not None:self.connection.settimeout(self.timeout)if self.disable_nagle_algorithm:self.connection.setsockopt(socket.IPPROTO_TCP,socket.TCP_NODELAY, True)self.rfile = self.connection.makefile('rb', self.rbufsize)  # 创建读取请求信息的文件描述符if self.wbufsize == 0:  # 创建响应socket文件描述符对象self.wfile = _SocketWriter(self.connection)else:self.wfile = self.connection.makefile('wb', self.wbufsize)

在这段代码中主要做了一下几个步骤:

  • 创建读取请求信息的文件描述符
  • 初始化响应所需的对象信息,以及设置超时时间以及buffer大小等
  • 调用handle方法开始处理请求

2.2 请求处理过程

flask中具体执行处理请求的方法是WSGIRequestHandler这个请求处理类的handle方法,但是在这个方法的实现中又是直接调用的父类的同名方法,所以接下来我们一起看一下这两个方法的实现:

class WSGIRequestHandler(BaseHTTPRequestHandler):"""A request handler that implements WSGI dispatching."""......def handle(self) -> None:"""Handles a request ignoring dropped connections."""try:super().handle()except (ConnectionError, socket.timeout) as e:self.connection_dropped(e)except Exception as e:if self.server.ssl_context is not None and is_ssl_error(e):self.log_error("SSL error occurred: %s", e)else:raiseclass BaseHTTPRequestHandler(socketserver.StreamRequestHandler):......def handle_one_request(self):"""Handle a single HTTP request.You normally don't need to override this method; see the class__doc__ string for information on how to handle specific HTTPcommands such as GET and POST."""try:self.raw_requestline = self.rfile.readline(65537)  # 通过前面创建的文件描述符读取请求数据if len(self.raw_requestline) > 65536:self.requestline = ''self.request_version = ''self.command = ''self.send_error(HTTPStatus.REQUEST_URI_TOO_LONG)returnif not self.raw_requestline:self.close_connection = Truereturnif not self.parse_request():  # 解析请求数据,读取请求头、请求方法、路径等信息# An error code has been sent, just exitreturnmname = 'do_' + self.command  # 构建请求方法名称if not hasattr(self, mname):self.send_error(HTTPStatus.NOT_IMPLEMENTED,"Unsupported method (%r)" % self.command)returnmethod = getattr(self, mname)  # 获取处理相应请求的方法method()  # 调用方法处理请求self.wfile.flush() # 强制刷新响应描述符,发送响应信息except socket.timeout as e:#a read or a write timed out.  Discard this connectionself.log_error("Request timed out: %r", e)self.close_connection = Truereturndef handle(self):"""Handle multiple requests if necessary."""self.close_connection = Trueself.handle_one_request()while not self.close_connection:self.handle_one_request()

BaseHTTPRequestHandlerhandle方法中,可以看到最终调用的是handle_one_request方法,在这个方法中又主要执行了一下几个步骤,用来处理请求并发送响应

  • 通过文件描述符读取请求数据
  • 解析请求数据,读取请求头、请求方法、路径等信息
  • 构建请求方法名称
  • 获取处理相应请求的方法
  • 调用方法处理请求
  • 强制刷新响应描述符,发送响应信息

这个方法中还隐藏了一个过程就是如何获取到处理请求的方法,从构建其你去的方法名称可以看出,这里是通过一个固定的格式do_method的方式构建的,然后通过getattr方法获取处理的方法,而在WSGIRequestHandler类中又通过定义了一个__getattr__方法来处理调用这个方法时执行的逻辑,具体代码如下

class WSGIRequestHandler(BaseHTTPRequestHandler):"""A request handler that implements WSGI dispatching."""......def run_wsgi(self) -> None:......def execute(app: "WSGIApplication") -> None:application_iter = app(environ, start_response) # 这里的app便是我们自己创建的Flask应用实例,通过调用flask实例的__call__方法来处理请求try:for data in application_iter:write(data)if not headers_sent:write(b"")if chunk_response:self.wfile.write(b"0\r\n\r\n")finally:if hasattr(application_iter, "close"):application_iter.close()  # type: ignoretry:execute(self.server.app)except (ConnectionError, socket.timeout) as e:self.connection_dropped(e, environ)except Exception as e:if self.server.passthrough_errors:raiseif status_sent is not None and chunk_response:self.close_connection = Truetry:# if we haven't yet sent the headers but they are set# we roll back to be able to set them again.if status_sent is None:status_set = Noneheaders_set = Noneexecute(InternalServerError())except Exception:passfrom .debug.tbtools import DebugTracebackmsg = DebugTraceback(e).render_traceback_text()self.server.log("error", f"Error on request:\n{msg}")......def __getattr__(self, name: str) -> t.Any:# All HTTP methods are handled by run_wsgi.if name.startswith("do_"):return self.run_wsgi# All other attributes are forwarded to the base class.return getattr(super(), name)

根据这段代码可以看到,在调用getattr方法时,如果方法名称是以do_开头的,返回的是run_wsgi方法,而在这个方法中又是通过调用flask应用实例的__call__方法来处理请求,并响应结果,那么接下来进入到Flask的这个方法区看看到底执行了那些过程

2.3 Flask调用注册的路由处理请求

Flask这个类中定义了一个__call__方法,在调用对象时,会自动执行这个方法,而在这个方法中,又返回了wsgi_app方法的调用结果,以下是代码实现:

class Flask(Scaffold):......def wsgi_app(self, environ: dict, start_response: t.Callable) -> t.Any:"""The actual WSGI application. This is not implemented in:meth:`__call__` so that middlewares can be applied withoutlosing a reference to the app object. Instead of doing this::app = MyMiddleware(app)It's a better idea to do this instead::app.wsgi_app = MyMiddleware(app.wsgi_app)Then you still have the original application object around andcan continue to call methods on it... versionchanged:: 0.7Teardown events for the request and app contexts are calledeven if an unhandled error occurs. Other events may not becalled depending on when an error occurs during dispatch.See :ref:`callbacks-and-errors`.:param environ: A WSGI environment.:param start_response: A callable accepting a status code,a list of headers, and an optional exception context tostart the response."""ctx = self.request_context(environ)error: t.Optional[BaseException] = Nonetry:try:ctx.push()response = self.full_dispatch_request()except Exception as e:error = eresponse = self.handle_exception(e)except:  # noqa: B001error = sys.exc_info()[1]raisereturn response(environ, start_response)finally:if self.should_ignore_error(error):error = Nonectx.auto_pop(error)  # 销毁当前请求上下文和应用上下文信息,并会依次执行通过teardown_request、teardown_appcontext装饰器装饰过的方法def __call__(self, environ: dict, start_response: t.Callable) -> t.Any:"""The WSGI server calls the Flask application object as theWSGI application. This calls :meth:`wsgi_app`, which can bewrapped to apply middleware."""return self.wsgi_app(environ, start_response)

从这个方法中可以看到,在开始处理请求之前,会为当前请求创建一个请求上下文,并通过_request_ctx_stack这个栈实现上下文的线程隔离,同时在将请求上下文存储之前有人会未当前请求创建一个应用上下文对象,存储到_implicit_app_ctx_stack这个栈中具体实现请感兴趣的小伙伴自行翻看源码进一步了解,上下文创建完成后,随机开始调用full_dispatch_request这个方法来处理请求,在处理完成后会销毁当前上下文信息,并会依次执行通过teardown_requestteardown_appcontext装饰器装饰过的方法,接下来看看这个方法又是怎么处理请求的

class Flask(Scaffold):......def dispatch_request(self) -> ResponseReturnValue:"""Does the request dispatching.  Matches the URL and returns thereturn value of the view or error handler.  This does not have tobe a response object.  In order to convert the return value to aproper response object, call :func:`make_response`... versionchanged:: 0.7This no longer does the exception handling, this code wasmoved to the new :meth:`full_dispatch_request`."""req = _request_ctx_stack.top.requestif req.routing_exception is not None:self.raise_routing_exception(req)rule = req.url_rule# if we provide automatic options for this URL and the# request came with the OPTIONS method, reply automaticallyif (getattr(rule, "provide_automatic_options", False)and req.method == "OPTIONS"):return self.make_default_options_response()# otherwise dispatch to the handler for that endpointreturn self.ensure_sync(self.view_functions[rule.endpoint])(**req.view_args)def full_dispatch_request(self) -> Response:"""Dispatches the request and on top of that performs requestpre and postprocessing as well as HTTP exception catching anderror handling... versionadded:: 0.7"""self.try_trigger_before_first_request_functions()  # 调用此方法,依次执行通过before_first_request装饰器装饰过的方法,会在接收到第一个请求的时候执行try:request_started.send(self)rv = self.preprocess_request()  # 依次执行通过before_request装饰器装饰过的方法if rv is None:rv = self.dispatch_request()  # 通过view_functions这个map记录注册过的路由信息,key为注册的路径,value方法,调用当前请求路径对应的视图方法处理请求except Exception as e:rv = self.handle_user_exception(e)return self.finalize_request(rv)  # 构建响应对象,依次执行通过after_request装饰器装饰过的方法,并返回响应对象

到这里为止flask处理请求的完整过程便已经结束,在这个方法里面主要做了以下几个步骤:

  • 依次执行通过before_first_request装饰器装饰过的方法,会在接收到第一个请求的时候执行
  • 依次执行通过before_request装饰器装饰过的方法
  • 通过view_functions这个map记录注册过的路由信息,key为注册的路径,value方法,调用当前请求路径对应的视图方法处理请求
  • 构建响应对象,依次执行通过after_request装饰器装饰过的方法,并返回响应对象

本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若转载,请注明出处:http://www.mzph.cn/web/51529.shtml

如若内容造成侵权/违法违规/事实不符,请联系多彩编程网进行投诉反馈email:809451989@qq.com,一经查实,立即删除!

相关文章

鸿蒙开发 数组改变,ui渲染没有刷新

问题描述: 数组push, 数组长度改变,ui也没有刷新 打印出了数组 console.log(this.toDoData.map(item > ${item.name}).join(, ), this.toDoData.length) 原代码: Text().fontSize(36).margin({ right: 40 }).onClick(() > {TextPicker…

MySQL 数据库深度解析:安装、语法与高级查询实战

一、引言 在现代软件开发和数据管理领域中,MySQL 数据库凭借其高效性、稳定性、开源性以及广泛的适用性,成为了众多开发者和企业的首选。无论是小型项目还是大型企业级应用,MySQL 都能提供可靠的数据存储和管理解决方案。本文将深入探讨 MyS…

uni-app - - - - - 使用uview-plus详细步骤

uni-app - - - - - 使用uview-plus详细步骤 1. 使用HbuilderX创建空白项目2. 安装插件3. uview-plus配置使用3.1 main.js配置3.2 uni.scss配置3.3 App.vue配置3.4 pages.json 4. 重启Hbuilderx 1. 使用HbuilderX创建空白项目 2. 安装插件 工具 > 插件安装 > 前往插件市场…

“精读”怎么翻译?

中文版 “Intensive reading” 是指一种深度阅读的方式,通常用于精读和分析文本的细节。与广泛阅读(extensive reading)不同,广泛阅读的目标是通过阅读大量的内容来提高整体的语言理解能力,而集中的阅读则注重理解单一…

Linux上安装Conda以管理Python环境

在Windows下装了Linux发行版Debian,以后不用来回开启VMware啦!并在Debian中安装了Conda,记录一下所需命令(其他版本如Ubuntu中安装是一样的命令)。 目录 1.WSL 2.安装Conda 3.Python环境配置 1.WSL Install WSL | Microsoft Learn 微软官网 ①以管理…

STM32(F103ZET6)第二十四课:IAP离线固件升级

目录 开发需求IAP介绍内部的内存分区1.内部FLASH划分2.内部数据读取3.数据写入与擦除4.具体升级函数 IAP更新升级步骤1.系统启动流程2.IAP启动流程详解3.整体设计流程4.Boot Loader的代码编写5.APP1代码编写(目前)6.APP2代码编写(待升级&…

WEB开发---使用HTML CSS开发网页实时显示当前日期和时间

自己刚开始学习html css知识&#xff0c;临时做个网页&#xff0c;实时显示当前日期和时间功能。 代码如下&#xff1a; test.html <!DOCTYPE html> <html lang"en"> <head><meta charset"UTF-8"><meta name"viewport&q…

python解释器[源代码层面]

1 PyDictObject 在c中STL中的map是基于 RB-tree平衡二元树实现&#xff0c;搜索的时间复杂度为O(log2n) Python中PyDictObject是基于散列表(散列函数)实现&#xff0c;搜索时间最优为O(1) 1.1 散列列表 问题&#xff1a;散列冲突&#xff1a;多个元素计算得到相同的哈希值 …

ARM 寻址方式(18)

立即寻址&#xff1a; 也叫作立即数寻址。 就是 立即数&#xff0c;本身就包含在了 指令当中。 举例&#xff1a; ADD R0, R0,#1 其中&#xff0c;#1 &#xff0c; 就是立即数&#xff0c;对于16进制的立即数&#xff0c; 需要在# 后加上 #0x. 寄存器寻址。 就是数据就在…

219. 存在重复元素 II【 力扣(LeetCode) 】

一、题目描述 给你一个整数数组 nums 和一个整数 k &#xff0c;判断数组中是否存在两个 不同的索引 i 和 j &#xff0c;满足 nums[i] nums[j] 且 abs(i - j) < k 。如果存在&#xff0c;返回 true &#xff1b;否则&#xff0c;返回 false 。 二、测试用例 示例 1&…

多模态论文学习8.28

系列文章目录 文章目录 系列文章目录LAMM: Label Alignment for Multi-Modal Prompt Learning学习1、论文理解1、研究背景2、论文贡献3、方法框架4、研究思路5、实验6、限制 LAMM: Label Alignment for Multi-Modal Prompt Learning学习 1、论文理解 VL模型和下游任务之间的类…

单调栈,LeetCode 2289. 使数组按非递减顺序排列

一、题目 1、题目描述 给你一个下标从 0 开始的整数数组 nums 。在一步操作中&#xff0c;移除所有满足 nums[i - 1] > nums[i] 的 nums[i] &#xff0c;其中 0 < i < nums.length 。 重复执行步骤&#xff0c;直到 nums 变为 非递减 数组&#xff0c;返回所需执行的…

Amos百度云下载与安装 附图文安装教程

如大家所了解的&#xff0c;Amos是一款经常被运用在社会科学研究中的数据分析软件&#xff0c;尤其广泛用于人文社会科学领域的各种研究中。运用Amos&#xff0c;可以帮助研究人员使用结构方程模型 (SEM) 对他们收集到的数据进行分析与解释。 自用Amos 24安装包&#xff0c;可按…

数据结构与算法(循环链表,双向链表)

循环链表 最后一个元素指向首元素 带尾指针的循环链表合并 双向链表 双向链表:在单链表的每个结点里再增加一个指向其直接前驱的指针 域 prior &#xff0c;这样链表中就形成了有两个方向不同的链&#xff0c;故称为双向链表 双向链表插入操作 思路 代码 删除操作 思路 代…

一个很大的文件,文件的每一行是一个很大的数字,如果给你一个单机,内存比较小,存不了这么大的文件,但是硬盘是无限大的,如何对文件做一个排序输出

对于需要排序的大文件,尤其是当文件大小超过了可用内存时,可以采用外部排序算法。这里我描述一种基于归并排序思想的外部排序方法,它将大文件分割成多个小文件,在每个小文件上进行排序,然后再将这些排序好的小文件合并成一个有序的大文件。 以下是具体的步骤: 步骤 1: …

CTFHub SSRF靶场通关攻略(6-11)

FastCGI协议 首先写一个php的一句话木马&#xff0c;并进行base64编码 <?php eval($_POST[cmd]);?> 编码完成后把他写入shell.php文件中 echo "PD9waHAgQGV2YWwoJF9QT1NUW2NtZF0pOz8" | base64 -d > shell.php 使用Gopherus工具生成payload: 执命令 …

【工控】线扫相机小结

背景简介 我目前接触到的线扫相机有两种形式: 无采集卡,数据通过网线传输。 配备采集卡,使用PCIe接口。 第一种形式的数据通过网线传输,速度较慢,因此扫描和生成图像的速度都较慢,参数设置主要集中在相机本身。第二种形式的相机配备采集卡,通常速度更快,但由于相机和…

Clickhouse集群化(三)集群化部署

1. 准备 clickhouse支持副本和分片的能力&#xff0c;但是自身无法实现需要借助zookeeper或者clickhouse-keeper来实现不同节点之间数据同步&#xff0c;同时clickhouse的数据是最终一致性 。 2. Zookeeper 副本的写入流程 没有主从概念 平等地位 互为副本 2.1. 部署zookeep…

Mysql高级 [Linux版] 性能优化 数据库系统配置优化 和 MySQL的执行顺序 以及 Mysql执行引擎介绍

数据库系统配置优化 1、定义 数据库是基于操作系统的&#xff0c;目前大多数MySQL都是安装在linux系统之上&#xff0c;所以对于操作系统的一些参数配置也会影响到MySQL的性能&#xff0c;下面就列出一些常用的系统配置。 2、优化配置参数-操作系统 优化包括操作系统的优化及My…

C# UserControl、Dockpanel和DockContent、Cursor、

一、UserControl类 UserControl 是 .NET 中的一个基类&#xff0c;用于创建自定义控件&#xff0c;主要用于 Windows Forms 和 WPF。通过继承 UserControl&#xff0c;你可以设计和实现具有特定界面和功能的控件组件。UserControl 允许你将多个标准控件组合在一起&#xff0c;…