【问题标题】:Can I use a generator expression to print key,value pairs of a list of dictionaries?我可以使用生成器表达式打印字典列表的键值对吗?
【发布时间】:2016-05-26 21:41:53
【问题描述】:

我有一个字典列表:

dictlist = [{'key1':'value1', 'key2':'value2'}, {'key3':'value3', 'key4':'value4'}]

我目前打印这样的键值对:

for d in dictlist:
  for key in d:
    print .format('{} ---> {}').format(key,d[key])

如何使用生成器表达式来减少代码行数?

编辑:我正在尝试让自己熟悉生成器表达式,而不仅仅是减少代码行数!

【问题讨论】:

  • 你为什么要这样做?它不工作吗?
  • 就是想看看能不能改进一下。
  • 不过,您只是在打印值。
  • 1.不要影响dict。 2.否则看起来还不错;单行可能不太可读。
  • 已编辑以更正@Nils_M 的观点。

标签: python dictionary list-comprehension generator-expression


【解决方案1】:

看看python docs on expressions。这些都是仅代表一种或另一种不同复杂性的值的东西。它们不是引起副作用的事物(如 I/O)或对可变数据的转换。生成器表达式,顾名思义,只是另一种表达式。

减少代码行并不是生成器表达式的预期目的。除了“内存效率和延迟计算值”的主要用途(感谢下面的评论者)之外,它们的很大一部分目的是通过使迭代器成为一种更易于访问的原子值来使您的代码更加简洁和可读。如果您使代码的可读性降低,则可能是您错误地使用了生成器表达式。

在我看来,特别是生成器表达式与 python 编程的函数范式紧密相关。它们产生的值与您从数学定义的系列中所期望的一样,例如the generator yielding all the Fibonacci numbers。考虑到这种功能范式,专门使用生成器来产生副作用可能是一种代码味道

积极地说,使用生成器表达式的正确方法可能是让它们一次对可迭代数据集的一个元素进行一次明确定义的转换,并生成转换后的数据然后。这符合函数式范式:函数接受一个值,返回一个值,并且没有副作用。

您可以使用生成器 function 以一种有趣的方式重构此代码,这将允许您通过迭代生成的元素来打印连续的元素。像这样:

def my_printer(dictlist):
    for d int dictlist: # renamed to not shadow dict
        for key in dict:
            print .format('{} ---> {}').format(key,dict[key])
            yield

然后你可以打印如下元素:

>>> p = my_printer(dictlist)
>>> p.next()
'some stuff 1'
>>> p.next()
'some stuff 2'

等等

这并不是你真正要求的。

为了更直接地回答您的问题,如果您将 print 语句排除在外,您可能会得到看起来像是嵌套 for 循环的扁平化:

>>> (('{} ---> {}').format(k,v) for d in dictlist for k,v in d.items())

或者实际上,由于(k,v) 只是一个您以相同顺序传递的元组,您可以使用 splat 运算符 * 将其作为参数直接解压缩到您的 format 调用中:

>>> (('{} ---> {}').format(*p) for d in dictlist for p in d.items())

最后,您可能希望通过使用iteritems 函数来删除d.items() 的中间列表创建:

>>> (('{} ---> {}').format(*p) for d in dictlist for p in d.iteritems())

然后使用常规 for 循环打印结果:

>>> for x in _:
    print x


key2 ---> value2
key1 ---> value1
key3 ---> value3
key4 ---> value4

【讨论】:

  • 我还要补充一点,生成器的主要目的之一是内存效率惰性计算值
  • @machineyearning 将它们重构为一个函数是有道理的。然而,把它们写成一个单一的表达方式是我很纠结的事情。就像你说的,我应该多练习。使用生成器功能会使操作更快吗?我想在遍历列表时,然后字典中的键一次只会评估一个元素。
  • 不,生成器函数不会更快。这只是一种不同的编写方式,允许您一次打印来自dictlist 的元素。不过,您绝对可以提前打包格式化的字符串,并且编写生成器表达式实际上比将它们放入 list 更节省内存。我会在最后添加。不过,请不要使用 print 语句。
  • @TheStupidEngineer 我添加了一些使用生成器表达式打包数据的不同方式的示例。
【解决方案2】:

让你的代码水平而不是垂直并没有真正的好处,但你可以这样做:

from __future__ import print_function
from itertools import imap
print(*(' ---> '.join(imap(str, item)) for dic in dictlist for item in dic.iteritems()), sep='\n')

结果:

key2 ---> value2
key1 ---> value1
key3 ---> value3
key4 ---> value4

请注意,map() 用于将非字符串元素转换为字符串,以便它们可以与join() 连接。如果您确定键和值将始终为字符串,则可以简单地使用 ' ---> '.join(item)

【讨论】:

  • 我很想听听我的回答有什么没有帮助或错误的地方,以使其值得一票否决。这样我可以改进我的答案!
  • 我没有投反对票,但 .items().map() 打败了发电机的好处。请改用.iteritems()imap(..)
【解决方案3】:

嗯,你所拥有的可能是最易读的版本。我可能会去。

但是如果你想要一个更紧凑的版本,怎么样:

>>> dictlist = [{'key1':'value1', 'key2':'value2'}, {'key3':'value3', 'key4':'value4'}]
>>> list((k,v) for d in dictlist for k,v in d.iteritems())
[('key2', 'value2'), ('key1', 'value1'), ('key3', 'value3'), ('key4', 'value4')]

注意:最外层的list 只是一个占位符。你应该调用 put 一个函数调用来代替它。

【讨论】:

  • 如果您要在list 中包装生成器表达式,为什么不首先使用列表推导式?
  • 最外面的列表是一个占位符。 OP 应该能够将其用作生成器。让我进行编辑。
  • @Thrustmaster 不错!你介意解释一下我是如何理解你的想法的吗?
  • @Thrustmaster 现在不是这个表达式把我的字典列表变成了元组列表吗?
【解决方案4】:

如果你真的想要,你可以迭代一个生成器表达式来打印东西本身,然后丢弃你迭代的内容:

for _ in (print('{} ---> {}'.format(key,dict[key])) for dict in dictlist for key in dict):
    pass

我真的没有理由这样做,但是,它的可读性不是很好。它假定 Python 3 或 from __future__ import print_function,因此您可以在表达式中使用 print

【讨论】:

    【解决方案5】:

    您可以使用生成器表达式和itertools 将其单行。

    from itertools import chain, imap
    
    print '\n'.join('{} ---> {}'.format(k,v) for k, v in chain.from_iterable(d.iteritems() for d in dictlist))
    

    d.itervalues() 返回每​​个字典中所有值的生成器。 itertools.chain 获取所有这些生成器,并将它们展平为单个生成器。 itertools.imap 函数将您的所有值转换为字符串,以便可以打印它们。然后将这些值提供给.join 方法,该方法在每个值之间放置一个换行符并打印它。

    【讨论】:

    • @TigerhawkT3 糟糕,忘记使用chain.from_iterable 而不是chain
    • 现在它不会生成回溯,但它只打印值,没有键。
    • @TigerhawkT3 是的,这个问题最初只询问值。一旦 OP 用新语法更新了问题,我就更新了它。考虑到您最初以相同的方式回答了问题,您应该知道这一点。
    猜你喜欢
    • 2021-08-14
    • 2021-04-26
    • 1970-01-01
    • 2022-01-07
    • 2016-01-25
    • 2020-08-27
    • 1970-01-01
    • 2022-12-05
    相关资源
    最近更新 更多