多字符串格式

mhl*_*ter 22 python string customization string-formatting

给定ints 的字典,我正在尝试使用每个数字格式化字符串,以及项目的复数形式.

样本输入dict:

data = {'tree': 1, 'bush': 2, 'flower': 3, 'cactus': 0}
Run Code Online (Sandbox Code Playgroud)

样本输出str:

'My garden has 1 tree, 2 bushes, 3 flowers, and 0 cacti'
Run Code Online (Sandbox Code Playgroud)

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

我提出的最佳解决方案是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'
Run Code Online (Sandbox Code Playgroud)

然后制作一个新的dict直通理解和classes映射:

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

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

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))
Run Code Online (Sandbox Code Playgroud)

输出以下内容:

My garden has 1 tree, 2 bushes, 3 flowers, and 0 cacti
Run Code Online (Sandbox Code Playgroud)

对于这种毫无疑问的共同需求,我犹豫是否想要这样一个错综复杂的解决方案.

有没有办法使用内置format方法格式化这样的字符串,并最少的额外代码?伪代码可能是这样的:

"{tree} tree{tree(s)}, {bush} bush{bush(es)}, {flower} flower{flower(s)}, {cactus} cact{cactus(i,us)}".format(data)
Run Code Online (Sandbox Code Playgroud)

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

mea*_*ppl 32

看看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))
Run Code Online (Sandbox Code Playgroud)

对于您的具体示例:

{print(str(count) + " " + p.pluralize(string, count)) for string, count in data.items() }
Run Code Online (Sandbox Code Playgroud)

  • @meawoppl:只是不要做Ruby on Rails所做的事情:一些聪明的aleck认为将"牛"复数转化为"kine"(这是正确但迂腐)会很酷,但却产生了"scow"的副作用"多元化为"skine"(显然是错误的). (3认同)
  • 问题已经打开,拉动请求正在进行中.不久之后会有仙人掌. (2认同)
  • 哈,结果仙人掌和仙人掌是有效的:复数:http://en.wikipedia.org/wiki/Cactus,http://grammarist.com/usage/cacti-cactuses/ (2认同)
  • 哈哈哈哈。F-是的语言学。再次强调,这是一个比大多数人想象的更复杂的问题。 (2认同)

fal*_*tru 20

使用自定义格式器:

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))
Run Code Online (Sandbox Code Playgroud)

输出:

1 tree, 2 bushes, 3 flowers, 0 cacti
Run Code Online (Sandbox Code Playgroud)

UPDATE

如果您使用的是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))
Run Code Online (Sandbox Code Playgroud)

输出:与上述相同.

  • @mhlester,实际上,我不仅阅读了文档,还阅读了[源代码`string.py`](http://hg.python.org/cpython/file/2e8a142dbccc/Lib/string.py#l162) . (3认同)

Ari*_*ide 16

当您只有两种形式,并且只需要快速而肮脏的修复时,请尝试's'[:i^1]

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

输出:

0 bottles of beer.
1 bottle of beer.
2 bottles of beer.
3 bottles of beer.
4 bottles of beer.
Run Code Online (Sandbox Code Playgroud)

解释:

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

  • i为零时,i ^ 1计算为1's'[:1]'s'.
  • i为 1 时,i ^ 1求值为0's'[:0]给出空字符串。
  • i大于 1 时,i ^ 1计算结果为大于的整数1(以 3、2、5、4、7、6、9、8...开头,有关详细信息,请参见https://oeis.org/A004442)。Python 不介意并愉快地返回尽可能多的字符's',即's'.

我的 1 美分 ;)

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

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

  • 如果您喜欢可读性,这里是使用 `!=1` 的原始解决方案: `bottle{'s'[:i!=1]}` (2认同)

mar*_*che 10

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

You have {{ num_messages }} message{{ num_messages|pluralize }}.
Run Code Online (Sandbox Code Playgroud)

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

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))
Run Code Online (Sandbox Code Playgroud)