基于Hmm模型和Viterbi算法的中文分词和词性标注

article/2025/9/12 10:01:46

使用 python 实现基于Hmm模型Viterbi算法的中文分词及词性标注;使用 最大概率算法 进行优化。最终效果:人民日报语料:分词(F1:96.189%);词性标注(F1:97.934%)

完整代码和数据,参见本实验的 github地址:https://github.com/xuzf-git/WordSegment-and-PosTag

1、基于统计的分词方法(隐马尔可夫模型)

(1)算法设计

采用隐马尔科夫(Hmm)模型进行统计分词。使用BMES标注方法,将分词任务转换为字标注的问题,通过对每个字进行标注得到词语的划分。具体来说,BMES标注方法是用“B、M、E、S”四种标签对词语中不同位置的字符进行标注,B表示一个词的词首位置,M表示一个词的中间位置,E表示一个词的末尾位置,S表示一个单独的字
字标注的问题可视为隐马尔可夫模型中的解码问题。句子的BMES标注序列作为隐藏状态序列,句子的字符序列作为可观测序列,通过以下两个步骤实现分词:

  1. 学习模型参数
    对预料进行统计,获得隐藏状态的转移概率矩阵trans、发射概率矩阵emit 、初始状态矩阵start

    1. 观测序列 O O O :句子的字符序列 [ w 0 , w 1 , … … w n ] [w_0,w_1,……w_n] [w0,w1,wn]
    2. 隐藏序列 S S S:BMES标注序列 [ p 0 , p 1 , … … p n ] [p_0,p_1,……p_n] [p0,p1,pn]
    3. 初始概率 π \pi π s t a r t ( i ) = P ( p 0 = i ) = c o u n t ( p 0 = i ) / c o u n t ( s e n t e n c e ) i ∈ { B 、 M 、 E 、 D } start(i)=P_{(p_0=i)}=count(p_0=i)/count(sentence) \quad i\in\{B、M、E、D\} start(i)=P(p0=i)=count(p0=i)/count(sentence)i{BMED}
    4. 转移概率 t r a n s trans trans t r a n s ( i , j ) = P ( j │ i ) = c o u n t ( p k = i , p k + 1 = j ) / c o u n t ( i ) i , j ∈ { B 、 M 、 E 、 D } trans(i,j)=P(j│i)=count(p_k=i ,p_{k+1}=j)/count(i) i,j \in\{B、M、E、D\} trans(i,j)=P(ji)=count(pk=i,pk+1=j)/count(i)i,j{BMED}
    5. 发射概率 e m i t emit emit e m i t ( i , w ) = P ( w │ i ) = c o u n t ( s t a t e ( w ) = i ) / c o u n t ( i ) i ∈ { B 、 M 、 E 、 D } emit(i,w)=P(w│i)=count(state(w)=i)/count(i) \quad i\in\{B、M、E、D\} emit(i,w)=P(wi)=count(state(w)=i)/count(i)i{BMED},
  2. 使用 Viterbi 算法预测
    Viterbi算法是用动态规划的方法求解最优的标注序列。每个标注序列视为从句首到句尾的一个路径,通过Viterbi算法获取概率最大的路径,在主要由以下几步实现:

    1. 状态 d p [ i ] [ j ] dp[i][j] dp[i][j]:表示第 i i i个字符,标签为 j j j 的所有路径中的最大概率。
    2. 记录路径 p a t h [ i ] [ j ] path[i][j] path[i][j]:表示 d p [ i ] [ j ] dp[i][j] dp[i][j]为最大概率时,第 i − 1 i-1 i1个字符的标签
    3. 状态初始化: d p [ 0 ] [ j ] = s t a r t ( j ) e m i t ( j , w 0 ) dp[0][j] =start(j) emit(j,w_0) dp[0][j]=start(j)emit(j,w0)
    4. 递推(状态转移方程): d p [ i ] [ j ] = m a x k ∈ { p o s } ⁡ ( d p [ i − 1 ] [ k ] × t r a n s [ k , j ] ) × e m i t [ j , w i ] dp[i][j]= max_{k\in \{pos\}}⁡(dp[i-1][k]×trans[k,j]) × emit[j,w_i] dp[i][j]=maxk{pos}(dp[i1][k]×trans[k,j])×emit[j,wi]
    5. 记录路径: p a t h [ i ] [ j ] = a r g ⁡ m a x k ∈ { p o s } ⁡ ( d p [ i − 1 ] [ k ] × t r a n s [ k , j ] ) path[i][j]=arg⁡max_{k∈\{pos\}}⁡(dp[i-1][k]×trans[k,j]) path[i][j]=argmaxk{pos}(dp[i1][k]×trans[k,j])
    6. 回溯最优路径: p i = p a t h [ i + 1 ] [ p ( i + 1 ) ] i = n − 1 , n − 2 , … … 1 , 0 p_i=path[i+1][p_(i+1) ] \quad i=n-1,n-2,……1,0 pi=path[i+1][p(i+1)]i=n1,n2,1,0
    7. 输出最优路径: [ p 1 , p 2 … … p n ] [p_1,p_2……p_n] [p1,p2pn]

(2)程序结构

import time
import json
import pandas as pdclass Hmm:def __init__(self):self.trans_p = {'S': {}, 'B': {}, 'M': {}, 'E': {}}self.emit_p = {'S': {}, 'B': {}, 'M': {}, 'E': {}}self.start_p = {'S': 0, 'B': 0, 'M': 0, 'E': 0}self.state_num = {'S': 0, 'B': 0, 'M': 0, 'E': 0}self.state_list = ['S', 'B', 'M', 'E']self.line_num = 0self.smooth = 1e-6@staticmethoddef __state(word):"""获取词语的BOS标签,标注采用 4-tag 标注方法,tag = {S,B,M,E},S表示单字为词,B表示词的首字,M表示词的中间字,E表示词的结尾字Args:word (string): 函数返回词语 word 的状态标签"""if len(word) == 1:state = ['S']else:state = list('B' + 'M' * (len(word) - 2) + 'E')return statedef train(self, filepath, save_model=False):"""训练hmm, 学习发射概率、转移概率等参数Args:save_model: 是否保存模型参数filepath (string): 训练预料的路径"""print("正在训练模型……")start_time = time.thread_time()with open(filepath, 'r', encoding='utf8') as f:for line in f.readlines():self.line_num += 1line = line.strip().split()# 获取观测(字符)序列char_seq = list(''.join(line))# 获取状态(BMES)序列state_seq = []for word in line:state_seq.extend(self.__state(word))# 判断是否等长assert len(char_seq) == len(state_seq)# 统计参数for i, s in enumerate(state_seq):self.state_num[s] = self.state_num.get(s, 0) + 1.0self.emit_p[s][char_seq[i]] = self.emit_p[s].get(char_seq[i], 0) + 1.0if i == 0:self.start_p[s] += 1.0else:last_s = state_seq[i - 1]self.trans_p[last_s][s] = self.trans_p[last_s].get(s, 0) + 1.0# 归一化:self.start_p = {k: (v + 1.0) / (self.line_num + 4)for k, v in self.start_p.items()}self.emit_p = {k: {w: num / self.state_num[k]for w, num in dic.items()}for k, dic in self.emit_p.items()}self.trans_p = {k1: {k2: num / self.state_num[k1]for k2, num in dic.items()}for k1, dic in self.trans_p.items()}end_time = time.thread_time()print("训练完成,耗时 {:.3f}s".format(end_time - start_time))# 保存参数if save_model:parameters = {'start_p': self.start_p,'trans_p': self.trans_p,'emit_p': self.emit_p}jsonstr = json.dumps(parameters, ensure_ascii=False, indent=4)param_filepath = "./data/HmmParam_Token.json"with open(param_filepath, 'w', encoding='utf8') as jsonfile:jsonfile.write(jsonstr)def viterbi(self, text):"""Viterbi 算法Args:text (string): 句子Returns:list: 最优标注序列"""text = list(text)dp = pd.DataFrame(index=self.state_list)# 初始化 dp 矩阵 (prop,last_state)dp[0] = [(self.start_p[s] * self.emit_p[s].get(text[0], self.smooth),'_start_') for s in self.state_list]# 动态规划地更新 dp 矩阵for i, ch in enumerate(text[1:]):  # 遍历句子中的每个字符 chdp_ch = []for s in self.state_list:  # 遍历当前字符的所有可能状态emit = self.emit_p[s].get(ch, self.smooth)# 遍历上一个字符的所有可能状态,寻找经过当前状态的最优路径(prob, last_state) = max([(dp.loc[ls, i][0] * self.trans_p[ls].get(s, self.smooth) *emit, ls) for ls in self.state_list])dp_ch.append((prob, last_state))dp[i + 1] = dp_ch# 回溯最优路径path = []end = list(dp[len(text) - 1])back_point = self.state_list[end.index(max(end))]path.append(back_point)for i in range(len(text) - 1, 0, -1):back_point = dp.loc[back_point, i][1]path.append(back_point)path.reverse()return pathdef cut(self, text):"""根据 viterbi 算法获得状态,根据状态切分句子Args:text (string): 待分词的句子Returns:list: 分词列表"""state = self.viterbi(text)cut_res = []begin = 0for i, ch in enumerate(text):if state[i] == 'B':begin = ielif state[i] == 'E':cut_res.append(text[begin:i + 1])elif state[i] == 'S':cut_res.append(text[i])return cut_res# if __name__ == "__main__":
#     hmm = Hmm()
#     hmm.train('./data/PeopleDaily_Token.txt', save_model=True)
#     cutres = hmm.cut('中央电视台收获一批好剧本')
#     print(cutres)

2、基于字典的分词方法(最短路分词模型)

(1) 算法设计

最短路分词模型的主要思想是将句子中的所有字符当作节点,根据字典找出句子中所有的词语,将词语两端的字符连接起来,构成从词首指向词尾的一条边。通过找出所有的候选词,构建出一个有向无环图(DAG)。找到从句首字符到句尾字符的最短路径,即可作为句子的分词结果。最短路径分词方法采用的规则使切分出来的词数最少,符合汉语自身的规律。

最短路分词算法,由以下几个步骤实现:

  1. 构造句子的切分图,如果句子 s e n t e n c e sentence sentence 的子串 w [ i : j ] w[i:j] w[i:j] 在词典中,则添加边 V ( i , j ) V(i,j) V(i,j),得到句子的有向无环图 DAG
  2. 采用Dijkstra 算法动态规划地求解最短路径, d p [ i ] dp[i] dp[i] 表示DAG中句首到第 i i i 个字符的路径长度
  3. 状态转移函数如下: d p [ i ] = m i n d p [ j − 1 ] + 1 dp[i] = min{dp[j-1] + 1} dp[i]=mindp[j1]+1 ;其中: i i i 为当前边的起点, j j j 为当前边的终点。
  4. 回溯最优路径

(2)程序结构

import json
import math
import timeclass ShortTokenizer:def __init__(self, use_freq=True):self.word_freq = {}self.word_num = 0self.use_freq = use_freqdef train(self, filepath, trained=False):"""根据训练语料统计词频Args:filepath (string): 训练语料文件路径trained (bool): 模型是否已经训练"""if not trained:# 统计词频print("正在训练模型……")stime = time.thread_time()with open(filepath, 'r', encoding='utf8') as f:for line in f.readlines():line = line.strip().split()self.word_num += len(line)self.word_freq.update({i: self.word_freq.get(i, 0) + 1 for i in line})etime = time.thread_time()print("训练完成,耗时{}s".format(etime - stime))# 保存词频jsonstr = json.dumps(self.word_freq, ensure_ascii=False, indent=4)with open('./data/word_freq_npath.json', 'w',encoding='utf8') as f:f.write(jsonstr)else:# 读入词频with open(filepath, 'r', encoding='utf8') as f:jsonstr = ''.join(f.readlines())self.word_freq = json.loads(jsonstr)self.word_num = sum(self.word_freq.values())def __weight(self, word):"""计算word的词频 -log(P(w)) = log(num) - log(k_w)Args:word (string): 切分的词语,切分图上的一条边Returns:float: 词典中存在该词返回 -log(P),否则返回0"""freq = self.word_freq.get(word, 0)if freq and self.use_freq:return math.log(self.word_num) - math.log(freq)elif freq:return 1else:return 0def Token(self, sentence):"""结合统计信息的最短路分词函数(最大概率分词)Args:sentence (string): 待切分的句子Returns:list: 切分的词语,构成的 list"""length = len(sentence)# 构造句子的切分图graph = {}for i in range(length):graph[i] = []for j in range(i):freq = self.__weight(sentence[j:i + 1])if freq:graph[i].append((j, freq))# 动态规划求解最优路径 ( arg min[-log(P)] )# 初始化DP矩阵dp = [(i, self.__weight(sentence[i])) for i in range(length)]dp.insert(0, (-1, 0))# 状态转移函数:dp[i] = min{dp[j-1] + weight(sentence[j:i])}# i:为当前词的词尾;j: 为当前词的词头for i in range(2, len(dp)):index = dp[i][0]cost = dp[i][1] + dp[i - 1][1]for j, freq in graph[i - 1]:if freq + dp[j][1] < cost:cost = freq + dp[j][1]index = jdp[i] = (index, cost)# 回溯最优路径token_res = []break_p = lengthwhile break_p > 0:token_res.append(sentence[dp[break_p][0]:break_p])break_p = dp[break_p][0]token_res.reverse()return token_res# if __name__ == "__main__":
#     Tokenizer = ShortTokenizer()
#     # Tokenizer.train('./data/PeopleDaily_Token.txt')
#     Tokenizer.train('./data/word_freq_npath.json', trained=True)
#     Tokenizer.Token('迈向充满希望的新世纪')
#     Tokenizer.Token('1997年,是中国发展历史上非常重要的很不平凡的一年。')

3、改进最短路分词模型(最大概率模型)

(1)算法设计

最短路分词方法构建有向无环图DAG的过程中,只要词语在字典中出现即可添加边,忽略了成词的概率。现在考虑成词的概率,通过极大似然估计,以词频表示成词概率,为DAG的每条边赋予权重,优化分词结果。通过 Dijkstra 算法求得的带权最短路径即为所有分词结果中概率最大的分词方法。该分词方法本质上是使用了1-gram文法的最大概率分词模型。

(2)程序结构

同最短路分词模型的实现程序,实例化模型时传入 use_freq = True 参数。

4、隐马尔可夫模型进行词性标注

(1)算法设计

词性标注是序列标注问题,可采用Hmm模型的解码问题的解决方法。将词性序列作为隐藏序列,将词语序列作为观测序列,同过Viterbi算法预测最优的词性序列。算法实现步骤同 1、基于统计的分词方法(隐马尔可夫模型)

(2)程序结构

import json
import math
import pandas as pdclass HmmPosTag:def __init__(self):self.trans_prop = {}self.emit_prop = {}self.start_prop = {}self.poslist = []self.trans_sum = {}self.emit_sum = {}def __upd_trans(self, curpos, nxtpos):"""更新转移概率矩阵Args:curpos (string): 当前词性nxtpos (string): 下一词性"""if curpos in self.trans_prop:if nxtpos in self.trans_prop[curpos]:self.trans_prop[curpos][nxtpos] += 1else:self.trans_prop[curpos][nxtpos] = 1else:self.trans_prop[curpos] = {nxtpos: 1}def __upd_emit(self, pos, word):"""更新发射概率矩阵Args:pos (string): 词性word (string): 词语"""if pos in self.emit_prop:if word in self.emit_prop[pos]:self.emit_prop[pos][word] += 1else:self.emit_prop[pos][word] = 1else:self.emit_prop[pos] = {word: 1}def __upd_start(self, pos):"""更新初始状态矩阵Args:pos (string): 初始词语的词性"""if pos in self.start_prop:self.start_prop[pos] += 1else:self.start_prop[pos] = 1def train(self, data_path):"""训练 hmm 模型、求得转移矩阵、发射矩阵、初始状态矩阵Args:data_path (string): 训练数据的路径"""f = open(data_path, 'r', encoding='utf-8')for line in f.readlines():line = line.strip().split()# 统计初始状态的概率self.__upd_start(line[0].split('/')[1])# 统计转移概率、发射概率for i in range(len(line) - 1):self.__upd_emit(line[i].split('/')[1], line[i].split('/')[0])self.__upd_trans(line[i].split('/')[1], line[i + 1].split('/')[1])i = len(line) - 1self.__upd_emit(line[i].split('/')[1], line[i].split('/')[0])f.close()# 记录所有的 posself.poslist = list(self.emit_prop.keys())self.poslist.sort()# 统计 trans、emit 矩阵中各个 pos 的归一化分母num_trans = [sum(self.trans_prop[key].values()) for key in self.trans_prop]self.trans_sum = dict(zip(self.trans_prop.keys(), num_trans))num_emit = [sum(self.emit_prop[key].values()) for key in self.emit_prop]self.emit_sum = dict(zip(self.emit_prop.keys(), num_emit))def predict(self, sentence):"""Viterbi 算法预测词性Args:sentence (string): 分词后的句子(空格隔开)Returns:list: 词性标注序列 """sentence = sentence.strip().split()posnum = len(self.poslist)dp = pd.DataFrame(index=self.poslist)path = pd.DataFrame(index=self.poslist)# 初始化 dp 矩阵(DP 矩阵: posnum * wordsnum 存储每个 word 每个 pos 的最大概率)start = []num_sentence = sum(self.start_prop.values()) + posnumfor pos in self.poslist:sta_pos = self.start_prop.get(pos, 1e-16) / num_sentencesta_pos *= (self.emit_prop[pos].get(sentence[0], 1e-16) /self.emit_sum[pos])sta_pos = math.log(sta_pos)start.append(sta_pos)dp[0] = start# 初始化 path 矩阵path[0] = ['_start_'] * posnum# 递推for t in range(1, len(sentence)):  # 句子中第 t 个词prob_pos, path_point = [], []for i in self.poslist:  # i 为当前词的 posmax_prob, last_point = float('-inf'), ''emit = math.log(self.emit_prop[i].get(sentence[t], 1e-16) / self.emit_sum[i])for j in self.poslist:  # j 为上一次的 postmp = dp.loc[j, t - 1] + emittmp += math.log(self.trans_prop[j].get(i, 1e-16) / self.trans_sum[j])if tmp > max_prob:max_prob, last_point = tmp, jprob_pos.append(max_prob)path_point.append(last_point)dp[t], path[t] = prob_pos, path_point# 回溯prob_list = list(dp[len(sentence) - 1])cur_pos = self.poslist[prob_list.index(max(prob_list))]path_que = []path_que.append(cur_pos)for i in range(len(sentence) - 1, 0, -1):cur_pos = path[i].loc[cur_pos]path_que.append(cur_pos)# 返回结果postag = []for i in range(len(sentence)):postag.append(sentence[i] + '/' + path_que[-i - 1])return postagif __name__ == "__main__":# data_clean()hmm = HmmPosTag()hmm.train("./data/PeopleDaily_clean.txt")hmm.predict("在 这 一 年 中 , 中国 的 改革 开放 和 现代化 建设 继续 向前 迈进  再次 获得 好 的 收成 ")# 1. 语料库中有 26 个基本词类标记
#       形容词a、区别词b、连词c、副词d、叹词e、方位词f、语素g、前接成分h、成语i、
#       简称j、后接成分k、习惯用语l、数词m、名词n、拟声词o、介词p、量词q、代词r、
#       处所词s、时间词t、助词u、动词v、标点符号w、非语素字x、语气词y、状态词z、
#
#
# 2. 语料库中还有 74 个扩充标记:对于语素,具体区分为 Ag Bg Dg Mg Ng Rg Tg Vg Yg
#
#
# 3. 词性标注只标注基本词性,因此在数据清洗的过程中,将扩充标记归类到各个基本词类中,语素也归类到相应词类中

5、实验结果评估

采用1998年人民日报语料库进行评估,分别用以上算法实现分词、词性标注。评价指标包括精确率precision、召回率recall、F1分数、算法效率。

(1) 对分词模型进行评估

选取语料库中的6000行数据进行评估,运行结果如下图:
在这里插入图片描述
由评估结果可知,最大概率分词模型效果最优,相较于最短路径模型有3% 的提升;Hmm 模型运行效率远低于其他两个模型,且效果不佳。

(2) 对词性标注模型进行评估

选取语料库中的2000行数据进行评估,运行结果如下图:
在这里插入图片描述
运行结果第一项是对标准分词结果进行词性标注,运行结果第二项是对最大概率分词模型的预测结果进行分词(只有当词语被正确划分出并且词性标注争取时才会被标记为预测正确)

6、问题及解决方法

  1. 问题:Hmm 模型中,大部分词语的发射概率较低,随着句子长度的增加(约为120词),路径的概率变得很小,程序下溢。路径概率取对数,概率相乘转化为对数相加,避免路径概率下溢
  2. 对于 Hmm 模型中出现的未登录词(字)采用 Laplace 平滑处理。由于某些字、词出现很少,如果采用加一平滑会导致发射概率过大的问题,因此采用较小的 λ = 1e-6
  3. 预料库将人名的姓和名拆分成两个词,将组合的实体名也有拆分,在数据清洗时,我将这两类进行了合并。λ

http://chatgpt.dhexx.cn/article/k0PF0Ek9.shtml

相关文章

【生信算法】利用HMM纠正测序错误(Viterbi算法的python实现)

利用HMM纠正测序错误&#xff08;Viterbi算法的python实现&#xff09; 问题背景 对两个纯系个体M和Z的二倍体后代进行约~0.05x的低覆盖度测序&#xff0c;以期获得后代个体的基因型&#xff0c;即后代中哪些片段分别来源于M和Z。已知&#xff1a; 后代中基因型为MM、MZ&…

Viterbi算法实现中文分词和词性标注

Viterbi算法 目标过程词典分词统计分词词性标注 附录附录二附录三 源码地址 目标 实现基于词典的分词方法和统计分词方法对分词结果进行词性标注对分词及词性标注结果进行评价&#xff0c;包括4个指标&#xff1a;正确率、召回率、F1值和效率 过程 词典分词 基于词典的分词…

viterbi 算法与python实现

Viterbi算法 &#xff08;部分内容转自知乎&#xff1a;《如何通俗地讲解 viterbi 算法&#xff1f;》&#xff09; 1、问题描述 如下如所示&#xff0c;如何快速找到从 S 到 E 的最短路径&#xff1f; 一&#xff1a;遍历穷举法&#xff0c;可行&#xff0c;但速度太慢&am…

维特比算法(viterbi)原理以及简单实现

维特比算法 看一下维基百科的解释&#xff0c;维特比算法&#xff08;Viterbi algorithm&#xff09;是一种动态规划算法。它用于寻找最有可能产生观测事件序列的维特比路径——隐含状态序列&#xff0c;特别是在马尔可夫信息源上下文和隐马尔可夫模型中。 通俗易懂的解释知乎…

flask中jsonify遇到的坑

1.jsonify可以将字典转换成json对象传入前端 data {"movie": movie_list,"page": page,"dic_list": dic,"total_page": total_page}>>坑1 字典的值不能为range(x,x)&#xff0c;上图dic就是像range(x,x)&#xff0c;会报错 …

flask中的jsonify返回的是乱码

用flask返回json时遇到了返回字符串支持中文显示的问题&#xff0c;在web端显示的是utf-8的编码如图; 虽然不影响接口的读取&#xff0c;但是可读性太差&#xff0c;于是研究了一下怎么直接显示成中文。最后找到了解决方案如下&#xff0c;在配置中加入下面一行代码就OK了。 …

request jsonify

python的flask框架为用户提供了直接返回包含json格式数据响应的方法&#xff0c;即jsonify&#xff0c;在开发中会经常用到。如下一段简单的flask后端代码&#xff0c;服务端视图函数根据请求参数返回json格式的数据到客户端。 转载于:https://www.cnblogs.com/daqingzi/p/9018…

Flask使用json或jsonify返回响应的数据

前言 在做网站前后端数据交互的时候&#xff0c;我们经常需要使用Ajax等方法向后端发送请求来获取响应的数据&#xff0c;而我们经常需要的就是json格式的响应数据&#xff0c;它实际上就是一个字符串。要知道Flask如何返回json响应数据&#xff0c;首先就需要知道如何将字典di…

Flask 学习-8. jsonify返回中文没正常显示问题

前言 Flask 接口返回的json 格式数据有中文的时候&#xff0c;默认是以ASCII码 返回的&#xff0c;没正常显示中文。 jsonify 返回 json 数据 函数直接返回dict 数据 或返回jsonfy() 函数处理的数据&#xff0c;都是以json格式返回的 from flask import Flask, jsonify fro…

flask jsonify之序列化时的default函数、jsonify序列化自定义对象

目录 1.看源码 2、重写默认的default函数&#xff0c;实现自己的序列化机制 3、把对象转化成字典 3.1 __dict__的方式 3.2、定义keys和__getitem__的方式 4、最终的代码实现 5、关于default函数的其他知识 1.看源码 打开site-package&#xff0c;flask&#xff0c;json…

Flask | 解决jsonify返回中文乱码问题

在采用 return jsonify(data) 返回内容中含有中文时&#xff0c;前端接收数据出现中文乱码问题&#xff0c;乱码格式如下&#xff08;仅中文为ASCII码&#xff09;&#xff1a; 故在此记录下该问题的解决方式&#xff0c;以作后期参考&#xff1a; 在定义Flask app时&#xff…

jsonify返回中文编码的问题

app.config[JSON_AS_ASCII]Flase 在它下面加上上面的代码就欧克了 没加之前或者为True&#xff1a; 加了之后&#xff1a;

flask学习二(jsonify)

示例一&#xff1a; 实现动态路由&#xff0c;代码如下 # coding:utf-8 from flask import Flask from flask import jsonify # 创建对象 app Flask(__name__)users_list {"1001":["123","张三",19],"1002":["234","…

flask中jsonify和json区别

一 JSON数据结构 要把json与字典区分开来 dumps(字典转换成Json) loads(Json转换成字典) Python 的字典是一种数据结构&#xff0c;JSON 是一种数据格式。 json 就是一个根据某种约定格式编写的纯字符串&#xff0c;不具备任何数据结构的特征。而 python 的字典…

关于flask入门教程-记录集转jsonify

Flask 框架里&#xff0c;可以用 jsonify 返回 json 数据&#xff0c;但是为什么不用 Python 自带的 json 模块返回 JSON 数据呢&#xff1f; 其实两者是差不多的&#xff0c;jsonify指明了Content-Type 是 application/json &#xff0c; 这样做是符合 HTTP 协议的规定的&…

Flask 的 jsonify 理解

文章目录 python 代码解决原因Content-Type的区别 python 代码 # -*- coding:utf-8 -*- from flask import Flask, jsonifyapp Flask(__name__)urls [{id: 1,title: python,description: https://www.python.org/},{id: 2,title: flask,description: https://flask.palletsp…

Flask中jsonify和json.dumps用法以及区别(简单案例)

环境&#xff1a;python3.6, Flask1.0.3 flask提供了jsonify函数供用户处理返回的序列化json数据&#xff0c; 而python自带的json库中也有dumps方法可以序列化json对象. 其二者的区别&#xff0c;写个简单的案例实测一下便见分晓。 from flask import Flask from flask im…

const常量函数详解

在类中&#xff0c;如果你不希望某些数据被修改&#xff0c;可以使用const关键字加以限定。const 可以用来修饰成员变量和成员函数。 const常量与指针 const int *p1与const int *p2的顺序不同&#xff0c;但是他们指向的值都不能改变&#xff0c;上述代码说明虽然指向的值不能…

C++const函数的运用:深度解析const函数的魅力

C 深度解析const函数的魅力 1. C const函数的基本概念&#xff08;Basic Concepts of const Functions in C&#xff09;1.1 const函数的定义与特性&#xff08;Definition and Characteristics of const Functions&#xff09;1.2 const函数的使用场景&#xff08;Usage Scena…

const 用法

const 用法 const 修饰变量&#xff0c;这个变量被称为常变量&#xff0c;不能被修改&#xff0c;但本质上还是一个变量 #通过指针改变num的值 int main() {int num 10;int* p &num;*p 20;printf("%d ", num);return 0; }#这里num被 const修饰本来不能被修改…