用 Python 擼一個 Web 服務器-第3章:使用 MVC 構建程序

Todo List 程序介紹

我們將要編寫的 Todo List 程序包含四個頁面,分別是注冊頁面、登錄頁面、首頁、編輯頁面。以下分別為四個頁面的截圖。

注冊頁面:

注冊

注冊

登錄頁面:

登錄

登錄

首頁:

首頁

首頁

編輯頁面:

編輯

編輯

程序頁面非常簡潔,甚至有些 Low。但這足夠我們學習開發 Web 服務器程序原理,頁面樣式的問題并不是我們本次學習的重點,所以讀者不必糾結于此。

Todo List 程序功能大概分為兩個部分,一部分是 todo 管理,包含增刪改查基礎功能;另一部分是用戶管理,包含注冊和登錄功能。

初識 MVC

介紹了 Todo List 程序的頁面和功能,接下來我們就要思考如何設計程序。

以客戶端通過瀏覽器向服務器發送一個獲取應用首頁的請求為例,來分析下服務器在收到這個請求后都需要做哪些事情:

  1. 首先服務器需要對請求數據進行解析,發現客戶端是要獲取應用首頁。
  2. 然后找到代表首頁的 HTML 文件,讀取 HTML 文件中的內容。
  3. 最后將 HTML 內容組裝成符合 HTTP 規范的數據進行返回。

這是一個較為理想的情況,因為 HTML 頁面內容是固定的,我們不需要對其進行其他處理,直接返回給瀏覽器即可。通常我們管這種頁面叫靜態頁面。

但實際情況中,Todo List 程序首頁內容并不是一成不變的,而是動態變化的。首頁 HTML 文件中只定義基礎結構,具體的 todo 數據需要動態填充進去。所以一個更加完整的服務器處理請求的過程應該像下面這樣:

  1. 首先服務器需要對請求數據進行解析,發現客戶端是要獲取應用首頁。
  2. 然后從數據庫中讀取 todo 數據。
  3. 接著找到代表首頁的 HTML 文件,讀取 HTML 文件中的內容。
  4. 再將 todo 數據動態添加到 HTML 內容中。
  5. 最后將處理好的 HTML 內容組裝成符合 HTTP 規范的數據進行返回。

現在已經知道了服務器處理請求的完整過程,我們就可以設計服務器程序了。試想一下,如果 Todo List 程序都像 Hello World 程序一樣把代碼都寫在一個 Python 文件中也不是不可以。但這樣的代碼顯然不具備良好的擴展性和可維護性。那么更好的設計模式是什么呢?

其實對于 Web 服務器程序的設計,業界早已達成了一個普遍的共識,那就是 MVC 模式:

M(Model):模型,用來存儲和處理 Web 應用數據。

V(View):視圖,格式化顯示 Web 應用頁面。

C(Controller):控制器,負責基礎邏輯,如從模型層讀取數據并將數據填充到視圖層,然后返回響應。

通過 MVC 的分層結構,能夠讓 Web 應用設計更加清晰,可以很容易的構建可擴展、易維護的代碼。模型層,說直白些其實就是用來讀寫數據庫的 Python 代碼,新增 todo 的時候,可以通過模型層的代碼將數據保存到數據庫中,訪問首頁時需要展示所有已保存的 todo,這時可以通過模型層的代碼從數據庫中讀取所有 todo。視圖層,可以將其簡單的理解為 HTML 模板文件的集合。控制器起到粘合的作用,它將從模型層讀取過來的數據填充到視圖層并返回給瀏覽器,或者將瀏覽器通過 HTML 頁面提交過來的數據解析出來再通過模型層寫入數據庫中。

我畫了一個示例圖,來幫助你理解 MVC 模式。圖中標注了瀏覽器發起一個請求到獲得響應,中間經歷的完整過程。

MVC

還是以客戶端請求 Todo List 程序首頁為例,一個完整的請求過程如下:

  1. 瀏覽器發起請求。
  2. 請求到達服務器后首先進入控制器,然后控制器從模型獲取 todo 數據。
  3. 模型操作數據庫,查詢 todo 數據。
  4. 數據庫返回 todo 數據。
  5. 模型將從數據庫中查詢的 todo 數據返回給控制器,控制器暫時將數據保存在內存中。
  6. 控制器從視圖中獲取首頁 HTML 模板。
  7. 控制器將從模型查出來的 todo 數據填充到首頁 HTML 模板中,并組裝成符合 HTTP 規范的數據。
  8. 服務器返回響應。

其實 MVC 是一個宏觀上的分層,具體細節部分還需要根據我們設計程序的粒度來進行處理,比如有些邏輯既可以寫在控制器層,也可以寫在模型層,甚至我們還可以在 MVC 的基礎上擴展更多的分層。這些都需要結合具體的業務邏輯來決定。

構建 Todo List 程序

學習了 MVC 模式,我們就可以根據 MVC 模式來試著構建 Todo List 程序了。

Todo List 程序分兩部分:todo 管理、用戶管理。在項目初期,我們肯定不會考慮的太過全面。所以可以先不考慮用戶管理功能部分的實現,先只考慮如何實現 todo 管理功能。

Todo List 程序目錄結構設計如下:

todo_list  
├── server.py  
└── todo  ├── \_\_init\_\_.py  ├── config.py  ├── controllers.py  ├── db  │   └── todo.json  ├── models.py  ├── templates  │   ├── edit.html  │   └── index.html  └── utils.py

這里以 todo_list/ 作為程序的根目錄,根目錄下包含 server.py 文件和 todo/ 目錄。其中 server.py 主要功能就是作為一個 Web Server 來接收請求和返回響應,它是 Todo List 程序的入口和出口。而 todo/ 目錄則是 Todo List 程序處理業務邏輯的核心。

todo/ 目錄下的 __init__.pytodo/ 文件夾標記為一個 Python 包。 config.py 用于存儲一些項目的基礎配置。utils.py 是一個工具集,里面可以定義一些供其他模塊調用的類和方法。db/ 目錄作為 Todo List 程序存儲數據的目錄,db/todo.json 用來存儲所有的 todo 內容。剩下還有兩個 .py 文件和一個目錄沒有介紹,相信你已經猜到了 models.pytemplates/controllers.py 分別對應了 MVC 模式中的模型、視圖、控制器。models.py 中編寫操作 todo 數據的代碼,templates/ 目錄用來存放 HTML 模板文件,templates/index.html 是首頁,templates/edit.html 是編輯頁面,controllers.py 編寫負責程序控制的基礎邏輯代碼。

我們對項目的目錄結構有了一個概覽,這里我要強調一下 db/ 目錄的作用。我們在開發整個 Todo List 程序的過程中都不會使用實際的數據庫程序,項目中所有需要存儲的數據都保存在 db/ 目錄下的文件中。在開發 Web 程序時,需要用到數據庫的目的就是為了存儲數據,對于 Todo List 程序來說使用文件同樣能滿足需求,同時能夠照顧到對數據庫不了解的讀者。

Todo List 首頁開發

Todo List 程序目錄結構構建完成后就可以動手開發程序了,我們可以從一個請求經歷的過程來著手。

一個請求發送到服務器,首先服務器需要有一個能夠接收請求的入口,在程序根目錄 todo_list/ 下的 server.py 就是這個入口。server.py 文件代碼如下:

\# todo_list/server.pyimport socket  
import threadingfrom todo.config import HOST, PORT, BUFFER_SIZEdef process_connection(client):  """處理客戶端請求"""  \# 接收請求報文數據  request_bytes = b''  while True:  chunk = client.recv(BUFFER_SIZE)  request_bytes += chunk  if len(chunk) < BUFFER_SIZE:  break\# 請求報文  request_message = request_bytes.decode('utf-8')  print(f'request_message: {request_message}')\# TODO: 解析請求  \# TODO: 返回響應\# 關閉連接  client.close()def main():  """入口函數"""  with socket.socket() as s:  s.setsockopt(socket.SOL_SOCKET, socket.SO_REUSEADDR, 1)  s.bind((HOST, PORT))  s.listen(5)  print(f'running on http://{HOST}:{PORT}')while True:  client, address = s.accept()  print(f'client address: {address}')  \# 創建新的線程來處理客戶端連接  t = threading.Thread(target=process_connection, args=(client,))  t.start()

server.py 程序幾乎就是之前實現的多線程版 Hello World 服務器程序照搬過來的。為了程序代碼更加清晰,這里將服務器的 IP 地址、端口、接收請求的緩沖區大小定義為變量寫在了配置文件 todo/config.py 中,所以需要在 server.py 文件頂部從配置文件中導入 HOSTPORTBUFFER_SIZE。在 main 函數中,實例化 socket 對象部分的代碼也有所改變,這里采用了 with 語句來實例化 socket 對象,這樣能夠保證任何情況下退出程序時 socket 都能夠被正確關閉。此處 with 語句的用法可以類比文件操作時的 with 語句。處理客戶端連接請求的 process_connection 函數內部基本邏輯沒有改變,其中有兩個 TODO 注釋表示解析請求和返回響應的功能暫未實現。

server.py 入口程序接收到客戶端的請求以后,需要解析請求報文,并根據解析出來的請求報文來決定如何處理請求并返回響應。所以接下來我們需要編寫解析請求的代碼。

不過在這之前,我先給出 todo/config.py 配置文件的代碼,畢竟之后還會用到:

\# todo_list/todo/config.pyimport os\# todo/ 目錄絕對路徑  
BASE_DIR = os.path.dirname(os.path.abspath(\_\_file\_\_))\# IP  
HOST = '127.0.0.1'  
\# 端口  
PORT = 8000\# 緩沖大小  
BUFFER_SIZE = 1024

配置文件中除了包含前面介紹過的表示 IP 地址、端口、接收請求的緩沖區大小的幾個變量,還有一個 BASE_DIR 變量用來表示 todo/ 目錄的絕對路徑,方便在程序中獲取項目路徑。

現在來看下如何解析請求,我們可以定義一個 Request 類用來專門解析請求報文,代碼寫在 todo/utils.py 文件中:

\# todo_list/todo/utils.pyclass Request(object):  """請求類"""def \_\_init\_\_(self, request_message):  method, path, headers = self.parse_data(request_message)  self.method = method \# 請求方法 GET、POST  self.path = path \# 請求路徑 /index  self.headers = headers \# 請求頭 {'Host': '127.0.0.1:8000'}def parse_data(self, data):  """解析請求報文數據"""  \# 用請求報文中的第一個 '\\r\\n\\r\\n' 做分割,將得到請求頭和請求體  \# 請求體暫時用不到先不處理  header, body = data.split('\\r\\n\\r\\n', 1)  method, path, headers = self.\_parse_header(header)  return method, path, headersdef \_parse_header(self, data):  """解析請求頭"""  \# 拆分請求行和請求首部  request_line, request_header = data.split('\\r\\n', 1)\# 請求行拆包 'GET /index HTTP/1.1' -> \['GET', '/index', 'HTTP/1.1'\]  \# 因為 HTTP 版本號沒什么用,所以用一個下劃線 _ 變量來接收  method, path, _ = request_line.split()\# 解析請求首部所有的鍵值對,組裝成字典  headers = {}  for header in request_header.split('\\r\\n'):  k, v = header.split(': ', 1)  headers\[k\] = vreturn method, path, headers

Request 類的初始化方法 __init__ 接收請求報文字符串作為參數。在其內部調用 parse_data 方法將請求報文字符串解析成我們需要的結構化數據。

解析完請求報文,我們需要根據請求報文信息來判斷如何返回響應。基礎邏輯判斷部分的代碼可以寫在 todo/controllers.py 中:

\# todo_list/todo/controllers.pyfrom todo.utils import render_templatedef index():  """首頁視圖函數"""  return render_template('index.html')

定義在控制器層的函數也叫視圖函數,因為它們通常返回視圖層的 HTML 內容。index 視圖函數用來處理請求首頁的邏輯,它返回 render_template 函數的調用結果,render_template 函數的作用是將 HTML 內容讀取成字符串并返回,其定義如下:

\# todo_list/todo/utils.pyimport osfrom todo.config import BASE_DIRdef render_template(template):  """讀取 HTML 內容"""  \# 讀取 'todo_list/todo/templates' 目錄下的 HTML 文件內容  template_dir = os.path.join(BASE_DIR, 'templates')  path = os.path.join(template_dir, template)  with open(path, 'r', encoding='utf-8') as f:  html = f.read()  return html

todo/controllers.py 文件底部還定義了一個 routes 字典,字典的鍵為請求路徑,值為一個元組,元組的第一個元素作為處理請求的函數,第二個元素是一個列表,里面定義處理請求的函數所允許的請求方法。index 視圖函數能夠同時匹配兩個路徑://index,因為這兩個路徑通常都代表首頁。

\# todo_list/todo/controllers.pyroutes = {  '/': (index, \['GET'\]),  '/index': (index, \['GET'\]),  
}

讀取出 HTML 內容以后,我們就可以構造響應報文并返回給瀏覽器了。在 utils.py 文件下,編寫一個 Response 類用來構造響應:

\# todo_list/todo/utils.pyclass Response(object):  """響應類"""\# 根據狀態碼獲取原因短語  reason_phrase = {  200: 'OK',  405: 'METHOD NOT ALLOWED',  }def \_\_init\_\_(self, body, headers=None, status=200):  \# 默認響應首部字段,指定響應內容的類型為 HTML  \_headers = {  'Content-Type': 'text/html; charset=utf-8',  }if headers is not None:  \_headers.update(headers)  self.headers = \_headers \# 響應頭  self.body = body \# 響應體  self.status = status \# 狀態碼def \_\_bytes\_\_(self):  """構造響應報文"""  \# 狀態行 'HTTP/1.1 200 OK\\r\\n'  header = f'HTTP/1.1 {self.status} {self.reason_phrase.get(self.status, "")}\\r\\n'  \# 響應首部  header += ''.join(f'{k}: {v}\\r\\n' for k, v in self.headers.items())  \# 空行  blank_line = '\\r\\n'  \# 響應體  body = self.bodyresponse_message = header + blank_line + body  return response_message.encode('utf-8')

Response 類的初始化方法 __init__ 接收三個參數,分別為響應體、響應首部字段、狀態碼。其中響應體為 str 類型,首頁的響應體實際上就是 index.html 文件內容。響應首部字段為 dict 類型,在構造響應報文時,所有的響應首部字段最終按照 HTTP 規范拼接到一起作為響應首部。狀態碼為數值類型,目前只考慮了狀態碼為 200 正常響應和 405 請求方法不被允許。

需要注意的是,Response 類定義了 __bytes__ 魔法方法作為構造響應報文的方法。當使用 Python 內置的 bytes 方法轉換 Response 實例對象時(bytes(Response())),會自動調用 __bytes__ 魔法方法。

從解析請求到構造響應報文的代碼現在已經基本編寫完成。接下來我們將整個處理請求的流程串聯起來,回到 server.py 文件,繼續完善代碼:

\# todo_list/server.pyimport socket  
import threadingfrom todo.config import HOST, PORT, BUFFER_SIZE  
from todo.utils import Request, Response  
from todo.controllers import routesdef process_connection(client):  """處理客戶端請求"""  \# 接收請求報文數據  request_bytes = b''  while True:  chunk = client.recv(BUFFER_SIZE)  request_bytes += chunk  if len(chunk) < BUFFER_SIZE:  break\# 請求報文  request_message = request_bytes.decode('utf-8')  print(f'request_message: {request_message}')\# 解析請求報文,構造請求對象  request = Request(request_message)  \# 根據請求對象構造響應報文  response_bytes = make_response(request)  \# 返回響應  client.sendall(response_bytes)\# 關閉連接  client.close()def make_response(request, headers=None):  """構造響應報文"""  \# 默認狀態碼為 200  status = 200  \# 獲取匹配當前請求路徑的處理函數和函數所接收的請求方法  \# request.path 等于 '/''/index' 時,routes.get(request.path) 將返回 (index, \['GET'\])  route, methods = routes.get(request.path)\# 如果請求方法不被允許,返回 405 狀態碼  if request.method not in methods:  status = 405  data = 'Method Not Allowed'  else:  \# 請求首頁時 route 實際上就是我們在 controllers.py 中定義的 index 視圖函數  data = route()\# 獲取響應報文  response = Response(data, headers=headers, status=status)  response_bytes = bytes(response)  print(f'response_bytes: {response_bytes}')return response_bytesdef main():  """入口函數"""  with socket.socket() as s:  s.setsockopt(socket.SOL_SOCKET, socket.SO_REUSEADDR, 1)  s.bind((HOST, PORT))  s.listen(5)  print(f'running on http://{HOST}:{PORT}')while True:  client, address = s.accept()  print(f'client address: {address}')  \# 創建新的線程來處理客戶端連接  t = threading.Thread(target=process_connection, args=(client,))  t.start()if \_\_name\_\_ == '\_\_main\_\_':  main()

首先完成之前未寫完的 process_connection 函數。將原來標記 TODO 注釋的地方替換成了如下代碼:

\# 解析請求報文,構造請求對象  
request = Request(request_message)  
\# 根據請求對象構造響應報文  
response_bytes = make_response(request)  
\# 返回響應  
client.sendall(response_bytes)

新增了一個 make_response 函數,方便用來根據請求對象構造響應報文。函數中我寫了比較詳細的注釋,你可以根據注釋內容讀懂代碼邏輯。

最后給出首頁 todo/templates/index.html 的 HTML 代碼:

<!--todo_list/todo/templates/index.html--><!DOCTYPE html>
<html>  
<head>  <meta charset="UTF-8">  <title>Todo List</title>  <style>  \* {  margin: 0;  padding: 0;  }  ul {  list-style: none;  }  a {  text-decoration: none;  outline: none;  color: #000000;  }  h1 {  margin: 20px auto;  }  .container {  display: flex;  justify-content: center;  align-items: center;  }  .container ul {  width: 100%;  max-width: 600px;  }  .container ul li {  height: 40px;  line-height: 40px;  margin-bottom: 4px;  padding: 0 6px;  display: flex;  justify-content: space-between;  background-color: #d2d2d2;  }  </style>  
</head>  
<body>  
<h1 class="container">Todo List</h1>  
<div class="container">  <ul>  <li>  <div>Hello World</div>  </li>  </ul>  
</div>  
</body>  
</html>

HTML 代碼比較簡單,其中頂部寫了一些基礎的 CSS 樣式,都很容易看懂,這里不再講解。

接下來在終端中,進入 todo_list/目錄下,使用 Python 運行 server.py 文件,看到如下打印結果說明程序已經正常啟動:

運行 Todo List 服務器

運行 Todo List 服務器

打開瀏覽器,地址欄輸入 http://127.0.0.1:8000/ 或者 http://127.0.0.1:8000/index,你將看到 Todo List 程序首頁:

Todo List 首頁

Todo List 首頁

至此,Todo List 程序首頁初步完成。不過我想很多讀者看到這里會產生疑惑,說好的 MVC 呢,目前為止我們并沒有編寫一行模型層的代碼,并且首頁的 HTML 內容也不是動態填充的。沒錯,為了能夠盡快讓 Todo List 程序跑起來,我有意的避開了這兩個問題,下一章我們再來解決這兩個問題。

本章源碼:chapter3

原文出處: https://jianghushinian.cn

本文來自互聯網用戶投稿,該文觀點僅代表作者本人,不代表本站立場。本站僅提供信息存儲空間服務,不擁有所有權,不承擔相關法律責任。
如若轉載,請注明出處:http://www.pswp.cn/web/24215.shtml
繁體地址,請注明出處:http://hk.pswp.cn/web/24215.shtml
英文地址,請注明出處:http://en.pswp.cn/web/24215.shtml

如若內容造成侵權/違法違規/事實不符,請聯系多彩編程網進行投訴反饋email:809451989@qq.com,一經查實,立即刪除!

相關文章

程序員搞副業一些會用到的工具

微信號采集(爬蟲)技術的選型 那么&#xff0c;我們應該使用什么技術來從龐大的網頁內容中自動篩選和提取微信號呢&#xff1f;答案就是&#xff1a;數據采集技術&#xff0c;也就是爬蟲技術。 然而&#xff0c;數據采集技術種類繁多&#xff0c;我們具體應該采用哪一個呢&…

【Linux】—— 線程控制的基本介紹

目錄 &#xff08;一&#xff09;POSIX線程庫 &#xff08;二&#xff09;創建線程 2.1 線程ID及進程地址空間布局 &#xff08;三&#xff09;線程終止 &#xff08;四&#xff09;分離線程 &#xff08;一&#xff09;POSIX線程庫 POSIX線程庫&#xff08;POSIX Thread…

Node.js后端構建指南:MongoDB與Express的集成

安裝express 安裝 Express 并將其保存到依賴列表中&#xff1a; $ cnpm install express --save 以上命令會將 Express 框架安裝在當前目錄的 node_modules 目錄中&#xff0c; node_modules 目錄下會自動創建 express 目錄。以下幾個重要的模塊是需要與 express 框架一起安…

nss刷題(4)

1、[SWPUCTF 2021 新生賽]easyrce <?php error_reporting(0); highlight_file(__FILE__); if(isset($_GET[url])) { eval($_GET[url]); } ?> if(isset($_GET[url])) isset函數用來檢測url變量是否存在&#xff1b;$_GET函數獲取變量數據 eval($_GET[url]); eval函數用…

【GIS矢量切片】tippecanoe在Windows和CentOS中的安裝

組件安裝記錄 背景介紹Windows下安裝1、下載工具2、存放安裝包3、進入DOS終端4、在終端執行命令5、下載程序6、放置源碼7、修改配置信息8、編譯9、測試10、參數說明瓦片輸出瓦片描述和權屬信息輸入文件和圖層名輸入文件的并行處理輸入文件的投影縮放級別瓦片分辨率CentOS 7安裝…

嘗試用 GPT-4o 寫 2024高考語文作文

文章目錄 新課標I卷科技進步與問題的演變 新課標II卷抵達未知之境&#xff1a;探索與成長的旅程 全國甲卷坦誠交流&#xff1a;構建真正相遇的橋梁 北京卷歷久彌新 天津卷定義與自定義&#xff1a;在世界的繽紛中前行 上海卷認可度的思考與反思 新課標I卷 閱讀下面的材料&#…

Mongodb---java篇

一、導入依賴 <dependency><groupId>org.springframework.boot</groupId><artifactId>spring-boot-starter-data-mongodb</artifactId></dependency> 二、編寫配置文件連接Mongodb 我的認證數據庫是admin&#xff0c;你們可能不一樣 sp…

第三篇——大數據思維的科學基礎

目錄 一、背景介紹二、思路&方案三、過程1.思維導圖2.文章中經典的句子理解3.學習之后對于投資市場的理解4.通過這篇文章結合我知道的東西我能想到什么&#xff1f; 四、總結五、升華 一、背景介紹 大數據時代&#xff0c;大數據思維的重要性不言而喻&#xff1b;而信息在…

Elasticsearch搜索優化-自定義路由規劃(routing)

在es的實踐學習中&#xff0c;我覺得它的文檔是最好的老師&#xff0c;所以先把這部分鏈接貼出來&#xff0c;本文只是引導&#xff0c;文檔全是細節&#xff0c;還是推薦大家事后認真看看文檔 Metadata fields-routing 在es搜索中&#xff0c;請求是先分發到所有分片&#x…

6月26~28日,2024北京國際消防展即將開幕!

隨著社會的快速發展&#xff0c;消防安全日益受到廣大民眾的高度關注。為了進一步推動消防科技的創新與發展&#xff0c;提升全民消防安全意識&#xff0c;2024年北京消防展將于6月26日在北京國家會議中心盛大開展。目前:觀眾預登記已全面啟動&#xff0c;廣大市民和業界人士可…

馬爾科夫性質-舉例簡單說明,馬爾科夫模型和隱馬爾科夫模型在自然語言處理方面應用是什么

目錄 馬爾科夫模型應用 馬爾科夫性質,舉例簡單說明 馬爾科夫模型 馬爾科夫鏈 馬爾科夫決策過程(Markov Decision Process, MDP) 例子 隱馬爾科夫模型(Hidden Markov Model, HMM) 馬爾科夫模型和隱馬爾科夫模型在自然語言處理方面應用是什么 馬爾科夫模型在自然語言…

SQLite3(1):介紹安裝與測試

目錄 1、SQLite3介紹 2、SQLite3的優勢和特性 3、SQLite3安裝與測試 3.1 SQLite3安裝 3.2 SQLite3測試 4、SQLite3簡單使用 4.1 連接數據庫文件 4.2 創建信息表 4.3 插入三個學生信息 4.4 確認信息 5、總結 1、SQLite3介紹 SQLite3是一種輕量級的關系型數據庫管理系…

論文閱讀 A Distributional Framework for Data Valuation

本論文解決的問題 量化數據價值&#xff08;機器學習模型訓練中各個數據點的貢獻&#xff09; 避免數據價值受到其所處數據集的影響&#xff0c;使數據點的估值更加穩定、一致 變量假設 假設 D 表示一個在全集 Z 上的數據分布。對于監督學習問題&#xff0c;我們通常認為 Z…

jvm學習筆記(一) ----- JAVA 內存

JAVA 內存 一、程序計數器二、虛擬機棧三、本地方法棧四、堆五、非JAVA內存(堆外內存)1.元空間(Metaspace)2.直接內存 鏈接: jvm學習筆記(二) ----- 垃圾回收 鏈接: jvm學習筆記(三) ----- 垃圾回收器 一、程序計數器 虛擬機需要通過『程序計數器』記錄指令執行到哪了。線程要…

代碼隨想錄算法訓練營day43

題目&#xff1a;1049. 最后一塊石頭的重量 II 、494. 目標和、474.一和零 參考鏈接&#xff1a;代碼隨想錄 1049. 最后一塊石頭的重量 II 思路&#xff1a;本題石頭是相互粉碎&#xff0c;粉碎后剩下的重量就是兩塊石頭之差&#xff0c;我們可以想到&#xff0c;把石頭分成…

使用智譜 GLM-4-9B 和 SiliconCloud 云服務快速構建一個編碼類智能體應用

本篇文章我將介紹使用智譜 AI 最新開源的 GLM-4-9B 模型和 GenAI 云服務 SiliconCloud 快速構建一個 RAG 應用&#xff0c;首先我會詳細介紹下 GLM-4-9B 模型的能力情況和開源限制&#xff0c;以及 SiliconCloud 的使用介紹&#xff0c;最后構建一個編碼類智能體應用作為測試。…

數據結構和算法之數組和鏈表

一、數組 數組是一種線性數據結構&#xff0c;它是由一組連續的內存單元組成的&#xff0c;用于存儲相同類型的數據。在JavaScript中&#xff0c;數組可以包含任意類型的數據&#xff0c;不只限于基本數據類型。 1.存儲方式 在內存中&#xff0c;數組的元素是連續存儲的&…

【Vue】組件的存放目錄問題

注意&#xff1a; .vue文件 本質無區別 組件分類 .vue文件分為2類&#xff0c;都是 .vue文件&#xff08;本質無區別&#xff09; 頁面組件 &#xff08;配置路由規則時使用的組件&#xff09;復用組件&#xff08;多個組件中都使用到的組件&#xff09; 存放目錄 分類開來的…

Llama模型家族之拒絕抽樣(Rejection Sampling)(二)均勻分布簡介

LlaMA 3 系列博客 基于 LlaMA 3 LangGraph 在windows本地部署大模型 &#xff08;一&#xff09; 基于 LlaMA 3 LangGraph 在windows本地部署大模型 &#xff08;二&#xff09; 基于 LlaMA 3 LangGraph 在windows本地部署大模型 &#xff08;三&#xff09; 基于 LlaMA…

ssti模板注入

一、Flask應用 1、介紹 定義 Flask&#xff1a;是一個使用Python編寫的輕量級web應用框架。Flask基于Werkzeug WSGI工具包和Jinja2模板引擎。 特點 良好的文檔、豐富的插件、包含開發服務器和調試器、集成支持單元測試、RESTful請求調度、支持安全cookies、基于Unicode。 …