可以将文章内容翻译成中文,广告屏蔽插件可能会导致该功能失效(如失效,请关闭广告屏蔽插件后再试):
问题:
Given a dictionary of int
s, I'm trying to format a string with each number, and a pluralization of the item.
Sample input dict
:
data = {'tree': 1, 'bush': 2, 'flower': 3, 'cactus': 0}
Sample output 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 PluralItem
class to store two attributes, n
(the original value), and s
(the string 's'
if plural, empty string ''
if not). Subclassed for different pluralization methods
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 dict
through comprehension and a classes
mapping:
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 format
method, and minimal additional code? Pseudocode might be something like:
"{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
回答1:
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_map
was added), you can use the idea of OP (see comment) that use customized dict.
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.
回答2:
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!
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() }
回答3:
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.
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'
回答4:
If you happen to use Django already, it is easy: pluralize
is a function that.
It is often used in templates:
You have {{ num_messages }} message{{ num_messages|pluralize }}.
However, you can also use it in your python code:
f'You have {num_messages} message{pluralize(num_messages)}.'
In Python2 this would read:
'You have {} message{}.'.format(num_messages, pluralize(num_messages))
or:
'You have %d message%s' % (num_messages, pluralize(num_messages))
Django pluralize docs:
https://docs.djangoproject.com/en/2.0/ref/templates/builtins/#pluralize