【问题标题】:Python list comprehension on single-valued returns vs nesting [duplicate]单值返回与嵌套的Python列表理解[重复]
【发布时间】:2019-10-29 16:09:18
【问题描述】:

我有一组字典声明为定义文件类型的类变量(并存储在FILE_TYPES 中)。我需要循环浏览这些字典以将文件类型名称(存储在“名称”键中)与传递给类的文件名配对。以下哪种方法更符合 Python 风格?或者,有没有更好的方法——也许我缺少一些听写理解?

第一种方式:

filetype = [ftype for ftype in cls.FILE_TYPES if ftype['name'] in filename][0]

第二种方式:

for ftype in cls.FILE_TYPES:
    if ftype['name'] in filename:
        filetype = ftype

我本身并没有坚持这一点,但我想知道这些方法中的任何一种是否更好(例如,或多或少的 Pythonic),或者它是否对任何人都没有影响。

如果我在 python 中编码,我通常会尽可能避免嵌套逻辑,就像你在第二种方法中看到的那样(完全不知道为什么,但在某些时候,这个习惯是由天晓得的人养成的)。但是,在第一种方法中使用[0] 访问总是单数的列表元素似乎也令人讨厌。

【问题讨论】:

  • 这两个代码sn-ps做的不一样
  • 这些不做同样的事情。但无论如何,如果您不需要制作列表,请不要使用列表推导。我不太确定您所说的“嵌套逻辑”是什么意思,但据我所知,您似乎在这两种情况下都在这样做。
  • 第二个相当于filetype = [ftype for ftype in cls.FILE_TYPES if ftype['name'] in filename][-1]如果有的话
  • 在这种特殊情况下是的,但您也可以在第二个中添加 break 语句来消除歧义。相关:Get the first item from an iterable that matches a condition
  • 再一次,不要在这里使用列表推导(字典推导更没有意义)。只需使用 for 循环,但在找到它时添加一个 break

标签: python python-3.x list-comprehension dictionary-comprehension


【解决方案1】:

我会使用 next() 中的理解生成器而不是 list 理解,例如:

filetype = next(ftype for ftype in cls.FILE_TYPES if ftype['name'] in filename)

根本区别在于list 理解不会利用这样一个事实,即您可以在满足条件后立即终止循环(因此计算效率会降低),并且会创建一个不必要的临时列表(所以它的内存效率也会降低)。

测试它:

file_types = [dict(name='foo'), dict(name='bar'), dict(name='baz'), dict(name='pam'), dict(name='egg'),]
filename = 'spam'

filetype1 = next(ftype for ftype in file_types if ftype['name'] in filename)
print(filetype)
# {'name': 'pam'}

filetype = [ftype for ftype in file_types if ftype['name'] in filename][0]
print(filetype)
# {'name': 'pam'}

for ftype in file_types:
    if ftype['name'] in filename:
        filetype = ftype
        break
print(filetype)
# {'name': 'pam'}

编辑:根据@pault 的评论,这个答案与here 提出的方法基本相同。

【讨论】:

  • 这与Get the first item from an iterable that matches a condition 上接受的答案相同。最好只是 VTC,IMO。
  • 绝对是。我只是没有看到评论。当然,你能写的关于 Python 的所有东西都已经是 Alex Martelli 10 年前写的了。
  • @pault VTC?投票结束?
  • 这同样有效——如果您需要例外情况会更好,如果您不需要例外情况会更糟。无论如何,我会让其他帖子作为答案,因为其他解决方案仍将存在于 cmets 中。
  • @justadampaul 查看编辑以了解为什么next() 更可取。
【解决方案2】:

这是解决问题的一种可能有用的不同方法。

如果文件类型由文件的扩展名确定,则可以使用os.path.splitext 函数将扩展名与文件名的其余部分分开。然后,文件类型字典可以是扩展名到您需要的任何文件类型对象的映射。

import os

# as a function/method
def get_filetype(filename):
    ext = os.path.splitext(filename)[1]
    return FILETYPES[ext]

filetype = get_filetype(filename)


# or the one-liner
filetype = FILETYPES[os.path.splitext(filename)[1]]

结果是它在可能的扩展数量方面以恒定时间运行。 (只要它们在 FILETYPES 字典中可用。)

如果您不使用扩展名来确定文件类型,只要文件名的某些部分可以提取并映射到文件类型,这种方法也有效。唯一需要的更改是将提取扩展名的代码更改为提取文件名的任何部分确定文件类型的代码。

【讨论】:

    【解决方案3】:

    我更喜欢使用next,如果不存在匹配项会引发异常:

    filename = "foofoo"
    filename2 = "nope"
    FILE_TYPES = [{'name':'foo'},{'name':'baz'},{'name':'bat'},{'name':'bar'}]
    
    try:
        print (next(x for x in FILE_TYPES if x['name'] in filename))
        print (next(x for x in FILE_TYPES if x['name'] in filename2))
    
    except StopIteration:
        print ("no such element")
    
    {'name': 'foo'}
    no such element
    

    【讨论】:

      猜你喜欢
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      • 2020-01-31
      • 1970-01-01
      • 1970-01-01
      相关资源
      最近更新 更多