来自深层嵌套列表/元组的提取元素的递归函数

时间:2018-03-13 03:34:09

标签: python python-3.x recursion nested

我想编写一个从深层嵌套元组和列表中提取元素的函数,比方说我有这样的东西

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'],看似terms = []在循环到'c'后再次设置。

4 个答案:

答案 0 :(得分:6)

您在功能的顶部执行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编辑 - 但这不是你想要依赖的东西。使用==来测试它们是否意味着相同的字符串,无论它们是否真的是相同的对象。

测试类型以获取身份更有用,但通常不是您想要的。事实上,您通常不想测试 equality 的类型。您不经常拥有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)

这不是我解决这个问题的方式(wim's answer可能就是我给出的这个规范,并告诉用递归来解决它),但这有保留现有设计精神(以及大部分实施)的优点。

答案 1 :(得分:4)

分离"扁平化"的关注点会很好。和"过滤"。解耦代码更易于编写且更易于测试。所以,让我们先写一个" 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'}))

答案 2 :(得分:0)

假设可以忽略每个元组的第一个元素,并且我们应该使用第二个元素列表递归,我们可以这样做:

use Illuminate\Support\Facades\Schema;
use Illuminate\Database\Schema\Blueprint;
use Illuminate\Database\Migrations\Migration;

class CreateTrigger extends Migration
{
    /**
     * Run the migrations.
     *
     * @return void
     */
    public function up()
    {
        $database           = config('database.default');
        $databaseUserName   = config('database.connections.'.$database.'.username');

        $definer = 'DEFINER=`'.$databaseUserName.'`@`%`';

        DB::unprepared('
            DROP TRIGGER IF EXISTS user_religion_details_AFTER_UPDATE;

            CREATE '.$definer.' TRIGGER user_religion_details_AFTER_UPDATE AFTER UPDATE ON `'.$databaseName.'.user_religion_details` 

            FOR EACH ROW

            BEGIN
                IF (NEW.religion_id <> OLD.religion_id) THEN
                    UPDATE users SET religion_id = NEW.religion_id  WHERE id = OLD.user_id;
                END IF;
            END
        ');
    }

    /**
     * Reverse the migrations.
     *
     * @return void
     */
    public function down()
    {
        DB::unprepared('DROP TRIGGER `user_religion_details_AFTER_UPDATE`');
    }
}

列表理解有点密集,这与循环相同:

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

答案 3 :(得分:-1)

这个迭代函数应该与.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']