JJCINC
JJCINC

Reputation: 15

TypeError when Using Function in .format()

I'm going through Learn Python the Hard Way exercise 24 while converting all Old style formatting (%) they used in the book into my preferred New style (.format()).

As you can see in the code below, I can successfully unpack the tuple values returned by the function if I assign a variable "p". But when I use that return value directly, it throws a TypeError.

def secret_formula(started):
    jelly_beans = started * 500
    jars = jelly_beans / 1000
    crates = jars / 100
    return jelly_beans, jars, crates

start_point = 10000

#Old style
print("We'd have %d beans, %d jars, and %d crates." % secret_formula(start_point))

#New style that works
print("We'd have {p[0]:.0f} beans, {p[1]:.0f} jars, and {p[2]:.0f} crates.".format(p=secret_formula(start_point)))

#This doesn't work:
print("We'd have {0:.0f} beans, {1:.0f} jars, and {2:.0f} crates.".format(secret_formula(start_point)))

Throws error:

Traceback (most recent call last):
      File "ex.py", line 16, in <module>
        print("We'd have {0:.0f} beans, {1:.0f} jars, and {2:.0f} crates.".format(secret_formula(start_point)))
    TypeError: unsupported format string passed to tuple.__format__
  1. Can someone explain why using function in .format() directly does not work?
  2. How does one convert it into f-string?

Upvotes: 0

Views: 265

Answers (2)

MrE
MrE

Reputation: 20778

that's because you're passing a tuple of 3 values as the output of your function

To make this work, you need to unpack the tuple, with the *

print("We'd have {0:.0f} beans, {1:.0f} jars, and {2:.0f} crates.".format(*secret_formula(start_point)))

You can also do this with an object, where keys should match the function parameter names, like:

def func(param, variable):
  return None

args = {'param': 1, 'variable': 'string'}
func(*args)

Upvotes: 6

user2357112
user2357112

Reputation: 280335

Passing the return value of secret_formula to format positionally isn't any more direct than passing it by keyword. Either way, you are passing the return value as a single argument.

To access the elements of that argument when you pass it as the p keyword argument, you use p[0], p[1], and p[2]. Similarly, when passing the argument positionally, you would have to access the elements as 0[0], 0[1], and 0[2], specifying the position 0. (This is specifically how str.format handles format placeholders, not normal Python indexing syntax):

print("We'd have {0[0]:.0f} beans, {0[1]:.0f} jars, and {0[2]:.0f} crates.".format(
      secret_formula(start_point)))

However, it would be simpler and more conventional to instead unpack the return value with *, passing the elements as separate arguments:

print("We'd have {0:.0f} beans, {1:.0f} jars, and {2:.0f} crates.".format(
      *secret_formula(start_point)))

Upvotes: 2

Related Questions