python - Plural String Formatting

ID : 131357

viewed : 11

Tags : pythonstringcustomizationstring-formattingpython

Top 5 Answer for python - Plural String Formatting

vote vote

95

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

84

When you have only two forms, and just need a quick and dirty fix, try 's'[:i^1]:

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

Output:

0 bottles of beer. 1 bottle of beer. 2 bottles of beer. 3 bottles of beer. 4 bottles of beer. 

Explanation:

^ is the bitwise operator XOR (exclusive disjunction).

  • When i is zero, i ^ 1 evaluates to 1. 's'[:1] gives 's'.
  • When i is one, i ^ 1 evaluates to 0. 's'[:0] gives the empty string.
  • When i is more than one, i ^ 1 evaluates to an integer greater than 1 (starting with 3, 2, 5, 4, 7, 6, 9, 8..., see https://oeis.org/A004442 for more information). Python doesn't mind and happily returns as many characters of 's' as it can, which is 's'.

My 1 cent ;)

Bonus. For 2-character plural forms (e.g., bush/bushes), use 'es'[:2*i^2]. More generally, for an n-character plural form, replace 2 by n in the previous expression.

Opposite. In the comments, user @gccallie suggests 's'[i^1:] to add an 's' to verbs in the third person singular:

for i in range(5):     print(f"{i} bottle{'s'[:i^1]} of beer lie{'s'[i^1:]} on the wall.") 

Output:

0 bottles of beer lie on the wall. 1 bottle of beer lies on the wall. 2 bottles of beer lie on the wall. 3 bottles of beer lie on the wall. 4 bottles of beer lie on the wall. 

Python interprets the first form as [:stop], and the second one as [start:].

Edit. A previous, one-character longer version of the original trick used != instead of ^.

vote vote

76

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.

vote vote

66

Django users have pluralize, a function used in templates:

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

57

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:

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

Then it works like this:

lemon = ["lemon", "lemons"] 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)]}" 

Top 3 video Explaining python - Plural String Formatting

Related QUESTION?