2
def my_function(fname):
    print(fname + " Refsnes")

my_function("Emil")
my_function("Tobias")
my_function("Linus")

def my_greeting(*fname):
    print(fname + " Smith")

my_greeting("Don")

The first part runs fine the second part crashes. Here are the results.

Emil Refsnes
Tobias Refsnes
Linus Refsnes

Traceback (most recent call last):
  File "C:/Data/Python/turtleFun/kikiFunction.py", line 19, in <module>
    my_greeting("Don")
  File "C:/Data/Python/turtleFun/kikiFunction.py", line 17, in my_greeting
    print(fname + " Smith")
 TypeError: can only concatenate tuple (not "str") to tuple

I haven't created a tuple, I'm not trying to concatenate. The structures are the same but one runs and the other crashes. Why does Python 3.8.3 think I have a tuple? How do I fix? Tuples would be

myTuple = ("Don", "Fred", "George", "Paul", "John", "Ringo")
jonrsharpe
  • 115,751
  • 26
  • 228
  • 437

1 Answers1

5

Problem

The * next to *fname makes fname a tuple, always.

Understanding what's going on

Let's see what a function with *fname as a parameter really does.

>>> def func(*fnames):
...     print(fnames)
...     print(type(fnames))
>>> func('str 1', 'str 2')
('str 1', 'str 2')
<class 'tuple'>

Look at that! Even though it seems like func takes just 1 parameter (*fnames) we don't get an error even though when we call it with 2 parameters ('str 1' and 'str 2'). That's because the * in *fnames lets any number of parameters (including 0!) to be passed to func, and all of those parameters get passed in as 1 tuple.

This is why, even when you pass just 1 argument to the function, fnames still takes on the type of tuple. In your example:

def my_greeting(*fname):
    print(fname)  # let's not concatenate for now

my_greeting('Don')  # prints a tuple: ('Don',)

Why doesn't Python only use tuples when *fnames is given 2+ args?

Python always uses tuples for *-type arguments so that our code can always expect a certain type. Otherwise, my_greeting('Don') would set fnames to a str, while my_greeting('Don', 'Don2') would set fnames to a tuple of str.

I hope this helps you learn!

Thomas
  • 859
  • 6
  • 16
  • 1
    Also: `*fnames` doesn't just allow for 1 or more arguments, it allows for *zero* arguments. `my_greeting()` is legal, and having an empty tuple lets you distinguish that from any other possible single argument. – chepner Jun 05 '20 at 16:05
  • Yep! Good point, adding that now. – Thomas Jun 05 '20 at 16:06
  • 1
    Thanks Thomas! The video series I'm learning from didn't mention that the *fnames would be treated as a tuple when you passed an argument of ("Don") to it, or that you could just pass (Don) and it would be treated as a string. I've learned a LOT about python from this free online video class, but it's left me with a LOT of unanswered questions. I will add this info to my notes. – Donald_Smith Jun 06 '20 at 11:54