Web框架与Django简介

Web框架与Django简介

一、Web应用的组成

我们为了开发一款Web软件首先要了解什么才是Web应用软件呢?

对于传统的应用软件来说,基本都是部署单机使用,而Web应用软件就不一样,Web应用软件是基于B/S架构的,B和S都在不同的计算机上,并且基于网络通信,所以B和S的本质就是套接字,B指的是浏览器并且无需开发,我们需要开发的是S端。

我们在开发套接字服务端S的思路应该是这样:

1.接收客户端B发来的信息并且加以解析

2. 根据解析后的结果,加以判断,生成/获取用户想要的数据

3.返回数据给套接字客户端B

其中上述1和3属于套接字的底层通信,而2则属于应用程序的逻辑,所以我们通常说S端的开发由两大部分构成:server和application

1.Sever:称为服务器程序,指的是套接字的通信相关事宜,包含1和3

2.application:应用程序,指的是应用程序的逻辑,包含2

综上所述,一个完整的Web应用如下图所示:

  

二、开发一个Web应用

1、S端的简单开发与Http协议 

按照上述思路,开发S端如下

# S端
import socketdef make_server(ip, port, app):  # 代表server# 处理套接字通信相关事宜sock = socket.socket()sock.bind((ip, port))sock.listen(5)print('Starting development server at http://%s:%s/' %(ip,port))while True:conn, addr = sock.accept()# 1、接收浏览器发来的请求信息recv_data = conn.recv(1024)# print(recv_data.decode('utf-8'))# 2、将请求信息直接转交给application处理,得到返回值res = app(recv_data)# 3、向浏览器返回消息(此处并没有按照http协议返回)conn.send(res)conn.close()def app(environ):  # 代表application# 处理业务逻辑return b'hello world'if __name__ == '__main__':make_server('127.0.0.1', 8008, app)  # 在客户端浏览器输入:http://127.0.0.1:8008 会报错(注意:请使用谷歌浏览器)

目前S端已经可以正常接收浏览器发来的请求消息了,但是浏览器在接收到S端回复的响应消息b'hello world'时却无法正常解析 ,因为浏览器与S端之间收发消息默认使用的应用层协议是HTTP,浏览器默认会按照HTTP协议规定的格式发消息,而S端也必须按照HTTP协议的格式回消息才行。

2、结合http协议改进S端

处理HTTP协议的请求消息,并按照HTTP协议的格式回复消息

# S端
import socketdef make_server(ip, port, app): # 代表server# 处理套接字通信相关事宜sock = socket.socket()sock.bind((ip, port))sock.listen(5)print('Starting development server at http://%s:%s/' %(ip,port))while True:conn, addr = sock.accept()# 1、接收并处理浏览器发来的请求信息# 1.1 接收浏览器发来的http协议的消息recv_data = conn.recv(1024)# 1.2 对http协议的消息加以处理,简单示范如下ll=recv_data.decode('utf-8').split('\r\n')head_ll=ll[0].split(' ')environ={}environ['PATH_INFO']=head_ll[1]environ['method']=head_ll[0]# 2:将请求信息处理后的结果environ交给application,这样application便无需再关注请求信息的处理,可以更加专注于业务逻辑的处理res = app(environ)# 3:按照http协议向浏览器返回消息# 3.1 返回响应首行conn.send(b'HTTP/1.1 200 OK\r\n')# 3.2 返回响应头(可以省略)conn.send(b'Content-Type: text/html\r\n\r\n')# 3.3 返回响应体conn.send(res)conn.close()def app(environ): # 代表application# 处理业务逻辑return b'hello world'if __name__ == '__main__':make_server('127.0.0.1', 8008, app)

此时,重启S端后,再在客户端浏览器输入:http://127.0.0.1:8008 便可以看到正常结果hello world了。

3、返回Html

我们不仅可以回复hello world这样的普通字符,还可以夹杂html标签,浏览器在接收到消息后会对解析出的html标签加以渲染

# S端
import socketdef make_server(ip, port, app): sock = socket.socket()sock.bind((ip, port))sock.listen(5)print('Starting development server at http://%s:%s/' %(ip,port))while True:conn, addr = sock.accept()recv_data = conn.recv(1024)ll=recv_data.decode('utf-8').split('\r\n')head_ll=ll[0].split(' ')environ={}environ['PATH_INFO']=head_ll[1]environ['method']=head_ll[0]res = app(environ)conn.send(b'HTTP/1.1 200 OK\r\n')conn.send(b'Content-Type: text/html\r\n\r\n')conn.send(res)conn.close()def app(environ):# 返回html标签return b'<h1>hello web</h1><img src="https://www.baidu.com/img/bd_logo1.png"></img>'if __name__ == '__main__':make_server('127.0.0.1', 8008, app)

在上述函数app中,python代码与html代码耦合到一起,这是不合理的,我们应该将二者分离开,将html代码放入专门的文件中,于是我们新建timer.html文件,内容如下

<!DOCTYPE html>
<html>
<head><meta charset="UTF-8"><title>Title</title>
</head>
<body>
<h2>当前时间为:2020-02-02 20:20:20</h2>
</body>
</html>
S端程序如下
# S端
import socketdef make_server(ip, port, app): # 代表serversock = socket.socket()sock.bind((ip, port))sock.listen(5)print('Starting development server at http://%s:%s/' %(ip,port))while True:conn, addr = sock.accept()recv_data = conn.recv(1024)ll=recv_data.decode('utf-8').split('\r\n')head_ll=ll[0].split(' ')environ={}environ['PATH_INFO']=head_ll[1]environ['method']=head_ll[0]res = app(environ)conn.send(b'HTTP/1.1 200 OK\r\n')conn.send(b'Content-Type: text/html\r\n\r\n')conn.send(res)conn.close()def app(environ):# 处理业务逻辑:打开文件,读取文件内容并返回with open('timer.html', 'r', encoding='utf-8') as f:data = f.read()return data.encode('utf-8')if __name__ == '__main__':make_server('127.0.0.1', 8008, app)

 上述S端为浏览器返回的都是静态页面(内容都固定的),要想返回动态页面(内容是变化的),那timer.html中的内容就不能写死,可以定义一个特殊的符号(类似于变量名),然后每次用得到的值覆盖该符号即可(类似于为变量赋值),于是timer.html内容修改如下

<!DOCTYPE html>
<html>
<head><meta charset="UTF-8"><title>Title</title>
</head>
<body>
<h2>当前时间为:{{ xxx }}</h2>
</body>
</html>
S端修改如下
# S端
import socketdef make_server(ip, port, app): # 代表serversock = socket.socket()sock.bind((ip, port))sock.listen(5)print('Starting development server at http://%s:%s/' %(ip,port))while True:conn, addr = sock.accept()recv_data = conn.recv(1024)ll=recv_data.decode('utf-8').split('\r\n')head_ll=ll[0].split(' ')environ={}environ['PATH_INFO']=head_ll[1]environ['method']=head_ll[0]res = app(environ)conn.send(b'HTTP/1.1 200 OK\r\n')conn.send(b'Content-Type: text/html\r\n\r\n')conn.send(res)conn.close()def app(environ):# 处理业务逻辑import timenow = time.strftime("%Y-%m-%d %H:%M:%S", time.localtime())with open('timer.html', 'r', encoding='utf-8') as f:data = f.read()data = data.replace('{{ xxx }}', now)  # 字符串替换return data.encode('utf-8')if __name__ == '__main__':make_server('127.0.0.1', 8008, app) # 在浏览器输入http://127.0.0.1:8008,每次刷新都会看到不同的时间

4、jinja2模块

承接上例我们返回动态页面的解决方案,思考一个问题,如果页面中需要引入特殊符号/“变量”过多,那么函数app中要写一大堆字符串替换代码,相当麻烦,有一个模块jinja2很好地帮我们解决了这个问题,本质原理也就是字符串的替换,我们用它就好 

<!DOCTYPE html>
<html>
<head><meta charset="UTF-8"><title>Title</title>
</head>
<body>
<h2>当前时间为:{{ xxx }}</h2>
<h2>当前用户为:{{ user }}</h2>
<h2>当前角色:{{ role }}</h2>
</body>
</html>
S端内容如下
# S端
import socket
from jinja2 import Template # pip3 install jinja2def make_server(ip, port, app): # 代表serversock = socket.socket()sock.bind((ip, port))sock.listen(5)print('Starting development server at http://%s:%s/' %(ip,port))while True:conn, addr = sock.accept()recv_data = conn.recv(1024)ll=recv_data.decode('utf-8').split('\r\n')head_ll=ll[0].split(' ')environ={}environ['PATH_INFO']=head_ll[1]environ['method']=head_ll[0]res = app(environ)conn.send(b'HTTP/1.1 200 OK\r\n')conn.send(b'Content-Type: text/html\r\n\r\n')conn.send(res)conn.close()def app(environ):# 处理业务逻辑import timenow = time.strftime("%Y-%m-%d %H:%M:%S", time.localtime())with open('timer.html', 'r', encoding='utf-8') as f:data = f.read()template=Template(data)# data = data.replace('{{ xxx }}', now)  # 字符串替换data=template.render({'xxx':now,'user':'ly','role':'大总管'})return data.encode('utf-8')if __name__ == '__main__':make_server('127.0.0.1', 8008, app) # 在浏览器输入http://127.0.0.1:8008,每次刷新都会看到不同的时间

 

三、Web框架的由来

1、wsgiref模块

综上案例我们可以发现一个规律,在开发S端时,server的功能是复杂且固定的(处理socket消息的收发、解析http协议的数据),而app中的业务逻辑却各不相同(不同的软件就应该有不同的业务逻辑),重复开发复杂且固定的server是毫无意义的,有一个wsgiref模块帮我们写好了server的功能,这样我们便只需要专注于app功能的编写即可

timer.html已经存在了,新增的index.html页面内容如下:

<!DOCTYPE html>
<html lang="en">
<head><meta charset="UTF-8"><title>Title</title>
</head>
<body><h1>主页</h1>
</body>
</html>

 上述案例中app在处理业务逻辑时需要根据不同的url地址返回不同的页面内容,当url地址越来越多,需要写一堆if判断,代码不够清晰,耦合程度高,所以我们做出以下优化

# 处理业务逻辑的函数
from jinja2 import Templatedef index(environ):with open('index.html', 'r', encoding='utf-8') as f:data = f.read()return data.encode('utf-8')def timer(environ):import timenow = time.strftime("%Y-%m-%d %H:%M:%S", time.localtime())with open('timer.html', 'r', encoding='utf-8') as f:data = f.read()template=Template(data)data=template.render({'xxx':now,'user':'ly','role':'大总管'})return data.encode('utf-8')# 路径跟函数的映射关系
url_patterns = [('/index', index),('/timer', timer),
]from wsgiref.simple_server import make_server def app(environ, start_response):start_response('200 OK', [('Content-Type', 'text/html')])# 拿到请求的url并根据映射关系url_patters执行相应的函数reuqest_url = environ.get('PATH_INFO')for url in url_patterns:if url[0] == reuqest_url:data = url[1](environ)breakelse:data = b'404'return [data]if __name__ == '__main__':s = make_server('', 8011, app)print('监听8011')s.serve_forever()

 2、简单Web框架实现

随着业务逻辑复杂度的增加,处理业务逻辑的函数以及url_patterns中的映射关系都会不断地增多,此时仍然把所有代码都放到一个文件中,程序的可读性和可扩展性都会变得非常差,所以我们应该将现有的代码拆分到不同文件中

views.py 内容如下:

# 处理业务逻辑的函数
from jinja2 import Templatedef index(environ):with open('index.html', 'r', encoding='utf-8') as f:data = f.read()return data.encode('utf-8')def timer(environ):import timenow = time.strftime("%Y-%m-%d %H:%M:%S", time.localtime())with open('timer.html', 'r', encoding='utf-8') as f:data = f.read()template=Template(data)data=template.render({'xxx':now,'user':'ly','role':'大总管'})return data.encode('utf-8')urls.py内容如下:
# 路径跟函数的映射关系
from app01.views import * # 需要导入views中的函数url_patterns = [('/index', index),('/timer', timer),
]main.py 内容如下:
from wsgiref.simple_server import make_server
from mysite.urls import url_patterns  # 需要导入urls中的url_patternsdef app(environ, start_response):start_response('200 OK', [('Content-Type', 'text/html')])# 拿到请求的url并根据映射关系url_patters执行相应的函数reuqest_url = environ.get('PATH_INFO')for url in url_patterns:if url[0] == reuqest_url:data = url[1](environ)breakelse:data = b'404'return [data]if __name__ == '__main__':s = make_server('', 8011, app)print('监听8011')s.serve_forever()

至此,我们就针对application的开发自定义了一个框架,所以说框架的本质就是一系列功能的集合体、不同的功能放到不同的文件中。有了该框架,可以让我们专注于业务逻辑的编写,极大的提高了开发web应用的效率(开发web应用的框架可以简称为web框架),比如我们新增一个业务逻辑,要求为:浏览器输入http://127.0.0.1:8011/home 就能访问到home.html页面

3、简单web框架的使用

在框架的基础上具体开发步骤如下:

步骤一:在templates文件夹下新增home.html

步骤二:在urls.py的url_patterns中新增一条映射关系

url_patterns = [('/index', index),('/timer', timer),('/home', home), # 新增的映射关系
]


步骤三:在views.py中新增一个名为home的函数 

def home(environ):with open('templates/home.html', 'r',encoding='utf-8') as f: data = f.read()return data.encode('utf-8')

我们自定义的框架功能有限,总结下来大致有这么几个功能

功能1、socket收发消息,指的是server
功能2、根据不同的路径执行不同的处理逻辑/功能
功能3、返回动态页面(字符串的替换),如jinja2

 4、三大web框架简介与wsgi协议

在Python中我们可以使用别人开发的、功能更强大的Web框架,如django、tornado、flask等,三种区别如下

1、django框架
实现了上述功能1、2、3。

django框架自定义了server(基于wsgiref+socketserver模块来实现),但这只是提供给开发测试使用的server,并不能在生产环境应用,生产环境部署django的server通常采用uwsgi
django自定义了视图系统,即实现了功能2
django自定义了模板系统,即实现了功能3


2、tornado框架
实现了上述功能1、2、3
tornado框架自定义了server,是异步非阻塞的,效率很高,生产环境也可使用,考虑到高并发,通常选择该框架

3、flask框架
只实现了上述功能2

wsgi协议

综上,我们得知一个Web应用的S端由server和application构成,服务器程序server负责接受HTTP请求、解析HTTP请求、发送HTTP响应等底层套接字通信的处理,都是苦力活,如果我们自己来写这些底层代码,还没开始写应用程序逻辑application呢,就得花个把月去读HTTP规范,所以我们通常直接使用别人开发好的server程序,比如wsgiref、uwsgi、或者框架自带的等等,我们则只需要把精力放在开发应用程序逻辑application上即可。因为我们在开发application时不希望接触到诸如TCP连接、HTTP原始请求和响应格式等底层套接字通信,所以需要在server与application之间建立一套统一的规范/接口,让我们专心用Python编写Web业务。
这个接口就是WSGI:Web Server Gateway Interface。

其实wsgiref、uwsgi等服务器程序server都是遵循wsgi协议的,有了这套协议/标准,server与application的开发就完全解开了耦合,一批程序员可以专注于开发不同的server,一批程序员(就是我们自己)则专注于开发不同的application,只要二者都遵循wsgi协议,则开发的程序可以完美整合,这跟谈恋爱是一个道理,定好你对老婆的要求/标准,只要符合这个标准的女人都可以做你的老婆,反之也一样,所以,找到单身的原因没有

web框架的出现是为了让我们把精力更多地放在开发application上,有的web框架自己实现了高性能的server(比如tornado),有的web框架需要借助别人开发的高性能server=>uwsgi(比如django),之所以可以这么灵活,都要归功于wsgi协议

在开发过程中我们也无需关系wsgi协议,web框架都会按照wsgi协议为我们定制好application的基本功能,所以我们只需要关心最上层的应用程序的开发即可,有了web框架真是省了我们不少事,下面我们就来详细介绍一下django框架吧

四、Django框架的安装

1、django-生产环境的版本选择

Django有很多版本,而且在不断的更新,那如果生产环境要开发部署使用,需要选择什么样子的版本呢?

我们需要事先了解关于django的三个版本标识

1、功能发布的版本号如何标识?

版本号一般是A.B或者A.(B+1)格式,例如1.x系列版本有1.8、1.9、1.10等版本,代表1版本下更新的版本。这些版本的更新频率是8个月左右,更新内容基本都是新功能,改进现有功能等。

2、LTS版本

LTS是长期支持(Long Term Support)的缩写,某个特性版本将被指定为长期支持(LTS)版本。这些版本将对导致安全性和数据丢失的bug进行修复,支持时间通常三年。

3、补丁版本的版本号如何标识?

版本号一般是:A.B.C格式的,例如1.8.18、 1.9.2等最后一位数字代表的版本号的就是补丁版本了。这些版本的发布,是为了修正错误或安全问题。这些版本将100%兼容相关的主版本,因此我们应该保持升级到最新的补丁版本,以保证数据的完整和安全。

综上所述,最新的LTS版本,而且补丁版本也是要最新的,是最好的选择

4 Django的版本规划

 5 主要几个版本的支持时间

 django的版本我们推荐2.2.9LTS,所以搭配的python解释器版本,我们应该选择python3.5之后的解释器

强调:

django从未停止更新,最新版本信息请读者自行查阅并作出合理选择

Download Django | Django

2、安装

pip3 install django==2.2.9 # 在命令行执行该命令
查看
pip3 show django

五、django框架的使用

1、命令行创建并启动Django项目

如果使用的是我们自定义的框架来开发web应用,需要事先生成框架包含的一系列基础文件,然后在此基础上进行开发。

如果使用的是Django框架来开发web应用,同样需要事先生成Django框架包含的一系列基础文件,然后在此基础上进行开发。

但Django框架更为方便的地方在于它已经为我们提供了一系列命令来帮我们快速地生成这一系列基础文件

# 在命令行执行以下指令,会在当前目录生成一个名为mysite的文件夹,该文件夹中包含Django框架的一系列基础文件
django-admin startproject mysite

创建功能模块

cd mysite # 切换到mysite目录下,执行以下命令
python manage.py startapp app01 # 创建功能模块app01,此处的startapp代表创建application下的一个功能模块。例如我们要开发application是京东商城,京东商城这个大项目下有一个订单管理模块,我们可以将其命名为app01

运行

python manage.py runserver 8001 # 在浏览器输入:http://127.0.0.1:8001 会看到Django的欢迎页面。

2、基于Pycharm创建Django项目以及虚拟环境

 之前我们提过,django有多种版本,如果我们想不同版本的django都体验一下,怎么做呢,可以创建虚拟环境,在虚拟环境中安装想要使用的django版本,如下

创建成功后,在虚拟环境中安装最新3.0.2版本的django即可 

3、Django项目目录结构

mysite # 文件夹
*  ├── app01 # 文件夹
*  │  └── migrations # 文件夹**
*  │  └── admin.py
*  │  └── apps.py
*  │  └── models.py
*  │  └── tests.py
*  │  └── views.py
*  ├── mysite # 文件夹**
*  │  └── settings.py
*  │  └── urls.py
*  │  └── wsgi.py
*  └── templates # 文件夹├── manage.py

 关键文件介绍

-manage.py---项目入口,执行一些命令
-项目名
*  -settings.py  全局配置信息
*  -urls.py    总路由,请求地址跟视图函数的映射关系
-app名字
*  -migrations  数据库迁移的记录
*  -models.py   数据库表模型
*  -views.py   处理业务逻辑的函数,简称视图函数

 4、基于Django实现的一个简单示例

(1)url.py 
from django.contrib import admin
from django.urls import path,re_path # 导入re_path
#导入views模块
from app01 import viewsurlpatterns = [path('admin/', admin.site.urls), # 该行代码是新建项目时django框架自动生成的,暂且忽略,暂时注释掉也可以,随着一步步深入介绍,都会讲到它的专门用法# 新增地址http://127.0.0.1:8001/index/与index函数的映射关系re_path(r'^index/$',views.index), # r'^index/$'会匹配url地址的路径部分
]
(2)视图
from django.shortcuts import render# 必须定义一个request形参,request相当于我们自定义框架时的environ参数
def index(request):import datetimenow=datetime.datetime.now()ctime=now.strftime("%Y-%m-%d %X")return render(request,"index.html",{"ctime":ctime}) # render会读取templates目录下的index.html文件的内容并且用字典中的ctime的值替换模版中的{{ ctime }}

(3)模版

在templates目录下新建文件index.html

<!DOCTYPE html>
<html lang="en">
<head><meta charset="UTF-8"><title>Title</title>
</head>
<body>
<h4>当前时间:{{ ctime }}</h4></body>
</html>

测试:

python manage.py runserver 8001 # 在浏览器输入:http://127.0.0.1:8001/index/ 会看到当前时间。

六、MVC模式和MTV模式

MVC,全名是Model View Controller,是Web开发领域中的一种著名的软件架构模式,所谓MVC就是把Web应用分为模型(M),控制器(C)和视图(V)三层,他们之间以一种插件式的、松耦合的方式连接在一起

# M:Model模型负责业务对象与数据库的映射(ORM),用于数据库打交道
# V:view视图负责与用户的交互(html页面)
# C:Controller控制器接受用户的输入调用模型和视图完成用户的请求

MVC的软件架构模式具有有耦合性低、重用性高、生命周期成本低等优点,主要用于web框架,常用的开发语言,有java,php,node.js等等。
Django框架的设计模式借鉴了MVC框架的思想,也是分成三部分,不同之处在于它拆分的三部分为:Model(模型)、Template(模板)和View(视图),也就是MTV模式,

# Model(模型):负责业务对象与数据库的对象(ORM)# Template(模版):负责如何把页面展示给用户# View(视图):负责业务逻辑,并在适当的时候调用Model和Template此外,Django还有一个urls分发器,它的作用是将一个个URL的页面请求分发给不同的view处理,
view再调用相应的Model和Template

 MTV模式与MVC本质上都是一样的,都是为了各组件保持松耦合关系,如下所示

 七、Django框架的分层与请求生命周期        

综上,我们使用Django框架就是为了开发application,而application的工作过程本质就是根据不同的请求返回不同的数据,Django框架将这个工作过程细分为如下四层去实现

1、路由层(根据不同的地址执行不同的视图函数,详见urls.py)

2、视图层(定义处理业务逻辑的视图函数,详见views.py)

3、模型层 (跟数据库打交道的,详解models.py)

4、模板层(待返回给浏览器的html文件,详见templates)

 

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

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

相关文章

Spring Boot 3.2.0 Tomcat虚拟线程初体验 (部分装配解析)

写在前面 spring boot 3 已经提供了对虚拟线程的支持。 虚拟线程和平台线程主要区别在于&#xff0c;虚拟线程在运行周期内不依赖操作系统线程&#xff1a;它们与硬件脱钩&#xff0c;因此被称为 “虚拟”。这种解耦是由 JVM 提供的抽象层赋予的。 虚拟线程的运行成本远低于平…

记录:Unity脚本的编写8.0

目录 需求分析设计GUI包含账号和密码输入栏&#xff0c;包括登录和注册按键添加背景音乐编写脚本控制音乐 退出按钮编写脚本 背景图片完整代码 一个小demo&#xff0c;登录和注册的实现&#xff08;包括GUI和数据库操控&#xff09; 需求分析 自行设计GUI&#xff0c;要求 1.包…

智能优化算法应用:基于花授粉算法无线传感器网络(WSN)覆盖优化 - 附代码

智能优化算法应用&#xff1a;基于花授粉算法无线传感器网络(WSN)覆盖优化 - 附代码 文章目录 智能优化算法应用&#xff1a;基于花授粉算法无线传感器网络(WSN)覆盖优化 - 附代码1.无线传感网络节点模型2.覆盖数学模型及分析3.花授粉算法4.实验参数设定5.算法结果6.参考文献7.…

java游戏制作-王者荣耀游戏

一.准备工作 首先创建一个新的Java项目命名为“王者荣耀”&#xff0c;并在src下创建两个包分别命名为“com.sxt"、”com.stx.beast",在相应的包中创建所需的类。 创建一个名为“img”的文件夹来储存所需的图片素材。 二.代码呈现 package com.sxt; import javax.…

深入理解强化学习——马尔可夫决策过程:贝尔曼期望方程-[举例与代码实现]

分类目录&#xff1a;《深入理解强化学习》总目录 在文章《深入理解强化学习——马尔可夫决策过程&#xff1a;贝尔曼期望方程-[基础知识]》中我们讲到了贝尔曼期望方程&#xff0c;本文就举一个贝尔曼期望方程的具体例子&#xff0c;并给出相应代码实现。 下图是一个马尔可夫…

基于springboot实现私人健身与教练预约管理系统项目【项目源码+论文说明】计算机毕业设计

基于springboot实现私人健身与教练预约管理系统演示 摘要 随着信息技术和网络技术的飞速发展&#xff0c;人类已进入全新信息化时代&#xff0c;传统管理技术已无法高效&#xff0c;便捷地管理信息。为了迎合时代需求&#xff0c;优化管理效率&#xff0c;各种各样的管理系统应…

深入理解虚拟 DOM:提升前端性能的关键技术

&#x1f90d; 前端开发工程师&#xff08;主业&#xff09;、技术博主&#xff08;副业&#xff09;、已过CET6 &#x1f368; 阿珊和她的猫_CSDN个人主页 &#x1f560; 牛客高级专题作者、在牛客打造高质量专栏《前端面试必备》 &#x1f35a; 蓝桥云课签约作者、已在蓝桥云…

Vue 定义只读数据 readonly 与 shallowReadonly

readonly 让一个响应式数据变为 **深层次的只读数据**。 shallowReadonly 让一个响应式数据变为 **浅层次的只读数据**&#xff0c;只读第一层。 isReadonly 判断一个数据是不是只读数据。 应用场景&#xff1a;不希望数据被修改时使用。 readonly深层次只读&#xff1a; …

Java实现王者荣耀小游戏

主要功能 键盘W,A,S,D键&#xff1a;控制玩家上下左右移动。按钮一&#xff1a;控制英雄发射一个矩形攻击红方小兵。按钮二&#xff1a;控制英雄发射魅惑技能&#xff0c;伤害小兵并让小兵停止移动。技能三&#xff1a;攻击多个敌人并让小兵停止移动。普攻&#xff1a;对小兵造…

VsCode学习

一、在VsCode 上编写第一个C语言 在VsCode上写代码都是先打开文件夹&#xff0c;这样也方便管理代码和编译器产生的可执行程序&#xff0c;VsCode生成的配置文件等。 1.1打开文件夹 写代码前&#xff0c;首先创立一个文件夹存储以后我们写的VsCode代码&#xff0c;便于管理。…

代码随想录算法训练营 ---第四十九天

前言&#xff1a; 今天是买卖股票的最佳时机系列&#xff0c;本系列之前在学习贪心思想时做过一些。 第一题&#xff1a; 简介&#xff1a; 本题在读题时我们要注意到几个细节 1.本题股票买卖只有一次。2.我们要在最低点买股票&#xff0c;在最高点卖股票。 我的思路&#…

skywalking告警UI界面有告警信息,webhook接口没有回调,400错误

400错误就是回调接口返回数据的属性对应不上 PostMapping(“/webhook”) public void webhook(RequestBody List alarmMessageList) 自定义的实体类AlarmMessage有问题 只能去官网找了 告警实体类官网 Getter EqualsAndHashCode RequiredArgsConstructor NoArgsConstructor(fo…

父进程隐藏——ConsoleApplication903项目

首先我发现用calc来做进程隐藏实验是失败的&#xff0c;父进程一直都是svhost.exe 那么我用我自己生成的cs木马beacon903.exe试试 试试explorer.exe 再试试cmd.exe 可以看到成功变成cmd.exe 可以看到我们可以通过这种方式虚假父进程 以上我们是直接获得的pid&#xff0c;那…

【Kotlin】类与接口

文章目录 类的定义创建类的实例构造函数主构造函数次构造函数init语句块 数据类的定义数据类定义了componentN方法 继承AnyAny&#xff1a;非空类型的根类型Any?&#xff1a;所有类型的根类型 覆盖方法覆盖属性覆盖 抽象类接口:使用interface关键字函数&#xff1a;funUnit:让…

听GPT 讲Rust源代码--src/tools(2)

题图来自AI生成 File: rust/src/tools/rust-analyzer/crates/hir-def/src/src.rs rust-analyzer 是一个 Rust 语言的语法分析器和语义分析器&#xff0c;用于提供代码补全、导航、重构等开发工具。而 rust-analyzer 的代码实现存储在 rust/src/tools/rust-analyzer 这个文件夹中…

亚马逊云科技 re:Invent 2023:引领科技前沿,探索未来云计算之窗

文章目录 一、前言二、什么是亚马逊云科技 re:Invent&#xff1f;三、亚马逊云科技 re:Invent 2023 将于何时何地举行四、亚马逊云科技 re:Invent 2023 有什么内容&#xff1f;4.1 亚马逊云科技 re:Invent 2023 主题演讲4.2 亚马逊云科技行业专家探实战 五、更多亚马逊云科技活…

Unity UGUI的自动布局-LayoutGroup(水平布局)组件

Horizontal Layout Group | Unity UI | 1.0.0 1. 什么是HorizontalLayoutGroup组件&#xff1f; HorizontalLayoutGroup是Unity UGUI中的一种布局组件&#xff0c;用于在水平方向上对子物体进行排列和布局。它可以根据一定的规则自动调整子物体的位置和大小&#xff0c;使它们…

万字详解,和你用RAG+LangChain实现chatpdf

像chatgpt这样的大语言模型(LLM)可以回答很多类型的问题,但是,如果只依赖LLM,它只知道训练过的内容,不知道你的私有数据:如公司内部没有联网的企业文档,或者在LLM训练完成后新产生的数据。(即使是最新的GPT-4 Turbo,训练的数据集也只更新到2023年4月)所以,如果我们…

141.【Git版本控制-本地仓库-远程仓库-IDEA开发工具全解版】

Git-深入挖掘 (一)、Git分布式版本控制工具1.目标2.概述(1).开发中的实际常见(2).版本控制器的方式(3).SVN (集中版本控制器)(4).Git (分布版本控制器)(5).Git工作流程图 (二)、Git安装与常用命令1.Git环境配置(1).安装Git的操作(2).Git的配置操作(3).为常用的指令配置别名 (可…

想成为网络安全工程师该如何学习?

一、网络安全应该怎么学&#xff1f; 1.计算机基础需要过关 这一步跟网安关系暂时不大&#xff0c;是进入it行业每个人都必须掌握的基础能力。 计算机网络计算机操作系统算法与数据架构数据库 Tips:不用非要钻研至非常精通&#xff0c;可以与学习其他课程同步进行。 2.渗透技…