假设我们必须在一次迭代中以列表推导方式插入两个元素,但是我们必须选择要插入的两个元素。我们如何使这样的列表理解。
例如:-
假设我们有一个列表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循环将清除此问题。
答案 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理解与if
,else if
,else
一起使用,并使用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]))]
在这里!