簡體   English   中英

來自深層嵌套列表/元組的提取元素的遞歸函數

[英]recursive function for extract elements from deep nested lists/tuples

我想編寫一個從深層嵌套元組和列表中提取元素的函數,比方說我有這樣的東西

l = ('THIS', [('THAT', ['a', 'b']), 'c', ('THAT', ['d', 'e', 'f'])])

我想要一個沒有'THIS'和'THAT'的平面列表:

list = ['a', 'b', 'c', 'd', 'e', 'f']

這是我到目前為止所擁有的:

def extract(List):
    global terms
    terms = []
    for i in word:
        if type(i) is not str:
            extract(i)
        else:
            if i is not "THIS" and i is not "THAT":
                terms.append(i)
    return terms

但我一直得到list = ['d', 'e', 'f'] ,看起來在循環到'c'之后再次設置了terms = []

你正在函數頂部執行terms = [] ,所以當然每次遞歸調用函數時,你都會再次執行這些terms=[]

最快的解決方案是編寫一個簡單的包裝器:

def _extract(List):
    global terms
    for i in word:
        if type(i) is not str:
            _extract(i)
        else:
            if i is not "THIS" and i is not "THAT":
                terms.append(i)
    return terms

def extract(List):
    global terms
    terms = []
    return _extract(List)

還有一件事:你不應該使用is來測試字符串相等性(除非是非常非常特殊的情況)。 這測試它們在內存中相同的字符串對象 它會發生在這里,至少在CPython中(因為兩個"THIS"字符串都是同一模塊中的常量 - 即使它們不是,它們也會被intern ) - 但這不是你想要依賴的東西。 使用== ,它測試它們都意味着相同的字符串,無論它們是否實際上是相同的對象。

測試身份類型更常用,但通常不是你想要的。 實際上,您通常甚至不想測試類型是否相等 你經常沒有str子類 - 如果你這樣做了,你可能想把它們視為str (因為這是子類型的全部要點)。 對於你經常進行子類化的類型來說,這一點更為重要。

如果你還沒有完全理解所有這些,那么簡單的指導就是永遠不要使用is除非你知道你有充分的理由。

所以,改變這個:

if i is not "THIS" and i is not "THAT":

......對此:

if i != "THIS" and i != "THAT":

或者,也許更好的(肯定更好,如果你有,比如說,四根弦檢查,而不是兩個),使用一組成員資格測試,而不是and荷蘭國際集團一起多重考驗:

if i not in {"THIS", "THAT"}:

同樣,改變這個:

if type(i) is not str:

......對此:

if not isinstance(i, str):

但是,雖然我們在這里全部運作,為什么不使用閉包來消除全局?

def extract(List)
    terms = []
    def _extract(List):
        nonlocal terms
        for i in word:
            if not isinstance(i, str):
                _extract(i)
            else:
                if i not in {"THIS", "THAT"}:
                    terms.append(i)
        return terms
    return _extract(List)

這不是我解決這個問題的方式(如果給出這個規范並告訴他用遞歸來解決它,那么我的回答可能就是我所做的),但這有保留(和大部分的)精神的優點。實施)您現有的設計。

將“扁平化”和“過濾”的問題分開是很好的。 解耦代碼更易於編寫且更易於測試。 所以讓我們先用遞歸寫一個“flattener”:

from collections import Iterable

def flatten(collection):
    for x in collection:
        if isinstance(x, Iterable) and not isinstance(x, str):
            yield from flatten(x)
        else:
            yield x

然后提取和黑名單:

def extract(data, exclude=()):
    yield from (x for x in flatten(data) if x not in exclude)

L = ('THIS', [('THAT', ['a', 'b']), 'c', ('THAT', ['d', 'e', 'f'])])
print(*extract(L, exclude={'THIS', 'THAT'}))

假設可以忽略每個元組的第一個元素,並且我們應該使用作為第二個元素的列表進行遞歸,我們可以這樣做:

def extract(node):
    if isinstance(node, tuple):
        return extract(node[1])
    if isinstance(node, list):
        return [item for sublist in [extract(elem) for elem in node] for item in sublist]
    return node

列表理解有點密集,這里與循環相同:

def extract(node):
    if isinstance(node, tuple):
        return extract(node[1])
    if isinstance(node, list):
        result = []
        for item in node:
            for sublist in extract(item):
                for elem in sublist:
                    result.append(elem)
        return result
    return node

這個迭代函數應該與.extend()列表運算符一起完成。

def func(lst):
    new_lst = []
    for i in lst:
        if i != 'THAT' and i != 'THIS':
            if type(i) == list or type(i) == tuple: 
                new_lst.extend(func(i))
            else: new_lst.append(i)
    return new_lst

l = ('THIS', [('THAT', ['a', 'b']), 'c', ('THAT', ['dk', 'e', 'f'])])
print(func(l))

['a','b','c','dk','e','f']

暫無
暫無

聲明:本站的技術帖子網頁,遵循CC BY-SA 4.0協議,如果您需要轉載,請注明本站網址或者原文地址。任何問題請咨詢:yoyou2525@163.com.

 
粵ICP備18138465號  © 2020-2024 STACKOOM.COM