l = ["a", "b", "c", "d", "e"]
我想将这个列表转换为字典,格式如下:d = {"a": "b", "c": "d", "e": ""}
基本上,偶数将成为键,而奇数将成为值。我知道可以使用带有if语句的for循环等“非pythonic”的方法来完成它,但我相信应该有一种更“pythonic”的方法来实现这一目标。所以,我感谢任何帮助 :)
l = ["a", "b", "c", "d", "e"]
我想将这个列表转换为字典,格式如下:d = {"a": "b", "c": "d", "e": ""}
基本上,偶数将成为键,而奇数将成为值。我知道可以使用带有if语句的for循环等“非pythonic”的方法来完成它,但我相信应该有一种更“pythonic”的方法来实现这一目标。所以,我感谢任何帮助 :)
如果您还在思考什么,那么您并不孤单,实际上它并不是那么复杂,请让我解释一下。
我们希望使用奇数项(从1开始计数)作为键,将其连续的偶数项映射到以下列表中,以将其转换为字典。
l = ["a", "b", "c", "d", "e"]
使用内置的dict
函数可以创建字典,参考手册中支持以下方法:Mapping Types。
dict(one=1, two=2)
dict({'one': 1, 'two': 2})
dict(zip(('one', 'two'), (1, 2)))
dict([['two', 2], ['one', 1]])
最后一个选项建议我们提供一个由2个值或(key, value)
元组组成的列表,因此我们要将我们的顺序列表转换为:
l = [["a", "b"], ["c", "d"], ["e",]]
我们还介绍了zip
函数,它是内置函数之一,手册中这样解释:
返回的是一个元组(tuple)列表,其中第 i 个元组包含来自每个参数的第 i 个元素
换句话说,如果我们可以将列表转换为两个列表 a, c, e
和 b, d
,那么 zip
就会完成剩下的工作。
切片符号 在我们使用字符串以及后面的列表部分中看到,主要使用range或短切片符号。但这就是长切片符号的样子,以及我们可以用步长实现的内容:
>>> l[::2]
['a', 'c', 'e']
>>> l[1::2]
['b', 'd']
>>> zip(['a', 'c', 'e'], ['b', 'd'])
[('a', 'b'), ('c', 'd')]
>>> dict(zip(l[::2], l[1::2]))
{'a': 'b', 'c': 'd'}
尽管这是理解其中涉及的机制最简单的方法,但有一个缺点,因为每次切片都是新的列表对象,就像在这个克隆示例中所看到的那样:
>>> a = [1, 2, 3]
>>> b = a
>>> b
[1, 2, 3]
>>> b is a
True
>>> b = a[:]
>>> b
[1, 2, 3]
>>> b is a
False
虽然b看起来像a,但现在它们是两个独立的对象,这就是为什么我们更喜欢使用grouper recipe的原因。
尽管分组器被解释为itertools模块的一部分,但它也可以很好地与基本函数配合使用。
有些真正的巫术对吧?=)但实际上,分组器只是一点语法糖,通过以下表达式实现了分组方法。
*[iter(l)]*2
这更或少将相同迭代器的两个参数包装在列表中,如果有意义的话。让我们分解一下以帮助澄清。
>>> l*2
['a', 'b', 'c', 'd', 'e', 'a', 'b', 'c', 'd', 'e']
>>> [l]*2
[['a', 'b', 'c', 'd', 'e'], ['a', 'b', 'c', 'd', 'e']]
>>> [iter(l)]*2
[<listiterator object at 0x100486450>, <listiterator object at 0x100486450>]
>>> zip([iter(l)]*2)
[(<listiterator object at 0x1004865d0>,),(<listiterator object at 0x1004865d0>,)]
>>> zip(*[iter(l)]*2)
[('a', 'b'), ('c', 'd')]
>>> dict(zip(*[iter(l)]*2))
{'a': 'b', 'c': 'd'}
正如您所看到的,两个迭代器的地址是相同的,因此我们正在使用相同的迭代器。zip先从中获取一个键,然后每次获取一个值和一个键,并通过步进相同的迭代器来完成我们使用切片所做的事情,从而实现更高效的操作。
您可以通过以下方式完成非常相似的操作,这可能具有更小的“什么?”因素。
>>> it = iter(l)
>>> dict(zip(it, it))
{'a': 'b', 'c': 'd'}
如果您注意到所有的示例中都缺少空键e
,那么它该怎么办呢?因为zip
会选择两个参数中较短的一个,所以我们该怎么做呢。
嗯,一种解决方案可能是向奇数长度的列表添加一个空值。您可以选择使用append
和if
语句来完成这个技巧,尽管有点无聊,对吧?
>>> if len(l) % 2:
... l.append("")
>>> l
['a', 'b', 'c', 'd', 'e', '']
>>> dict(zip(*[iter(l)]*2))
{'a': 'b', 'c': 'd', 'e': ''}
现在,在你摆脱去输入from itertools import izip_longest
之前,你可能会惊讶地发现它是不必要的。我们可以只使用内置函数来实现同样的功能,而且我认为甚至更好。
我更喜欢使用map()函数而不是izip_longest(),它不仅使用更短的语法,不需要导入,还可以在需要时自动赋予一个真正的None
空值。
>>> l = ["a", "b", "c", "d", "e"]
>>> l
['a', 'b', 'c', 'd', 'e']
>>> dict(map(None, *[iter(l)]*2))
{'a': 'b', 'c': 'd', 'e': None}
根据KursedMetal指出的,比较这两种方法的性能,很明显itertools模块在处理大量数据时远优于map函数,以1000万条记录为基准。
$ time python -c 'dict(map(None, *[iter(range(10000000))]*2))'
real 0m3.755s
user 0m2.815s
sys 0m0.869s
$ time python -c 'from itertools import izip_longest; dict(izip_longest(*[iter(range(10000000))]*2, fillvalue=None))'
real 0m2.102s
user 0m1.451s
sys 0m0.539s
然而,导入模块的成本对于较小的数据集来说是一个负担,在大约10万条记录开始头对头到达时,map()函数能更快地返回结果。
$ time python -c 'dict(map(None, *[iter(range(100))]*2))'
real 0m0.046s
user 0m0.029s
sys 0m0.015s
$ time python -c 'from itertools import izip_longest; dict(izip_longest(*[iter(range(100))]*2, fillvalue=None))'
real 0m0.067s
user 0m0.042s
sys 0m0.021s
$ time python -c 'dict(map(None, *[iter(range(100000))]*2))'
real 0m0.074s
user 0m0.050s
sys 0m0.022s
$ time python -c 'from itertools import izip_longest; dict(izip_longest(*[iter(range(100000))]*2, fillvalue=None))'
real 0m0.075s
user 0m0.047s
sys 0m0.024s
看起来很简单!=)
享受吧!
map(None, ...)
,因此您的解决方案仅适用于 Python 2.X。 - laike9m使用通常的grouper配方,你可以这样做:
Python 2:
d = dict(itertools.izip_longest(*[iter(l)] * 2, fillvalue=""))
Python 3:
d = dict(itertools.zip_longest(*[iter(l)] * 2, fillvalue=""))
d = dict(itertools.izip_longest(l[::2], l[1::2], fillvalue=''))
,或者在Python 3中使用 d = dict(itertools.zip_longest(l[::2], l[1::2], fillvalue=''))
。你的版本完全可行,但对于那些没有意识到[iter(l)] * 2
创建了一个包含两个引用的列表,指向同一个迭代器的人可能会稍微有些困惑。([iter(l)]*2
-> [<list_iterator object at 0x01383FD0>, <list_iterator object at 0x01383FD0>]
) - JABl[::2]
和l[1::2]
创建两个列表副本来证明你的方法并没有足够的好处。 - Sven Marnachdel x[:1]
、x[:] = [2, 3]
等操作,以及 numpy 将数组切片实现为视图。但实际上似乎并非如此,所以更有效的方法应该是 d = dict(izip_longest(islice(l, 0, None, 2), islice(l, 1, None, 2), fillvalue=''))
。我想知道是否已经提交了这方面的功能请求,如果有的话,响应是什么... - JAB我会选择使用递归:
l = ['a', 'b', 'c', 'd', 'e', ' ']
d = dict([(k, v) for k,v in zip (l[::2], l[1::2])])
dict(zip(l[::2], l[1::2]))
已经足够了。在我的Python 3.8.5中运行良好。 - Shabeer我不确定这是否能帮到你,但对我有效:
l = ["a", "b", "c", "d", "e"]
outRes = dict((l[i], l[i+1]) if i+1 < len(l) else (l[i], '') for i in xrange(len(l)))
dict(zip(list, list))
。 - DanielBell99