Python 复数字符串格式

Python 复数字符串格式,python,string,customization,string-formatting,Python,String,Customization,String Formatting,给定一个ints的字典,我试图用每个数字格式化一个字符串,并对该项进行复数化 样本输入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'如果是复数,则为空字符串')。子

给定一个
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 = {'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)
其中,如果值为复数,或内容有逗号,则括号返回内容,表示复数/单数,使用:

更新

如果您使用的是Python3.2+(已添加),那么可以使用使用定制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))
class PluralDict(dict):
def________;缺失(自身,钥匙):
如果键中的“(”和键的.endswith(“)”):
key,rest=key.split(“(”,1)
value=super()
后缀=rest.rstrip(')).split('),'))
如果len(后缀)==1:
后缀。插入(0“”)
如果值则返回后缀[0],请检查。它将使事情多元化,同时也会进行一系列其他的语言欺骗。有太多的情况下,这些特殊情况下,你自己

从上面链接的文档中:

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

我会选择这样的

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'
格式为
始终/单数/复数
,可选择
单数
(然后是
复数

所以

那么,作为您的示例,您只需执行以下操作:

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'
Django用户有一个模板中使用的函数:

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

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

特点:

  • 可自定义编号索引模板(例如,请参见下面的“vague”)
  • 数量和对$n模板令牌的支持
  • 单数/复数形式(如“cact/us/i”)和对$thing/$things模板标记的支持
  • 不定冠词功能(受启发)和对$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.'
"""

如果要复数的单词数量有限,我发现将它们列为列表比较容易,然后生成一个小函数,返回给定数量的索引:

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)]}"

当您只有两个表单,并且只需要一个快速而肮脏的修复程序时,请尝试
's'[:i^1]

范围(5)内的i的
:
打印(f“{i}瓶{s'[:i^1]}啤酒。”)
输出:

0 bottles of beer.
1 bottle of beer.
2 bottles of beer.
3 bottles of beer.
4 bottles of beer.
说明:

^
是按位运算符XOR()

  • i
    为零时,
    i^1
    的计算结果为
    1
    <代码>'s'[:1]
给出了
's'
  • i
    为1时,
    i^1
    的计算结果为
    0
    <代码>'s'[:0]提供空字符串
  • i
    大于一时,
    i^1
    计算为大于
    1
    的整数(从3,2,5,4,7,6,9,8…开始,请参阅以了解更多信息)。Python并不介意,并且很高兴返回尽可能多的
    's'
    字符,即
    's'
  • 我的1美分;)

    编辑。使用的是以前的、长一个字符的版本
    =而不是
    ^


    奖金。对于两个字符的复数形式(如bush/bush),使用
    “es”[:2*i^2]
    。更一般地说,对于n个字符的复数形式,将前面表达式中的
    2
    替换为n。

    对此您有何看法?这基本上就是我的类所做的,但我不知道如何在字符串格式中放置类似的内容。特别是对于多个键。上面的{goose:5}如何处理?是的,对于我的代码,您必须创建另一个子类来替换整个单词。因此,寻找一个更好的方法来处理严重的问题,我敢打赌,你必须处理大约100个特殊情况。请看下面的答案。@mhlester,实际上,我不仅阅读了文档,而且也阅读了。@mhlester,顺便说一句,这不处理带有复数后缀的数字字段:例如
    0(I,ie)
    ,如果不阅读源代码或文档,我打赌用类似代码扩展
    args[key]
    行就足够简单了。别费心稀释了this@mhlester,你的想法是可能的。但仅在Python 3.2+中。检查更新。哦,这很聪明。我在2.7中,但这确实是一个很好的特性,这是一个非常有趣的方法。虽然问题已经打开,但请求仍在进行中,但很难强制转换为通用格式字符串。不久将有仙人掌。哈,原来仙人掌和仙人掌是有效的:复数:,@meawoppl:只是不要做Ruby on Rails做的事情:一些聪明的aleck认为将“cow”的复数形式改为“kine”(这是正确的,但很迂腐),但产生了副作用,将“scow”复数形式改为“skine”(显然是错误的)。哈哈哈。是的,语言学。让我再次强调,这是一个比大多数人所理解的更复杂的问题。谢谢,这是一个非常接近的解决方案,也是其中之一
    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)]}"
    
    0 bottles of beer.
    1 bottle of beer.
    2 bottles of beer.
    3 bottles of beer.
    4 bottles of beer.