列表中语法正确的人类可读字符串(带牛津逗号)

Grammatically correct human readable string from list (with Oxford comma)

我想要一个语法正确的人类可读的列表字符串表示形式。例如,列表 ['A', 2, None, 'B,B', 'C,C,C'] 应该 return 字符串 A, 2, None, B,B, and C,C,C。这个人为的例子有点必要。请注意 Oxford comma 与此问题相关。

我尝试了 ', '.join(seq) 但这并没有产生上述示例的预期结果。

注意先前存在的类似问题:

此函数的工作原理是处理小列表的方式不同于处理大列表的方式。

from typing import Any, List

def readable_list(seq: List[Any]) -> str:
    """Return a grammatically correct human readable string (with an Oxford comma)."""
    # Ref: 
    seq = [str(s) for s in seq]
    if len(seq) < 3:
        return ' and '.join(seq)
    return ', '.join(seq[:-1]) + ', and ' + seq[-1]

使用示例:

readable_list([])
''

readable_list(['A'])
'A'

readable_list(['A', 2])
'A and 2'

readable_list(['A', None, 'C'])
'A, None, and C'

readable_list(['A', 'B,B', 'C,C,C'])
'A, B,B, and C,C,C'

readable_list(['A', 'B', 'C', 'D'])
'A, B, C, and D'

您还可以使用解包来获得稍微更清洁的解决方案:

def readable_list(_s):
  if len(_s) < 3:
    return ' and '.join(map(str, _s))
  *a, b = _s
  return f"{', '.join(map(str, a))}, and {b}"

vals = [[], ['A'], ['A', 2], ['A', None, 'C'], ['A', 'B,B', 'C,C,C'], ['A', 'B', 'C', 'D']]
print([readable_list(i) for i in vals])

输出:

['', 'A', 'A and 2', 'A, None, and C', 'A, B,B, and C,C,C', 'A, B, C, and D']

我真的很固执,我真的很想找出一个 one-liner 解决方案。

"{} and {}".format(seq[0], seq[1]) if len(seq)==2 else ', '.join([str(x) if (y < len(seq)-1 or len(seq)<=1) else "and {}".format(str(x)) for x, y in zip(seq, range(len(seq)))])

我认为这个可以解决问题。而且我认为这个问题也比我想象的要用 non-ugly one-liner.

解决的更复杂

基于 accepted answer for the thread you linked to,这里有一个单行代码,它采用一个可选参数来决定是否使用牛津逗号。

from typing import List

def list_items_in_english(l: List[str], oxford_comma: bool = True) -> str:
    """
    Produce a list of the items formatted as they would be in an English sentence.
    So one item returns just the item, passing two items returns "item1 and item2" and
    three returns "item1, item2, and item3" with an optional Oxford comma.
    """
    return ", ".join(l[:-2] + [((oxford_comma and len(l) != 2) * ',' + " and ").join(l[-2:])])