【问题标题】:How to turn code into one-line return statement如何将代码变成一行return语句
【发布时间】:2021-04-17 07:56:28
【问题描述】:

我正在使用推导来最小化代码。我想出了如何对列表 l 进行理解,但我似乎无法弄清楚如何对 dict d 进行理解,然后将两个理解转换为单行返回语句。任何帮助将不胜感激!

def f(dct:{str:[(str,int,int)]}) -> [str]:
    d = dict()
    for a,b in dct.items():
        count = 0
        for x,y,z in b:
            if y <= -1:
                y = y * -1
            count += y
        d.update({a:count})
        
    l = list()
    for a,count in sorted(d.items(), key=(lambda t:(-t[1],t[0]))):
        l.append(a)
    return l

【问题讨论】:

  • 您似乎有一个return 声明。这是功能的一部分吗?如果是这样,最好将代码显示为函数以及调用它的示例。
  • for 循环比理解更强大:有很多事情只能通过循环来完成。
  • 要明确一点:您想将所有 create-loop-insert 块,即整个函数,变成由 dict/list 理解组成的一行?
  • " ... 尽量减少代码" - 您应该尽量减少阅读和理解代码所需的努力。无论如何,理解在这里并不是一个好主意,因为您必须在项目之间的count 中保持一些状态。当您生成仅依赖于每个单独的独立输入项的输出项时,理解会更好地适应。
  • 要记住的一点是,源代码的阅读频率远高于其编写频率。考虑一下您的代码的未来潜在读者,将它们全部放在一行中会使其更复杂(时间成本),从而潜在地改善执行时间或代码行数吗?

标签: python return list-comprehension dictionary-comprehension


【解决方案1】:

这似乎是等效的一个班轮:

def f2(dct):
    return [a for (a,_) in sorted({a:sum(abs(y) for (_,y,_) in b) for (a,b) in dct.items()}.items(), key=(lambda t:(-t[1],t[0])))]

此版本保留了原始dict 中具有相等总和的项目按名称顺序排序的属性。

【讨论】:

    【解决方案2】:

    您可以将所有循环折叠成嵌套推导。由于dict 从未用于键值访问,因此创建元组生成器更简单;通过直接将计数创建为负数并首先存储它,不需要lambda

    def f(dct: 'Dict[str, List[Tuple[str, int, int]]]') -> 'List[str]':
        return [a for _, a in sorted(((-sum(abs(y) for _, y, _ in b), a) for a, b in dct.items()))]
        #      ^^        ^            ^ ^ invert one item directly to remove the need for a key function
        #      ||        |            + instead of an entire dict[str, int], we create a lazy Generator[Tuple[int, str]]
        #      ||        + invert order since we store name last
        #      |+ the str in the list
        #      + the list we want to return
    

    如果从代码中看不够清楚,通常应该避免这种情况,因为它几乎不可读,至少是不可理解的。


    稍短的变体:

    def f(dct: 'Dict[str, List[Tuple[str, int, int]]]') -> 'List[str]':
        return [*[*zip(*sorted(((-sum(abs(y[1])for y in dct[a]),a)for a in dct))),][1],]
    

    如果结果是任何字符串序列都可以:

    def f(dct: 'Dict[str, List[Tuple[str, int, int]]]') -> 'Tuple[str]':
        return [*zip(*sorted(((-sum(abs(y[1])for y in dct[a]),a)for a in dct))),][1]
    

    【讨论】:

    • 您将删除- 之前的sum
    • 我是否正确阅读了您的代码并看到排序顺序是先按a,然后是sum of y
    • @quamrana 你做到了,只是发现并纠正了错误。
    • 如果需要5行cmets来解释1行,应该多行使用。 :-P
    • @tobias_k 是的,还不如编写常规循环。如果目的是让任何人都能够理解代码,它们会更短。
    【解决方案3】:

    除非我遗漏了什么,否则以下内容应该等同于您的功能:

    def f(dct:{str:[(str,int,int)]}) -> [str]:
        counts = {a: sum(abs(y) for (_, y, _) in b) for a, b in dct.items()}
        return sorted(dct, key=lambda a: (-counts[a], a))
    

    请注意,一般来说,“更少的行”不一定更好、更快或更具可读性。在这种情况下,我会说这两行是可以的,但是将其设为 one 行虽然可能,但并不能使它变得更好。将 dict 的名称更改为 counts 并在列表理解中迭代原始 dict 中的值也有助于恕我直言的可读性。

    此外,您实际上并不需要第一个字典,因为您只需要每个计数一次(并且这些计数在 lambda 中也只计算一次)。这样一来,单行也不会太恐怖。

    def f(dct:{str:[(str,int,int)]}) -> [str]:
        return sorted(dct, key=lambda a: (-sum(abs(y) for _,y,_ in dct[a]), a))
    

    【讨论】:

      猜你喜欢
      • 1970-01-01
      • 1970-01-01
      • 2022-09-24
      • 1970-01-01
      • 2018-06-30
      • 2023-02-13
      • 1970-01-01
      • 2015-07-11
      • 1970-01-01
      相关资源
      最近更新 更多