最新国产好看的视频,伊人天堂AV在线,国产Aaaaaa视频,蜜臀视频在线观看一区,人妻av色图,密臀久久久精品影片,青青视频免费观看毛片,久草在线观看视,国产三级精品色情在线

Python處理yaml和嵌套數(shù)據(jù)結(jié)構(gòu)技巧示例

 更新時(shí)間:2022年06月23日 10:49:29   作者:youerning  
這篇文章主要為大家介紹了Python處理yaml和嵌套數(shù)據(jù)結(jié)構(gòu)技巧示例,有需要的朋友可以借鑒參考下,希望能夠有所幫助,祝大家多多進(jìn)步,早日升職加薪

引言

分享一些用Python處理yaml和嵌套數(shù)據(jù)結(jié)構(gòu)的的一些技巧,首先從修改yaml格式文件的問(wèn)題出發(fā),演變出了各個(gè)解決辦法,又從最后的解決辦法中引申出了普適性更強(qiáng)的嵌套數(shù)據(jù)結(jié)構(gòu)的定位方法。

  • 保留注釋修改yaml文件
  • 定位嵌套數(shù)據(jù)結(jié)構(gòu)
  • 定位嵌套數(shù)據(jù)結(jié)構(gòu)2

保留注釋修改yaml文件

yaml比之json文件的其中一個(gè)區(qū)別就是可以注釋?zhuān)@些注釋有時(shí)候是很重要的內(nèi)容,就像代碼中的注釋一樣,如果是手動(dòng)編輯自然是沒(méi)有問(wèn)題的,那么如何在保留注釋的情況下用代碼修改yaml文件呢?

假設(shè)我們要修改的yaml文件如下:

# 主要維護(hù)人
name: zhangsan

# 各集群運(yùn)維人員
cluster1:
  node1:
    tomcat: user11

cluster2:
  node1:
    tomcat: user21

不保留注釋

為了演示處理yaml的各個(gè)方法,這里把不保留注釋的方法也納入到本文了。

def ignore_comment():
    data = yaml.load(text, Loader=yaml.Loader)
    data["name"] = "wangwu"
    print(yaml.dump(data))

輸出如下:

cluster1:
  node1:
    tomcat: user11
cluster2:
  node1:
    tomcat: user21
name: wangwu

很顯然,這不是我們要的結(jié)果, 那么就淘汰這個(gè)方法吧。

此方法只適用于不需要保留注釋的修改。

正則表達(dá)式

既然load, dump方法會(huì)丟棄注釋?zhuān)敲从谜齽t表達(dá)式不就可以了么,處理文本一定有正則表達(dá)式一席之地的。

假設(shè)還是將name: zhangsan改成name: wangwu。

def regex1():
    pattern = "name:\s+\w+"
    pat = re.compile(pattern=pattern)
    # 首先匹配到對(duì)應(yīng)的字符串
    sub_text = pat.findall(text)[0]
    # 根據(jù)這個(gè)字符串找到在文本的位置
    start_index = text.index(sub_text)
    # 根據(jù)起始位置計(jì)算結(jié)束位置
    end_index = start_index + len(sub_text)
    print(start_index, end_index, text[start_index:end_index])

    # 將根據(jù)索引替換內(nèi)容
    replace_text = "name: wangwu"
    new_text = text[:start_index] + replace_text + text[end_index:]
    print("="*10)
    print(new_text)

輸出如下:

8 22 name: zhangsan
==========
# 主要維護(hù)人
name: wangwu
# 各集群運(yùn)維人員
cluster1:
  node1:
    tomcat: user11
cluster2:
  node1:
    tomcat: user21

看起來(lái)不錯(cuò),好像能夠滿足需求,但是這里有一個(gè)問(wèn)題就是,假設(shè)修改是cluster2.node1.tomcat的值呢?

因?yàn)槲谋局杏袃蓚€(gè)tomcat的值,所以只是通過(guò)正則表達(dá)式不能一擊即中,需要多一些判斷條件,比如首先找到cluster2的起始位置,然后過(guò)濾掉小于這個(gè)起始位置的索引值,但是如果還有cluster3,cluster4呢?總的來(lái)說(shuō)還 是需要人工的過(guò)一遍,然后根據(jù)觀察結(jié)果來(lái)編寫(xiě)正則表達(dá)式,但是這樣太不智能,太不自動(dòng)了。

此方法適用于比較容器匹配的文本。

語(yǔ)法樹(shù)

其實(shí)整個(gè)文本的數(shù)據(jù)結(jié)構(gòu)大致如下:

無(wú)論是編程語(yǔ)言還是數(shù)據(jù)文本,如json, yaml, toml都可以得到這樣的語(yǔ)法樹(shù),通過(guò)搜索這顆語(yǔ)法樹(shù),我們就能找到對(duì)應(yīng)的鍵值對(duì)。

def tree1():
    tree = yaml.compose(text)
    print(tree)

輸出如下:

MappingNode(tag='tag:yaml.org,2002:map', value=[(ScalarNode(tag='tag:yaml.org,2002:str', value='name'), ScalarNode(tag='tag:yaml.org,2002:str', value='zhangsan')), (ScalarNode(tag='tag:yaml.org,2002:str', value='cluster1'), MappingNode(tag='tag:yaml.org,2002:map', value=[(ScalarNode(tag='tag:yaml.org,2002:str', value='node1'), MappingNode(tag='tag:yaml.org,2002:map', value=[(ScalarNode(tag='tag:yaml.org,2002:str', value='tomcat'), ScalarNode(tag='tag:yaml.org,2002:str', value='user11'))]))])), (ScalarNode(tag='tag:yaml.org,2002:str', value='cluster2'), MappingNode(tag='tag:yaml.org,2002:map', value=[(ScalarNode(tag='tag:yaml.org,2002:str', value='node1'), MappingNode(tag='tag:yaml.org,2002:map', value=[(ScalarNode(tag='tag:yaml.org,2002:str', value='tomcat'), ScalarNode(tag='tag:yaml.org,2002:str', value='user21'))]))]))])

通過(guò)yaml.compose方法我們就能得到一顆節(jié)點(diǎn)樹(shù),并且每個(gè)節(jié)點(diǎn)會(huì)包括該節(jié)點(diǎn)的文本信息,比如起始,終止的文本索引。

通過(guò)觀察我們能找到name: zhangsan的兩個(gè)節(jié)點(diǎn), 鍵name是一個(gè)ScalarNode節(jié)點(diǎn), 值z(mì)hangsan也是一個(gè)ScalarNode, 所以我們可以打印一下看看是否和正則表達(dá)式的結(jié)果一致。

def tree2():
    tree = yaml.compose(text)
    key_name_node = tree.value[0][0]
    value_name_node = tree.value[0][1]
    print(key_name_node.start_mark.pointer, value_name_node.end_mark.pointer, key_name_node.value, value_name_node.value)

輸出如下:

8 22 name zhangsan

結(jié)果與正則表達(dá)式一致,所以說(shuō)明這種方法可行并且準(zhǔn)確。

得到了修改文本的索引位置,就可以替換了,這里就不再演示了。

此方法適合保留注釋的修改,并且定位嵌套結(jié)構(gòu)較之正則表達(dá)式要簡(jiǎn)單,并且不需要人工介入。

那么如何定位嵌套結(jié)構(gòu)呢?

定位嵌套數(shù)據(jù)結(jié)構(gòu)

從上一節(jié)我們了解到了數(shù)據(jù)結(jié)構(gòu)可以抽象成一顆語(yǔ)法樹(shù), 那么利用一些樹(shù)的搜索算法就可以定位到目標(biāo)文本了。

這里展示一下不包含列表節(jié)點(diǎn)的搜索算法。

def find_slice(tree: yaml.MappingNode, keys: List[str]) -> Tuple[Tuple[int, int], Tuple[int, int]]:
    """
    找到y(tǒng)aml文件中對(duì)應(yīng)鍵值對(duì)的索引, 返回一個(gè)((key起始索引, key結(jié)束索引+1), (value起始索引, value結(jié)束索引+1))的元組
    暫時(shí)只支持鍵值對(duì)的尋找.

    比如:
    >>> find_slice("name: zhangsan", ["name"])
    ((0, 4), (6, 14))
    """
    if isinstance(tree, str):
        tree = yaml.compose(tree, Loader=yaml.Loader)
    assert isinstance(tree, yaml.MappingNode), "未支持的yaml格式"
    target_key = keys[0]
    for node in tree.value:
        if target_key == node[0].value:
            key_node, value_node = node
            if len(keys) == 1:
                key_pointers = (key_node.start_mark.pointer, key_node.end_mark.pointer)
                value_pointers = (value_node.start_mark.pointer, value_node.end_mark.pointer)
                return (key_pointers, value_pointers)

            return find_slice(node[1], keys[1:])

    return ValueError("沒(méi)有找到對(duì)應(yīng)的值")

算法核心在于遞歸。

這里的實(shí)現(xiàn)并沒(méi)有處理列表節(jié)點(diǎn)(SequenceNode)。

假設(shè)我們要找cluster1.node1.tomcat并將其值改成changed, 代碼如下:

def tree3():
    slices = find_slice(text, ["cluster1", "node1", "tomcat"])
    value_start_index, value_end_index = slices[1]
    replace_text = "changed"
    new_text = text[:value_start_index] + replace_text + text[value_end_index:]
    print(new_text)

輸出如下

# 主要維護(hù)人
name: zhangsan

# 各集群運(yùn)維人員
cluster1:
  node1:
    tomcat: changed

cluster2:
  node1:
    tomcat: user21

上面的算法只能定位key-value類(lèi)型的數(shù)據(jù)結(jié)構(gòu),現(xiàn)在在此優(yōu)化一下,讓其 支持序列。

def find_slice2(tree: yaml.MappingNode, keys: List[str]) -> Tuple[Tuple[int, int], Tuple[int, int]]:
    """
    找到y(tǒng)aml文件中對(duì)應(yīng)鍵值對(duì)的索引, 返回一個(gè)((key起始索引, key結(jié)束索引+1), (value起始索引, value結(jié)束索引+1))的元組
    暫時(shí)只支持鍵值對(duì)的尋找.

    比如:
    >>> find_slice2("name: zhangsan", ["name"])
    ((0, 4), (6, 14))
    """
    if isinstance(tree, str):
        tree = yaml.compose(tree, Loader=yaml.Loader)
    target_key = keys[0]

    assert isinstance(tree, yaml.MappingNode) or isinstance(tree, yaml.SequenceNode), "未支持的yaml格式"

    ret_key_node = None
    ret_value_node = None
    value_pointers= (-1, -1)

    if isinstance(tree, yaml.SequenceNode):
        assert isinstance(target_key, int), "錯(cuò)誤的數(shù)據(jù)格式"
        # 索引可以是負(fù)索引, 比如[1,2,3][-1]
        if len(tree.value) < abs(target_key):
            raise IndexError("索引值大于列表長(zhǎng)度")

        node = tree.value[target_key]
        if len(keys) > 1:
            return find_slice2(tree.value[target_key], keys[1:])

        if isinstance(node, yaml.MappingNode):
            ret_key_node, ret_value_node = node.value[0]
        else:
            ret_key_node = node

    if isinstance(tree, yaml.MappingNode):
        for node in tree.value:
            if target_key == node[0].value:
                key_node, value_node = node
                if len(keys) > 1:
                    return find_slice2(node[1], keys[1:])
                ret_key_node = key_node
                ret_value_node = value_node

    if ret_key_node:
        key_pointers = (ret_key_node.start_mark.pointer, ret_key_node.end_mark.pointer)

    if ret_value_node:
        value_pointers = (ret_value_node.start_mark.pointer, ret_value_node.end_mark.pointer)

    if ret_key_node:
        return (key_pointers, value_pointers)

    return ValueError("沒(méi)有找到對(duì)應(yīng)的值")

 假設(shè)yaml文件如下:

# 用戶列表
users:
  - user1: wangwu
  - user2: zhangsan

# 集群中間件版本
cluster:
  - name: tomcat
    version: 9.0.63
  - name: nginx
    version: 1.21.6
def tree4():
    slices = find_slice2(text2, ["cluster", 1, "version"])
    value_start_index, value_end_index = slices[1]
    replace_text = "1.22.0"
    new_text = text2[:value_start_index] + replace_text + text2[value_end_index:]
    print(new_text)

輸出如下:

# 用戶列表
users:
  - user1: wangwu
  - user2: zhangsan

# 集群中間件版本
cluster:
  - name: tomcat
    version: 9.0.63
  - name: nginx
    version: 1.22.0

結(jié)果符合預(yù)期。

定位嵌套數(shù)據(jù)結(jié)構(gòu)2

上面介紹了如何定位嵌套的數(shù)據(jù)結(jié)構(gòu)樹(shù),這一節(jié)介紹一下如何定位較深的樹(shù)結(jié)構(gòu)(主要指python字典)。

鏈?zhǔn)秸{(diào)用get

在獲取api數(shù)據(jù)的時(shí)候因?yàn)橄胍臄?shù)據(jù)結(jié)構(gòu)比較深,用索引會(huì)報(bào)錯(cuò),那么就 需要捕獲異常,這樣很麻煩,并且代碼很冗長(zhǎng),比如:

data1 = {"message": "success", "data": {"limit": 0, "offset": 10, "total": 100, "data": ["value1", "value1"]}}
data2 = {"message": "success", "data": None}
data3 = {"message": "success", "data": {"limit": 0, "offset": 10, "total": 100, "data": None}}

上面的數(shù)據(jù)結(jié)構(gòu)很有可能來(lái)自同一個(gè)api結(jié)構(gòu),但是數(shù)據(jù)結(jié)構(gòu)卻不太一樣。

如果直接用索引,就需要捕獲異常,這樣看起來(lái)很煩,那么可以利用字典的get方法。

ret = data1.get("data", {}).get("data", [])
if ret:
    pass # 做一些操作
if data2.get("data"):
    ret = data2["data"].get("data", [])
ret = data3.get("data", {}).get("data", [])

通過(guò)給定一個(gè)預(yù)期的數(shù)據(jù)空對(duì)象,讓get可以一致寫(xiě)下去。

寫(xiě)一個(gè)遞歸的get

起始在之前的find_slice方法中,我們就發(fā)現(xiàn)遞歸可以比較好的處理這種嵌套的數(shù)據(jù)結(jié)構(gòu),我們可以寫(xiě)一個(gè)遞歸處理函數(shù),用來(lái)處理很深的數(shù)據(jù)結(jié)構(gòu)。

假設(shè)數(shù)據(jù)結(jié)構(gòu)如下:

data = {"message": "success", "data": {"data": {"name": "zhangsan", "scores": {"math": {"mid-term": 88, "end-of-term": 90}}}}}

我們的目標(biāo)就是獲取數(shù)據(jù)中張三期中數(shù)學(xué)成績(jī): 88

實(shí)現(xiàn)的遞歸調(diào)用如下:

def super_get(data: Union[dict, list], keys: List[Union[str, int]]):
    assert isinstance(data, dict) or isinstance(data, list), "只支持字典和列表類(lèi)型"
    key = keys[0]

    if isinstance(data, list) and isinstance(key, int):
        try:
            new_data = data[key]
        except IndexError as exc:
            raise IndexError("索引值大于列表長(zhǎng)度") from exc
    elif isinstance(data, dict) and isinstance(key, str):
        new_data = data.get(key)
    else:
        raise ValueError(f"數(shù)據(jù)類(lèi)型({type(data)})與索引值類(lèi)型(f{type(key)}不匹配")

    if len(keys) == 1:
        return new_data

    if not isinstance(new_data, dict) and not isinstance(new_data, list):
        raise ValueError("找不到對(duì)應(yīng)的值")
    return super_get(new_data, keys[1:])

然后執(zhí)行代碼:

def get2():
    data = {"message": "success", "data": {"data": {"zhangsan": {"scores": {"math": {"mid-term": 88, "end-of-term": 90}}}}}}
    print(super_get(data, ["data", "data", "zhangsan", "scores", "math", "mid-term"]))
    # 輸出 88
    data = {"message": "success", "data": {"data": {"zhangsan": {"scores": {"math": [88, 90]}}}}}
    print(super_get(data, ["data", "data", "zhangsan", "scores", "math", 0]))
    # 輸出 88
    data = {"message": "success", "data": {"data": {"zhangsan": {"scores": {"math": [88, 90]}}}}}
    print(super_get(data, ["data", "data", "zhangsan", "scores", "math", -1]))
    # 輸出 90 

第三方庫(kù)

其實(shí)有語(yǔ)法比較強(qiáng)大的庫(kù),比如jq, 但是畢竟多了一個(gè)依賴,并且需要一定的學(xué)習(xí)成本,但是,如果確定自己需要更多的語(yǔ)法,那么可以去安裝一下第三方庫(kù)。

總結(jié)

如果遇到較深的嵌套,遞歸總能很好的解決,如果實(shí)在想不出比較好的算法,那就找個(gè)第三方庫(kù)吧,調(diào)庫(kù)嘛,不寒磣。

源碼地址:https://github.com/youerning/blog/tree/master/py_yaml_nested_data

以上就是Python處理yaml和嵌套數(shù)據(jù)結(jié)構(gòu)技巧示例的詳細(xì)內(nèi)容,更多關(guān)于Python處理yaml嵌套數(shù)據(jù)結(jié)構(gòu)的資料請(qǐng)關(guān)注腳本之家其它相關(guān)文章!

相關(guān)文章

  • python 調(diào)用API接口 獲取和解析 Json數(shù)據(jù)

    python 調(diào)用API接口 獲取和解析 Json數(shù)據(jù)

    這篇文章主要介紹了python 如何調(diào)用API接口 獲取和解析 Json數(shù)據(jù),幫助大家更好的理解和使用python,感興趣的朋友可以了解下
    2020-09-09
  • python將unicode和str互相轉(zhuǎn)化的實(shí)現(xiàn)

    python將unicode和str互相轉(zhuǎn)化的實(shí)現(xiàn)

    這篇文章主要介紹了python將unicode和str互相轉(zhuǎn)化的實(shí)現(xiàn),具有很好的參考價(jià)值,希望對(duì)大家有所幫助。一起跟隨小編過(guò)來(lái)看看吧
    2020-05-05
  • python爬蟲(chóng)之urllib3的使用示例

    python爬蟲(chóng)之urllib3的使用示例

    這篇文章主要介紹了 python爬蟲(chóng)之urllib3的使用示例,小編覺(jué)得挺不錯(cuò)的,現(xiàn)在分享給大家,也給大家做個(gè)參考。一起跟隨小編過(guò)來(lái)看看吧
    2018-07-07
  • PyTorch簡(jiǎn)單手寫(xiě)數(shù)字識(shí)別的實(shí)現(xiàn)過(guò)程

    PyTorch簡(jiǎn)單手寫(xiě)數(shù)字識(shí)別的實(shí)現(xiàn)過(guò)程

    Pytorch是熱門(mén)的深度學(xué)習(xí)框架之一,通過(guò)經(jīng)典的MNIST數(shù)據(jù)集進(jìn)行快速的pytorch入門(mén),這篇文章主要給大家介紹了關(guān)于PyTorch簡(jiǎn)單手寫(xiě)數(shù)字識(shí)別的相關(guān)資料,需要的朋友可以參考下
    2021-11-11
  • Python遞歸函數(shù)定義與用法示例

    Python遞歸函數(shù)定義與用法示例

    這篇文章主要介紹了Python遞歸函數(shù)定義與用法,結(jié)合具體實(shí)例形式分析了Python遞歸函數(shù)的原理、實(shí)現(xiàn)技巧與相關(guān)注意事項(xiàng),需要的朋友可以參考下
    2017-06-06
  • Python項(xiàng)目跨域問(wèn)題解決方案

    Python項(xiàng)目跨域問(wèn)題解決方案

    這篇文章主要介紹了Python項(xiàng)目跨域問(wèn)題解決方案,文中通過(guò)示例代碼介紹的非常詳細(xì),對(duì)大家的學(xué)習(xí)或者工作具有一定的參考學(xué)習(xí)價(jià)值,需要的朋友可以參考下
    2020-06-06
  • 淺析python遞歸函數(shù)和河內(nèi)塔問(wèn)題

    淺析python遞歸函數(shù)和河內(nèi)塔問(wèn)題

    這篇文章主要介紹了python遞歸函數(shù)和河內(nèi)塔問(wèn)題,非常不錯(cuò),具有參考借鑒價(jià)值,需要的朋友可以參考下
    2017-04-04
  • 使用Python操作MySQL的小技巧

    使用Python操作MySQL的小技巧

    這篇文章主要介紹了使用Python操作MySQL的小技巧,幫助大家更好的理解和使用python,感興趣的朋友可以了解下
    2020-09-09
  • Python selenium爬取微信公眾號(hào)文章代碼詳解

    Python selenium爬取微信公眾號(hào)文章代碼詳解

    這篇文章主要介紹了Python selenium爬取微信公眾號(hào)歷史文章代碼詳解,文中通過(guò)示例代碼介紹的非常詳細(xì),對(duì)大家的學(xué)習(xí)或者工作具有一定的參考學(xué)習(xí)價(jià)值,需要的朋友可以參考下
    2020-08-08
  • pytorch實(shí)現(xiàn)focal loss的兩種方式小結(jié)

    pytorch實(shí)現(xiàn)focal loss的兩種方式小結(jié)

    今天小編就為大家分享一篇pytorch實(shí)現(xiàn)focal loss的兩種方式小結(jié),具有很好的參考價(jià)值,希望對(duì)大家有所幫助。一起跟隨小編過(guò)來(lái)看看吧
    2020-01-01

最新評(píng)論

郓城县| 平潭县| 浪卡子县| 乌鲁木齐县| 安溪县| 曲靖市| 社旗县| 鄂温| 辛集市| 五华县| 沂南县| 彭泽县| 景洪市| 绥德县| 紫金县| 浦东新区| 岳阳市| 长宁县| 河池市| 武鸣县| 平远县| 芷江| 延长县| 三门峡市| 房产| 铜鼓县| 建湖县| 泽库县| 塔河县| 白银市| 广水市| 双城市| 湟源县| 南涧| 新龙县| 平湖市| 章丘市| 清新县| 亚东县| 南开区| 濮阳县|