Plural String Formatting Plural String Formatting python python

Plural String Formatting


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 inflectp = inflect.engine()# UNCONDITIONALLY FORM THE PLURALprint("The plural of ", word, " is ", p.plural(word))# CONDITIONALLY FORM THE PLURALprint("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() }


Using custom formatter:

import stringclass 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.


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

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

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.