Python有条件地在列表理解的一次迭代中插入多个元素

时间:2018-10-04 18:44:40

标签: python list for-loop if-statement list-comprehension

假设我们必须在一次迭代中以列表推导方式插入两个元素,但是我们必须选择要插入的两个元素。我们如何使这样的列表理解。

例如:- 假设我们有一个列表hostnames = ['aa', 'bb', 'dd', 'pp', 'bb', 'zz', 'hh']

现在我们要修改此列表,以使以p开头的主机名应有一个名为'_prd'的额外元素(例如'pp_prd'),类似地,以h开头的值为'_uat'的主机名也应有一个额外元素。(例如“ hh_uat”)。

因此上述列表的期望输出为mod_hostnames = ['aa', 'bb', 'dd', 'pp_prd', 'pp', 'bb', 'zz', 'hh_uat','hh']

现在我想像这样写出来-

>>> fh=lambda x: x+'_uat'
>>> fp=lambda x:x+'_prd'
>>> fo=lambda x:x
>>> lis
['aa', 'bb', 'dd', 'pp', 'bb', 'zz', 'hh']
>>> hostnames = lis
>>> mod_hostnames = [f(host) for f in (fo,fp) if host[0]=='p' else f(host) for f in (fo,fh) if host[0]=='h' else host for host in hostnames]
  File "<stdin>", line 1
    mod_hostnames = [f(host) for f in (fo,fp) if host[0]=='p' else f(host) for f in (fo,fh) if host[0]=='h' else host for host in hostnames]
                                                                 ^
SyntaxError: invalid syntax

我们收到语法错误。我还知道,在列表理解中,第二个循环运行得更快(就像在for循环中一样),在我们的代码中,host in hostnames运行得更快,而不是其他所需的方式。所以我尝试了:-

>>> mod_hostnames = [f(host) for host in hostnames for f in (fo,fp) if host[0]=='p' else for f in (fo,fh) if host[0]=='h' else for f in (fo)]  
File "<stdin>", line 1
    mod_hostnames = [f(host) for host in hostnames for f in (fo,fp) if host[0]=='p' else for f in (fo,fh) if host[0]=='h' else for f in (fo)]
                                                                                       ^
SyntaxError: invalid syntax

有什么建议吗?或者这在列表理解中是不可能的。
我知道这根本不可读,有很多更好的方法可以写出来。 (例如,使用dict'switch'在for循环内的一个语句中编写此语句,或者在循环中将其写入旧语句中,等等)。

发布解决方案编辑:收到了精彩的回复。谢谢!有人可以解释为什么我发布的代码也是错误的吗?
我之所以这样认为是因为条件为true或false时评估的语句只是解析为空的for循环:for f in (fo,fp) if host[0]=='p' else for f in (fo,fh)如果host [0] =='p'则程序进入空的for循环for f in (fo,fp)。它是否正确?
将我的错误理解反向工程为for循环将清除此问题。

4 个答案:

答案 0 :(得分:1)

首先,您需要编写一个转换函数,该函数始终返回 tuples ,并在必要时使用单例元组:

def transform(name):
    if name.startswith("p"):
        return (name + "_prd", name)
    if name.startswith("h"):
        return (name + "_uat", name)
    return (name,) # Singleton tuple.

然后您可以执行以下操作:

import itertools
mod_hostnames = list(itertools.chain.from_iterable(
    transform(name) for name in hostnames))
print(mod_hostnames)

itertools.chain.from_iterable本质上是一个扁平化操作,它将所有中间元组连接在一起,而外部list将该生成器的输出转换为实际列表。

但是,在这种情况下,采用传统的for循环的行人方式会更有意义。

答案 1 :(得分:1)

您的想法还可以,但是您的语法错误。这是修改现有代码的一种方法:

mod_hostnames = [
    f(host) for host in hostnames 
    for f in (
        (fo,fp) if host.startswith('p') else 
        (fo,fh) if host.startswith('h') else 
        (fo,)
    )
]
print(mod_hostnames)
#['aa', 'bb', 'dd', 'pp', 'pp_prd', 'bb', 'zz', 'hh', 'hh_uat']

if / else换行以用括号修改f的可迭代项,并且还需要在(fo,)中使用逗号作为元组。

您也可以使用str.startswith代替索引字符串中的第一个字符。

在任何情况下,传统循环在这里都非常好,对于可读性/易于理解而言可能更可取:

mod_hostnames = []
for host in hostnames:
    mod_hostnames.append(fo(host))
    if host.startswith('p'):
        mod_hostnames.append(fp(host))
    elif host.startswith('h'):
        mod_hostnames.append(fh(host))

答案 2 :(得分:0)

将{em> list理解与ifelse ifelse一起使用,并使用set

mod = [i +'_prd' if i.startswith('p') else i + '_uat' if i.startswith('h') else i for i in hostnames]
mod = sorted(list(set(mod) | set(hostnames)))
# ['aa', 'bb', 'dd', 'hh', 'hh_uat', 'pp', 'pp_prd', 'zz']

答案 3 :(得分:0)

实际上,最好从传统的for循环开始,这样一来,您就可以了解困难之处:

mod_hostnames = []
for name in hostnames:
    if name.startswith('p'):
        r = [name + '_prd', name]
    elif name.startswith('h'):
        r = [name + '_uat', name]
    else:
        r = [name]
    mod_hostnames.extend(r)

assert mod_hostnames == ['aa', 'bb', 'dd', 'pp_prd', 'pp', 'bb', 'zz', 'hh_uat', 'hh']

要创建理解列表,您需要使用... {{1}在单行语句中转换if ... elif ... else语句} ... if ...

您可以按照以下步骤进行处理:

else

嗯,这有点棘手。

要将此循环转换为理解列表,您需要迭代mod_hostnames = [] for name in hostnames: r = [name + '_prd', name] if name.startswith('p') else \ ([name + '_uat', name] if name.startswith('h') else [name]) mod_hostnames.extend(r) 值。您会在理解列表中得到一个理解列表。

r

哼!我认为保持理解列表太复杂了。谁想要调试它?

出于可维护性考虑,我建议使用一个功能较小的解决方案:

mod_hostnames = [item
                 for name in hostnames
                 for item in ([name + '_prd', name] if name.startswith('p') else
                              ([name + '_uat', name] if name.startswith('h') else [name]))]

在这里!