Functions

Python (and most languages) have a way to wrap code up so it can be called repeatedly: functions.

A function is a piece of code with a name. Other code calls the function when it wants something done.

Let's take the name tag program we wrote at the beginning of the course.

  • first_name = input('What is your first name? ')
  • last_name = input('What is your last name? ')
  • full_name = first_name + ' ' + last_name
  • print('Hello!')
  • print('My name is')
  • print(full_name)
Ray
Ray

Man, that code looks so easy now.

Aye. Difficulty is a relative thing, not absolute.

Let's say we have a list of people we want name tags for. We could write this:

  1. first_name = 'Wednesday'
  2. last_name = 'Addams'
  3. full_name = first_name + ' ' + last_name
  4. print('Hello!')
  5. print('My name is')
  6. print(full_name)
  7.  
  8. first_name = 'Xavier'
  9. last_name = 'Thorpe'
  10. full_name = first_name + ' ' + last_name
  11. print('Hello!')
  12. print('My name is')
  13. print(full_name)
  14.  
  15. first_name = 'Tyler'
  16. last_name = 'Galpin'
  17. full_name = first_name + ' ' + last_name
  18. print('Hello!')
  19. print('My name is')
  20. print(full_name)
  21.  
  22. first_name = 'Enid'
  23. last_name = 'Sinclair'
  24. full_name = first_name + ' ' + last_name
  25. print('Hello!')
  26. print('My name is')
  27. print(full_name)

(I love that show.)

Repeat for as many people we have.

A better way

Can we avoid having so much repeated code? Check this out:

  1. def make_name_tag(first_name, last_name):
  2.     full_name = first_name + ' ' + last_name
  3.     print('Hello!')
  4.     print('My name is')
  5.     print(full_name)
  6.  
  7. make_name_tag('Wednesday', 'Addams')
  8. make_name_tag('Xavier', 'Thorpe')
  9. make_name_tag('Tyler', 'Galpin')
  10. make_name_tag('Enid', 'Sinclair')

Lines 1 to 5 make a function called make_name_tag. (def stands for "define.") We've taken out the repeated code, and made it into it's own little chunk.

Ethan
Ethan

That's so much less code.

Yes, it is. From 27 lines to 10. Less code means faster to write, easier to debug, and faster to change. Cost control!

These things...

  • def make_name_tag(first_name, last_name):

... are called parameters or arguments. They're like placeholders. Whatever value you put into them goes into those slots in the code.

Parameters

Python doesn't care what the parameters are called. It just matches up their names with places in the code.

Any of these would work:

  • def make_name_tag(first_name, last_name):
  •     full_name = first_name + ' ' + last_name
  •     print('Hello!')
  •     print('My name is')
  •     print(full_name)
  •  
  • def make_name_tag(name_first, name_last):
  •     full_name = name_first + ' ' + name_last
  •     print('Hello!')
  •     print('My name is')
  •     print(full_name)
  •  
  • def make_name_tag(thing1, thing2):
  •     full_name = thing1 + ' ' + thing2
  •     print('Hello!')
  •     print('My name is')
  •     print(full_name)
  •  
  • def make_name_tag(something, something_else):
  •     full_name = something + ' ' + something_else
  •     print('Hello!')
  •     print('My name is')
  •     print(full_name)
Reflect

Which one of the four do you like best?

If you were logged in as a student, the lesson would pause here, and you'd be asked to type in a response. If you want to try that out, ask for an account on this site.
Adela
Adela

The first one. The parameters' names tell you what they mean.

Aye, that's so. Best to use meaningful names everywhere. That includes the names of functions, as well as parameters.

Bee tee dubs, a function can have any number of params, from zero on up.

Ethan
Ethan

Could you do this?

  • def make_name_tag(first_name, last_name):
  •     full_name = first_name + ' ' + last_name
  •     print('Hello!')
  •     print('My name is')
  •     print(full_name)
  •  
  • given_name = 'Wednesday'
  • family_name = 'Addams'
  • make_name_tag(given_name, family_name)
Reflect

Would that work?

If you were logged in as a student, the lesson would pause here, and you'd be asked to type in a response. If you want to try that out, ask for an account on this site.
Georgina
Georgina

I'm guessing it would, since the parameter names can be anything.

Ray
Ray

Wait. Wouldn't you need to change the parameters names? To:

def make_name_tag(given_name, family_name):

It might seem like that, but Georgina's right. def doesn't care what the parameters are called.

Ray
Ray

OK, but how does it know which name is the first?

Python uses the position of the parameters. The first parameter in the call matches the first param in the def, the second param in the call matches the second in the def, and so on.

Parameter order matters

Georgina
Georgina

Wait, doesn't that mean you could mix things up accidentally? Like this:

  1. def make_name_tag(first_name, last_name):
  2.     full_name = first_name + ' ' + last_name
  3.     print('Hello!')
  4.     print('My name is')
  5.     print(full_name)
  6.  
  7. family_name = 'Addams'
  8. given_name = 'Wednesday'
  9. # Wrong order!!
  10. make_name_tag(family_name, given_name)
Reflect

What would this code output?

If you were logged in as a student, the lesson would pause here, and you'd be asked to type in a response. If you want to try that out, ask for an account on this site.
Ethan
Ethan

You'd get:

  • Hello!
  • My name is
  • Addams Wednesday

That's right. It's a fairly common bug to get parameters in the wrong order.

An even better way

Actually, I'd write the program like this:

💩Smelly old function💩 ✨Sparkling new lemon-fresh function✨

  • def make_name_tag(first_name, last_name):
  •     full_name = first_name + ' ' + last_name
  •     print('Hello!')
  •     print('My name is')
  •     print(full_name)
  •  
  • make_name_tag('Wednesday', 'Addams')
  • make_name_tag('Xavier', 'Thorpe')
  • make_name_tag('Tyler', 'Galpin')
  • make_name_tag('Enid', 'Sinclair')
  • def make_name_tag(first_name, last_name):
  •     tag_text = 'Hello!\n'
  •     tag_text += 'My name is\n'
  •     tag_text += first_name + ' ' + last_name + '\n'
  •     return tag_text
  •  
  • print(make_name_tag('Wednesday', 'Addams'))
  • print(make_name_tag('Xavier', 'Thorpe'))
  • print(make_name_tag('Tyler', 'Galpin'))
  • print(make_name_tag('Enid', 'Sinclair'))

The new function doesn't print directly. Instead, it assembles the output into a string, and returns it to the caller. That's what the return statement does: send a value to whatever code called the function.

Ethan
Ethan

What's the \n thing?

Remember, a string can contain any character you can type. When you hit Enter, you're typing a character that means "go to the next line." Most languages use \n to show Enter in a string.

Georgina
Georgina

So... would this code make the Dr. Seuss thing?

  • rhyme = 'One fish\nTwo fish\nRed fish\nBlue fish\n'
  • print(rhyme)

Aye! It would. You can put them ol' \ns in the middle of a string, though I don't usually. I prefer to keep the code looking like the output:

  • rhyme = 'One fish\n'
  • rhyme += 'Two fish\n'
  • rhyme += 'Red fish\n'
  • rhyme += 'Blue fish\n'
  • print(rhyme)

There are other ways to do the multiline-string spell. You can google it.

Ray
Ray

OK, but you haven't told us why you like the lemon-fresh approach.

Oh, right! Here's the code again.

💩Smelly old function💩 ✨Sparkling new lemon-fresh function✨

  • def make_name_tag(first_name, last_name):
  •     full_name = first_name + ' ' + last_name
  •     print('Hello!')
  •     print('My name is')
  •     print(full_name)
  •  
  • make_name_tag('Wednesday', 'Addams')
  • make_name_tag('Xavier', 'Thorpe')
  • make_name_tag('Tyler', 'Galpin')
  • make_name_tag('Enid', 'Sinclair')
  • def make_name_tag(first_name, last_name):
  •     tag_text = 'Hello!\n'
  •     tag_text += 'My name is\n'
  •     tag_text += first_name + ' ' + last_name + '\n'
  •     return tag_text
  •  
  • print(make_name_tag('Wednesday', 'Addams'))
  • print(make_name_tag('Xavier', 'Thorpe'))
  • print(make_name_tag('Tyler', 'Galpin'))
  • print(make_name_tag('Enid', 'Sinclair'))

The smelly version of make_name_tag always shows the output in the console. The prints are in the function. If you want to change where the output goes, you change the function. It's best to write functions that don't need to change much.

The sparkly new version returns a string to the main program. You can do anything with the string, like put it in a variable, print it, whatevs.

Here's a version that accumulates all the output into the variable tags, and sends it to a network printer:

  • def make_name_tag(first_name, last_name):
  •     tag_text = 'Hello!\n'
  •     tag_text += 'My name is\n'
  •     tag_text += first_name + ' ' + last_name + '\n'
  •     return tag_text
  •  
  • # Accumulate name tags.
  • tags = make_name_tag('Wednesday', 'Addams')
  • tags += make_name_tag('Xavier', 'Thorpe')
  • tags += make_name_tag('Tyler', 'Galpin')
  • tags += make_name_tag('Enid', 'Sinclair')
  •  
  • send_to_network_printer('printer3', tags)

The function make_name_tag hasn't changed from the print-to-console version.

I made up send_to_network_printer for this example. It doesn't exist.

This next one accumulates the output and writes it to a file, so you can edit it with Writer or Word, email it to someone, whatevs.

  • def make_name_tag(first_name, last_name):
  •     tag_text = 'Hello!\n'
  •     tag_text += 'My name is\n'
  •     tag_text += first_name + ' ' + last_name + '\n'
  •     return tag_text
  •  
  • # Accumulate name tags.
  • tags = make_name_tag('Wednesday', 'Addams')
  • tags += make_name_tag('Xavier', 'Thorpe')
  • tags += make_name_tag('Tyler', 'Galpin')
  • tags += make_name_tag('Enid', 'Sinclair')
  •  
  • # Writing to file
  • with open('./name tags.txt', 'w') as name_tags_file:
  •     # Writing data to a file
  •     name_tags_file.write(tags)

This one does work. We'll talk more about files later.

Again, make_name_tag hasn't changed from the print-to-console version.

Georgina
Georgina

That's so cool!

You can use the same function to make the name tag data, but then do whatever with it.

Aye, it has excessive cools. You can reuse the function any way you want.

Changey changey

Here's our code again, the network printer version.

  1. def make_name_tag(first_name, last_name):
  2.     tag_text = 'Hello!\n'
  3.     tag_text += 'My name is\n'
  4.     tag_text += first_name + ' ' + last_name + '\n'
  5.     return tag_text
  6.  
  7. # Accumulate name tags.
  8. tags = make_name_tag('Wednesday', 'Addams')
  9. tags += make_name_tag('Xavier', 'Thorpe')
  10. tags += make_name_tag('Tyler', 'Galpin')
  11. tags += make_name_tag('Enid', 'Sinclair')
  12.  
  13. send_to_network_printer('printer3', tags)

Let's say we want to say G'day instead of Hello, for all the name tags.

Reflect

How would you change the code?

If you were logged in as a student, the lesson would pause here, and you'd be asked to type in a response. If you want to try that out, ask for an account on this site.
Adela
Adela

Just change line 2 from print('Hello!') to print("G'day").

Right. This works because the same function, make_name_tag, makes all the tags. Change it, and all the tags change.

Functions make teams work

Complex programs aren't written by one person, usually. Different people work on different parts of the same program at the same time.

Ethan
Ethan

How does that work? If all four of us worked on a program, how would we know what variables names to use, prompts, like that?

The key is coordinating ahead of time. You set things up so different people work on different pieces of code, and it works when you put it all together.

You saw one coordination mechanism early in the course.

Reflect

What coordination mechanism have you seen?

If you were logged in as a student, the lesson would pause here, and you'd be asked to type in a response. If you want to try that out, ask for an account on this site.

(Crickets)

Adela
Adela

Oh! Is it those structure diagrams?

Aye! Here's one Adela made:

Structure diagram

​Structure diagram

Ethan
Ethan

How does that help?

Adela: Aha!
Adela

Ooo! I get it! The input code needs to make the variables small_goats, medium_goats, large_goats, and months. We could give that Georgina, and she could write the code, since she knows what it needs to send to the next step.

Ethan could write the processing code at the same time as Georgina is doing her work. He knows what Georgina's code will give, and what the next chunk, output, needs.

Aye! Good thinking, Adela.

Functions give you even better coordination. You could tell someone (let's call her Elle Cordova (I'm a fan)):

"Write a function called make_name_tag that takes two string parameters: first name, and last name, in that order. The function returns the text of a name tag."

You don't have to tell Elle where first and last name come from. The function won't care. You don't have to give the parameter names, like first_name. It doesn't matter, since params values are matched up by position. These are fine:

  • def make_name_tag(first_name, last_name):
  •  
  • def make_name_tag(given_name, family_name):

The first parameter is the first name, and the second is the last name, no matter what Elle names them.

This wouldn't be OK:

  • def make_name_tag(last_name, first_name):

It doesn't match the specifications (specs) we gave Elle: "takes two string parameters: first name, and last name, in that order."

Let's leave things here for now. We'll talk more about cool function stuff later.

Summary

  • A function is a piece of code with a name.
  • Other code calls the function when it wants something done.
  • Send data into functions with parameters, also called arguments. They're like placeholders. Functions can have any number of parameters, including none.
  • Python uses the position of parameters to work out what goes where.
  • Functions can send data back to callers with the return statement.
  • \n in a string means, "Press the Enter key here."
  • Change something in a function, and it will apply every time the function is called.
  • Functions help coordinate teams.