我想编写一个从深层嵌套元组和列表中提取元素的函数,比方说我有这样的东西
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'
后再次设置。
答案 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']