web-dev-qa-db-ja.com

複数の文字列のフォーマット

intsのディクショナリが与えられた場合、私は各数値とアイテムの複数形で文字列をフォーマットしようとしています。

入力例dict

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

出力例str

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

任意のフォーマット文字列で動作する必要があります。

私が思いついた最善の解決策は、2つの属性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)

値が複数の場合、またはコンテンツにカンマが含まれている場合、括弧はコンテンツを返します。複数/単数を意味します

26
mhlester

カスタムフォーマッタ の使用:

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 が追加されました)、カスタマイズされた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))

出力:上記と同じ。

21
falsetru

インフレクトパッケージ を確認してください。それは物事を複数形にするだけでなく、他の言語の策略の全体を行います。自分でこれらを特殊ケース化するには、状況が多すぎます。

上記のリンクのドキュメントから:

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

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

あなたが複数形にするつもりの単語の数が限られているなら、私はそれらをリストとして持つ方が簡単だとわかりました[singular, plural]、そして量を与えられたインデックスを返す小さな関数を作ります:

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

その後、次のように機能します。

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

そして実際には、Wordを分割すると、一度に多くのファイルを取得できます。

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

私は次のようなもので行きます

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

上記の回答、特に@Veedracの回答に触発されて、複数のユーティリティを作成しました。

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

特徴:

  • カスタマイズ可能な番号インデックス付きテンプレート(例:以下の「あいまい」を参照)
  • $ nテンプレートトークンの数とサポート
  • 単数形/複数形(例: 'cact/us/i')と$ thing/$ thingsテンプレートトークンのサポート
  • 無期限の記事機能( https://stackoverflow.com/a/20337527/418221 に触発された)と$ aテンプレートトークンのサポート
  • 左/右の文字列連結
  • 番号、フォーム、およびテンプレートのサブセットを含む部分
  • Call()またはフォーマット文字列による部分的な完了

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.'
"""
1
Eli Chan