我想打印出字典键,偶数频率的值对,如
a = 字典("A": 3, "B": 5} => ["A">
, "B", "A", "B", "A", "B", "B", "B"]a = dict('A': 4, 'B': 1} => ["A", "B", "A", "A", "A"]
我知道我可以使用 while 循环来打印每个键并每次删除计数,直到所有键中的所有值均为 0,但如果有更好的方法呢?
def func(d: dict):
res = []
while any(i > 0 for i in d.values()):
for k, c in d.items():
if c > 0:
res.append(k)
d[k] -= 1
return res
(我假设你使用的是保证字典迭代顺序的 Python 版本(
这是一个迭代工具式的方法。它为每个字母创建一个生成器,生成给定的字母次数,并将所有这些字母与zip_longest
组合在一起,以便它们均匀地生成。
from itertools import repeat, zip_longest
def iterate_evenly(d):
generators = [repeat(k, v) for k,v in d.items()]
exhausted = object()
for round in zip_longest(*generators, fillvalue=exhausted):
for x in round:
if x is not exhausted:
yield x
print(list(iterate_evenly({"A": 3, "B": 5})))
print(list(iterate_evenly({"A": 4, "B": 1})))
结果:
['A', 'B', 'A', 'B', 'A', 'B', 'B', 'B']
['A', 'B', 'A', 'A', 'A']
您可以在更少的行中执行相同的操作,尽管它变得更难阅读。
from itertools import repeat, zip_longest
def iterate_evenly(d):
exhausted = object()
return [x for round in zip_longest(*(repeat(k, v) for k,v in d.items()), fillvalue=exhausted) for x in round if x is not exhausted]
print(iterate_evenly({"A": 3, "B": 5}))
print(iterate_evenly({"A": 4, "B": 1}))
对于单行。
首先,创建一个包含两个元素的列表:A
s 列表和 B
s 列表:
>>> d = {'A': 3, 'B': 5}
>>> [[k]*v for k, v in d.items()]
[['A', 'A', 'A'], ['B', 'B', 'B', 'B', 'B']]
[k]*v
的意思是:一个带有v
k
s的列表。第二,交错A
和B
。我们需要zip_longest
,因为zip
会在第一个列表结束后停止:
>>> import itertools
>>> list(itertools.zip_longest(*[[k]*v for k, v in d.items()]))
[('A', 'B'), ('A', 'B'), ('A', 'B'), (None, 'B'), (None, 'B')]
现在,只需展平列表并删除None
值:
>>> [v for vs in itertools.zip_longest(*[[k]*v for k, v in d.items()]) for v in vs if v is not None]
['A', 'B', 'A', 'B', 'A', 'B', 'B', 'B']
其他例子:
>>> d = {'A': 4, 'B': 1}
>>> [v for vs in itertools.zip_longest(*[[k]*v for k, v in d.items()]) for v in vs if v is not None]
['A', 'B', 'A', 'A', 'A']
您可以将sum
与生成器理解一起使用:
res = sum(([key]*value for key, value in d.items()), [])
这利用了这样一个事实,即除了序列乘法("A"*4 == "AAAA"
(之外,sum
还可以"添加"任何可以使用+
运算符的东西,例如列表。
如果您希望顺序随机化,请使用random
模块:
from random import shuffle
shuffle(res)
正如 Thierry Lathuille 所指出的,如果你想按原始顺序循环浏览值,你可以使用一些迭代工具魔法:
from itertools import chain, zip_longest
res = [*filter(
bool, # drop Nones
chain(*zip_longest(
*([key]*val for key, val in d.items()))
)
)]
作为复制和zip_longest
方法的替代方案,让我们尝试简化 OP 的原始代码:
def function(dictionary):
result = []
while dictionary:
result.extend(dictionary)
dictionary = {k: v - 1 for k, v in dictionary.items() if v > 1}
return result
print(function({'A': 3, 'B': 5}))
print(function({'A': 4, 'B': 1}))
输出
% python3 test.py
['A', 'B', 'A', 'B', 'A', 'B', 'B', 'B']
['A', 'B', 'A', 'A', 'A']
%
尽管它可能看起来不同,但它对字典参数没有破坏性,这与 OP 的原始代码不同。
也可以使用通过扩展每个字典条目形成的(位置,字符(元组来完成:
a = {'A': 3, 'B': 5}
result = [c for _,c in sorted( (p,c) for c,n in a.items() for p,c in enumerate(c*n))]
print(result) # ['A', 'B', 'A', 'B', 'A', 'B', 'B', 'B']
如果字典的顺序可用,您可以放弃排序并使用它:
result = [c for i in range(max(a.values())) for c,n in a.items() if i<n]