Python 复数字符串格式

声明:本页面是StackOverFlow热门问题的中英对照翻译,遵循CC BY-SA 4.0协议,如果您需要使用它,必须同样遵循CC BY-SA许可,注明原文地址和作者信息,同时你必须将它归于原作者(不是我):StackOverFlow 原文地址: http://stackoverflow.com/questions/21872366/
Warning: these are provided under cc-by-sa 4.0 license. You are free to use/share it, But you must attribute it to the original authors (not me): StackOverFlow

提示:将鼠标放在中文语句上可以显示对应的英文。显示中英文
时间:2020-08-18 23:46:53  来源:igfitidea点击:

Plural String Formatting

pythonstringcustomizationstring-formatting

提问by mhlester

Given a dictionary of ints, I'm trying to format a string with each number, and a pluralization of the item.

给定一个ints字典,我试图用每个数字和项目的复数来格式化一个字符串。

Sample input dict:

样本输入dict

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

Sample output str:

示例输出str

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

It needs to work with an arbitrary format string.

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

The best solution I've come up with is a PluralItemclass to store two attributes, n(the original value), and s(the string 's'if plural, empty string ''if not). Subclassed for different pluralization methods

我想出的最佳解决方案是一个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'

Then make a new dictthrough comprehension and a classesmapping:

然后dict通过理解和classes映射创建一个新的:

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

Lastly, the format string, and implementation:

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

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))

Outputs the following:

输出以下内容:

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


For such an undoubtedly common need, I'm hesitant to throw in the towel with such a convoluted solution.

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

Is there a way to format a string like this using the built-in formatmethod, and minimal additional code? Pseudocode might be something like:

有没有办法使用内置format方法和最少的附加代码来格式化这样的字符串?伪代码可能类似于:

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

where parentheses return the contents if value is plural, or if contents has comma, means plural/singular

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

采纳答案by falsetru

Using custom formatter:

使用自定义格式化程序

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))

Output:

输出:

1 tree, 2 bushes, 3 flowers, 0 cacti

UPDATE

更新

If you're using Python 3.2+ (str.format_mapwas added), you can use the idea of OP (see comment) that use customized dict.

如果您使用的是 Python 3.2+(str.format_map已添加),您可以使用使用自定义 dict 的 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))

Output: same as above.

输出:同上。

回答by meawoppl

Check out the inflect package. It will pluralize things, as well as do a whole host of other linguistic trickery. There are too many situations to special-case these yourself!

查看inflect 包。它将使事物多元化,并进行大量其他语言技巧。有太多的情况需要自己特殊处理!

From the docs at the link above:

从上面链接中的文档:

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))

For your specific example:

对于您的具体示例:

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

回答by Veedrac

I would go with something like

我会选择类似的东西

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'

The format is always/singular/plural, which singular(then plural) optional.

格式为always/singular/plural,其中singular(然后plural)可选。

So

所以

"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"

Then for your example one just does:

那么对于你的例子,一个就是:

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'

回答by markushinsche

Django users have pluralize, a function used in templates:

Django 用户有pluralize一个在模板中使用的函数:

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

But you can import this into your code and call it directly:

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

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))

回答by Eli Chan

I was inspired by the answers above, particularly @Veedrac's, to create a Plurality utility:

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

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

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

Features:

特征:

  • Customizable number-indexed templates (e.g. see 'vague' below)
  • Numbers and support for $n template tokens
  • Singular/plural forms (e.g. 'cact/us/i') and support for $thing/$things template tokens
  • Indefinite article capability (inspired by https://stackoverflow.com/a/20337527/4182210) and support for $a template token
  • Left/right string concatenation
  • Partials with any subset of number, forms, and templates
  • Partial completion via call() or format string
  • 可定制的数字索引模板(例如,参见下面的“模糊”)
  • 数字和对 $n 模板令牌的支持
  • 单数/复数形式(例如“cact/us/i”)和对 $thing/$things 模板标记的支持
  • 不定冠词功能(受https://stackoverflow.com/a/20337527/4182210启发)并支持 $a 模板令牌
  • 左/右字符串连接
  • 具有数字、形式和模板的任何子集的部分
  • 通过 call() 或格式字符串部分完成

From the docstring:

从文档字符串:

"""
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.'
"""

回答by Rusca8

If there's a limited number of words you're gonna pluralize, I found it easier to have them as lists [singular, plural], and then make a small function that returns the index given the amount:

如果您要复数化的单词数量有限,我发现将它们作为列表更容易[singular, plural],然后创建一个小函数,返回给定数量的索引:

def sp(num):
    if num == 1:
        return 0
    else:
        return 1

Then it works like this:

然后它的工作方式如下:

str = f"Hi I have bought 2 {lemon[sp(2)]}"

And actually you can get a lot of them at once if you split the word:

事实上,如果你把这个词分开,你可以一次得到很多:

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