复数字符串格式

问题描述 投票:0回答:7

给定一个

int
s的字典,我试图用每个数字格式化一个字符串,以及项目的复数形式。

样本输入

dict

data = {'tree': 1, 'bush': 2, 'flower': 3, 'cactus': 0}

样本输出

str

'My garden has 1 tree, 2 bushes, 3 flowers, and 0 cacti'

它需要使用任意格式的字符串。

我想出的最好的解决方案是一个

PluralItem
类来存储两个属性,
n
(原始值)和
s
(字符串
's'
如果是复数,空字符串
''
如果不是).针对不同的复数方法进行子分类

class PluralItem(object):
    def __init__(self, num):
        self.n = num
        self._get_s()
    def _get_s(self):
        self.s = '' if self.n == 1 else 's'

class PluralES(PluralItem):
    def _get_s(self):
        self.s = 's' if self.n == 1 else 'es'

class PluralI(PluralItem):
    def _get_s(self):
        self.s = 'us' if self.n == 1 else 'i'

然后通过理解和

dict
映射制作一个新的
classes

classes = {'bush': PluralES, 'cactus': PluralI, None: PluralItem}
plural_data = {key: classes.get(key, classes[None])(value) for key, value in data.items()}

最后,格式字符串和实现:

formatter = 'My garden has {tree.n} tree{tree.s}, {bush.n} bush{bush.s}, {flower.n} flower{flower.s}, and {cactus.n} cact{cactus.s}'
print(formatter.format(**plural_data))

输出如下:

My garden has 1 tree, 2 bushes, 3 flowers, and 0 cacti

对于这样一个无疑是普遍的需求,我犹豫是否要放弃这样一个复杂的解决方案。

有没有办法使用内置的

format
方法和最少的附加代码来格式化这样的字符串?伪代码可能是这样的:

"{tree} tree{tree(s)}, {bush} bush{bush(es)}, {flower} flower{flower(s)}, {cactus} cact{cactus(i,us)}".format(data)

如果值是复数,括号返回内容,或者如果内容有逗号,表示复数/单数

python string customization string-formatting
7个回答
54
投票

检查 inflect 包。它将使事物多元化,并进行大量其他语言欺骗。有太多的情况需要自己特例!

来自上面链接的文档:

import inflect
p = inflect.engine()

# UNCONDITIONALLY FORM THE PLURAL
print("The plural of ", word, " is ", p.plural(word))

# CONDITIONALLY FORM THE PLURAL
print("I saw", cat_count, p.plural("cat", cat_count))

对于您的具体示例:

{print(str(count) + " " + p.pluralize(string, count)) for string, count in data.items() }

53
投票

基本技巧

当您只有两种形式,并且只需要快速而肮脏的修复时,请尝试

's'[:i^1]

for i in range(5):
    print(f"{i} bottle{'s'[:i^1]} of beer.")

输出:

0 bottles of beer.
1 bottle of beer.
2 bottles of beer.
3 bottles of beer.
4 bottles of beer.

说明:

^
是按位运算符异或(异或)。

您还可以使用

!=

 而不是 
^
 构建解决方案。

扩展

n 字符复数形式

对于 2 个字符的复数形式(例如,bush/bushes),使用

'es'[:2*i^2]

。更一般地,对于 n 字符的复数形式,在前面的表达式中用 n 替换
2

对面

在评论中,用户@gccallie 建议

's'[i^1:]

 在第三人称单数动词中添加's':

for i in range(5): print(f"{i} bottle{'s'[:i^1]} of beer lie{'s'[i^1:]} on the wall.")
输出:

0 bottles of beer lie on the wall. 1 bottle of beer lies on the wall. 2 bottles of beer lie on the wall. 3 bottles of beer lie on the wall. 4 bottles of beer lie on the wall.
Python 将第一种形式解释为 

[:stop]

,将第二种形式解释为 
[start:]
.

复制

从 Python 3.8 开始,您可以(滥用)使用海象运算符来避免对同一后缀进行多次计算。这在法语中特别有用,形容词有复数形式:

for i in range(5): print(f"{i} grande{(s:='s'[:i^1])}, belle{s} et solide{s} bouteille{s}.")
输出:

0 grandes, belles et solides bouteilles. 1 grande, belle et solide bouteille. 2 grandes, belles et solides bouteilles. 3 grandes, belles et solides bouteilles. 4 grandes, belles et solides bouteilles.
注意强制性括号,并注意新变量不是 f 字符串的局部变量。

当然,在“正常”样式中,您应该将其写成两行(赋值 + f-string)。


22
投票
使用

自定义格式化程序

import string class PluralFormatter(string.Formatter): def get_value(self, key, args, kwargs): if isinstance(key, int): return args[key] if key in kwargs: return kwargs[key] if '(' in key and key.endswith(')'): key, rest = key.split('(', 1) value = kwargs[key] suffix = rest.rstrip(')').split(',') if len(suffix) == 1: suffix.insert(0, '') return suffix[0] if value <= 1 else suffix[1] else: raise KeyError(key) data = {'tree': 1, 'bush': 2, 'flower': 3, 'cactus': 0} formatter = PluralFormatter() fmt = "{tree} tree{tree(s)}, {bush} bush{bush(es)}, {flower} flower{flower(s)}, {cactus} cact{cactus(i,us)}" print(formatter.format(fmt, **data))

输出:

1 tree, 2 bushes, 3 flowers, 0 cacti

更新

如果您使用的是 Python 3.2+(添加了

str.format_map

),您可以使用使用自定义字典的 OP(见评论)的想法。

class PluralDict(dict): def __missing__(self, key): if '(' in key and key.endswith(')'): key, rest = key.split('(', 1) value = super().__getitem__(key) suffix = rest.rstrip(')').split(',') if len(suffix) == 1: suffix.insert(0, '') return suffix[0] if value <= 1 else suffix[1] raise KeyError(key) data = PluralDict({'tree': 1, 'bush': 2, 'flower': 3, 'cactus': 0}) fmt = "{tree} tree{tree(s)}, {bush} bush{bush(es)}, {flower} flower{flower(s)}, {cactus} cact{cactus(i,us)}" print(fmt.format_map(data))

输出:同上。


15
投票
Django 用户有

pluralize

,模板中使用的函数:

You have {{ num_messages }} message{{ num_messages|pluralize }}.

但是你可以将它导入到你的代码中并直接调用它:

from django.template.defaultfilters import pluralize f'You have {num_messages} message{pluralize(num_messages)}.' 'You have {} message{}.'.format(num_messages, pluralize(num_messages)) 'You have %d message%s' % (num_messages, pluralize(num_messages))
    

4
投票
如果你要复数的单词数量有限,我发现将它们作为列表更容易

[singular, plural]

,然后制作一个小函数返回给定数量的索引:

def sp(num): if num == 1: return 0 else: return 1
然后它像这样工作:

lemon = ["lemon", "lemons"] str = f"Hi I have bought 2 {lemon[sp(2)]}"
实际上,如果你拆分这个词,你可以一次得到很多:

s = ["","s"] str = f"Hi I have 1 cow{s[sp(1)]}"
    

2
投票
我会选择类似的东西

class Pluralizer: def __init__(self, value): self.value = value def __format__(self, formatter): formatter = formatter.replace("N", str(self.value)) start, _, suffixes = formatter.partition("/") singular, _, plural = suffixes.rpartition("/") return "{}{}".format(start, singular if self.value == 1 else plural) "There are {:N thing/s} which are made of {:/a cactus/N cacti}".format(Pluralizer(10), Pluralizer(1)) #>>> 'There are 10 things which are made of a cactus'

格式为

always/singular/plural

,其中
singular
(然后是
plural
)可选。

所以

"xyz/foo/bar".format(Pluralizer(1)) == "xyzfoo" "xyz/foo/bar".format(Pluralizer(2)) == "xyzbar" "xyz/bar".format(Pluralizer(1)) == "xyz" "xyz/bar".format(Pluralizer(2)) == "xyzbar" "xyz".format(Pluralizer(1)) == "xyz" "xyz".format(Pluralizer(2)) == "xyz"

然后对于你的例子,一个只是做:

data = {'tree': 1, 'bush': 2, 'flower': 3, 'cactus': 0} string = 'My garden has {tree:N tree/s}, {bush:N bush/es}, {flower:N flower/s}, and {cactus:N cact/us/i}' string.format_map({k: Pluralizer(v) for k, v in data.items()}) #>>> 'My garden has 1 tree, 2 bushes, 3 flowers, and 0 cacti'
    

1
投票
我受到上面答案的启发,特别是@Veedrac 的,创建了一个 Plurality 实用程序:

https://gist.github.com/elidchan/40baea13bb91193a326e3a8c4cbcaeb9

特点:

    可自定义的数字索引模板(例如,参见下面的“模糊”)
  • $n 模板令牌的数量和支持
  • 单数/复数形式(例如“cact/us/i”)并支持 $thing/$things 模板标记
  • 不定冠词功能(灵感来自
  • https://stackoverflow.com/a/20337527/4182210)并支持 $a 模板令牌
  • 左/右字符串连接
  • 带有数字、表格和模板的任何子集的部分
  • 通过 call() 或格式化字符串部分完成
来自文档字符串:

""" Usage: >>> from utils.verbiage import Plurality >>> f"We have {Plurality(0, 'g/oose/eese')}." 'We have 0 geese.' >>> f"We have {Plurality(1, 'g/oose/eese')}." 'We have 1 goose.' >>> f"We have {Plurality(2, 'g/oose/eese')}." 'We have 2 geese.' >>> oxen = Plurality('ox/en') >>> oxen.template_formatter '1=$n $thing;n=$n $things' >>> f"We have {oxen(0)}." 'We have 0 oxen.' >>> f"We have {oxen(1)}." 'We have 1 ox.' >>> f"We have {oxen(2)}." 'We have 2 oxen.' >>> cows = Plurality('/cow/kine', '0=no $things', '1=$a $thing') >>> cows.template_formatter '0=no $things;1=a $thing;n=$n $things' >>> f"We have {cows(0)}." 'We have no kine.' >>> f"We have {cows(1)}." 'We have a cow.' >>> f"We have {cows(2)}." 'We have 2 kine.' >>> 'We have {:0=no $things;0.5=half $a $thing}.'.format(Plurality(0, 'octop/us/odes')) 'We have no octopodes.' >>> 'We have {:octop/us/odes;0=no $things;0.5=half $a $thing}.'.format(Plurality(0.5)) 'We have half an octopus.' >>> 'We have {:4;octop/us/odes;0=no $things;0.5=half $a $thing}.'.format(Plurality()) 'We have 4 octopodes.' >>> data = {'herb': 1, 'bush': 2, 'flower': 3, 'cactus': 0} >>> s = "We have {herb:herb/s}, {bush:bush/es}, {flower:flower/s}, and {cactus:cact/us/i}." >>> s.format_map({k: Plurality(v) for k, v in data.items()}) 'We have 1 herb, 2 bushes, 3 flowers, and 0 cacti.' >>> vague = Plurality('0=no $things;1=$a $thing;2=a couple $things;n=some $things') >>> s.format_map({k: vague(v) for k, v in data.items()}) 'We have an herb, a couple bushes, some flowers, and no cacti.' """
    
© www.soinside.com 2019 - 2024. All rights reserved.