Professional Documents
Culture Documents
Python’s functions offer us a very expressive syntax. We’re going to look into some of the
finer details of how functions in Python work and some techniques we can use to be more
intuitive while writing and calling functions.
Some of this terminology can be used inconsistently between schools, people, and
businesses. Some people don’t differentiate between “parameter” and “argument” when
speaking. It’s useful here because we’re going to be looking at a lot of behavior that looks
very similar in a function definition and a function call, but will be subtly different. But the
distinction is sometimes unnecessary, so don’t get too hung up if something is called a
“parameter” that should be an “argument” or vice versa.
Instructions
1.
In script.py call the function play_record with the argument next_album.
How do you define a variable that you can’t assign a value to yet? You use None.
None is a special value in Python. It is unique (there can’t be two different Nones) and
immutable (you can’t update None or assign new attributes to it).
none_var = None
if none_var:
print("Hello!")
else:
print("Goodbye")
# Prints "Goodbye"
None is falsy, meaning that it evaluates to False in an if statement, which is why the above
code prints “Goodbye”. None is also unique, which means that you can test if something
is None using the iskeyword.
if session_id is None:
print("session ID is None!")
# this prints out "session ID is None!"
submit_review(review)
Default Return
What does a function return when it doesn’t return anything? This sounds like a riddle, but
there is a correct answer. A Python function that does not have any
explicit return statement will return None after completing. This means that all functions in
Python return something, whether it’s explicit or not. For example:
def no_return():
print("You've hit the point of no return")
# notice no return statement
here_it_is = no_return()
print(here_it_is)
# Prints out "None"
Above we defined a function called no_return() and saved the result to a
variable here_it_is. When we print() the value of here_it_is we get None, referring to
Python’s None. It’s possible to make this syntax even more explicit — a return statement
without any value returns None also.
def fifty_percent_off(item):
# Check if item.cost exists
if hasattr(item, 'cost'):
return item.cost / 2
sale_price = return_half_of_cost(product)
if sale_price is None:
print("This product is not for sale!")
Here we have implemented a function that returns the cost of a product with “50% Off” (or
“half price”). We check if the item passed to our function has a cost attribute. If it exists, we
return half the cost. If not, we simply return, which returns None.
When we plug a product into this function, we can expect two possibilities: the first is that
the item has a cost and this function returns half of that. The other is that item does not
have a listed cost and so the function returns None. We can put error handling in the rest of
our code, if we get None back from this function that means whatever we’re looking at isn’t
for sale!
Instructions
1.
Lots of everyday Python functions return None. What’s the return value of a call to print()?
Since print() is a function it must return something.
Create a variable called prints_return and set it equal to a print statement. Make sure to
give the print statement parentheses and give it an argument (like “hi!”).
Stuck? Get a hint
2.
Now print out prints_return.
3.
Inside script.py is a list called sort_this_list. Create a new variable
called list_sort_return and set it equal to sort_this_list.sort().
Stuck? Get a hint
4.
What do you expect list_sort_return to contain?
print(prints_return)
list_sort_return = sort_this_list.sort()
print(list_sort_return)
Default Arguments
Function arguments are required in Python. So a standard function definition that defines
two parameters requires two arguments passed into the function.
Not all function arguments in Python need to be required. If we give a default argument to a
Python function that argument won’t be required when we call the function.
# Calling with two arguments uses the default value for is_admin
user2 = create_user('djohansen')
Above we defined create_user with a default argument for is_admin, so we can call that
function with only the one argument 'djohansen'. It assumes the default value
for is_admin: False. We can make both of our parameters have a default value (therefore
making them all optional).
Set is so that if we call make_folders() without an argument for nest the function assumes
it gets a value of False.
import os
if nest:
"""
./Music/fun/parliament
"""
path_to_new_folder = ""
path_to_new_folder += "/{}".format(folder)
try:
os.makedirs(path_to_new_folder)
except FileExistsError:
continue
else:
"""
"""
try:
os.makedirs(folder)
except FileExistsError:
continue
make_folders(['./Music', './fun', './parliament'])
# Raises a TypeError
def create_user(is_admin=False, username, password):
create_email(username, password)
set_permissions(is_admin)
In the above code, we attempt to define a default argument for is_adminwithout defining
default arguments for the later parameters: usernameand password.
If we want to give is_admin a default argument, we need to list it last in our function
definition:
# Works perfectly
def create_user(username, password, is_admin=False):
create_email(username, password)
set_permissions(is_admin)
Instructions
1.
In script.py the function get_id tries to define a parameter with a default argument before
a required parameter.
request = requests.get(website)
return parsed_html.find(id_=html_id)
Keyword Arguments
When we call a function in Python, we need to list the arguments to that function to match
the order of the parameters in the function definition. We don’t necessarily need to do this if
we pass keyword arguments.
We use keyword arguments by passing arguments to a function with a special syntax that
uses the names of the parameters. This is useful if the function has many optional default
arguments or if the order of a function’s parameters is hard to tell. Here’s an example of a
function with a lot of optional arguments.
1.
In script.py we’ve defined a draw_shape() function that will draw a shape to the terminal!
Call draw_shape() with "m" as the character and line_breaks set to False.
import shapes
if not line_breaks:
print(shape[1:-1])
else:
print(shape)
# call draw_shape() with keyword arguments here:
draw_shape(character='m', line_breaks=False)
returned_list = populate_list(length=4)
print(returned_list)
# Prints [0, 1, 2, 3] -- this is expected
returned_list = populate_list(length=6)
print(returned_list)
# Prints [0, 1, 2, 3, 0, 1, 2, 3, 4, 5] -- this is a surprise!
When we call populate_list a second time we’d expect the list [0, 1, 2, 3, 4, 5]. But
the same list is used both times the function is called, causing some side-effects from the
first function call to creep into the second. This is because a list is a mutable object.
A mutable object refers to various data structures in Python that are intended to be
mutated, or changed. A list has append and remove operations that change the nature of a
list. Sets and dictionaries are two other mutable objects in Python.
It might be helpful to note some of the objects in Python that are not mutable (and therefore
OK to use as default arguments). int, float, and other numbers can’t be mutated
(arithmetic operations will return a new number). tuples are a kind of immutable list. Strings
are also immutable — operations that update a string will all return a completely new string.
Instructions
1.
In script.py we’ve written a helper function that adds a new menu item to an order in a
point-of-sale system. As you can see, we can start a new order by
calling update_order without an argument for current_order. Unfortunately, there’s a bug
in our code causing some previous order contents to show up on other people’s bills!
First, try to guess what the output of this code will be. Then, run script.py.
We’ll fix this function in the next exercise, if you want more of an explanation of what’s
happening here, check out the hint!
current_order.append(new_item)
return current_order
print(order2)
current_books.extend(authors_books)
return current_books
In the above function, we accept current_books a value expected to be a list. But we don’t
require it. If someone calls add_author() without giving an argument for current_books,
we supply an empty list. This way multiple calls to add_author won’t include data from
previous calls to add_author.
Instructions
1.
Update the function so that calls to update_order don’t have side-effects — no order
should affect other orders.
if current_order is None:
current_order = []
current_order.append(new_item)
return current_order
print(order2)
print(sum_and_div)
# Prints "(30, 2)"
print(sum_and_div[0])
# Prints "30"
So we get those two values back in what’s called a tuple, an immutable list-like object
indicated by parentheses. We can index into the tuple the same way as a list and
so sum_and_div[0] will give us our sum_nums value and sum_and_div[1] will produce
our div_nums value.
What if we wanted to save these two results in separate variables? Well we can
by unpacking the function return. We can list our new variables, comma-separated, that
correspond to the number of values returned:
print(sum)
# Prints "27"
print(div)
# Prints "2"
Above we were able to unpack the two values returned into separate variables.
Instructions
1.
In script.py you’ll find the definition of the function scream_and_whisper(). Call the
function with a string of your choice and store the results in the_scream and the_whisper.
def scream_and_whisper(text):
scream = text.upper()
whisper = text.lower()
def shout_strings(*args):
for argument in args:
print(argument.upper())
Note that args is just a parameter name, and we aren’t limited to that name
(although it is rather standard practice). We can also have other positional
parameters before our *args parameter. We can’t, as we’ll see, :
1.
Write your own function, called myjoin() which takes an arbitrary number of
strings and appends them all together, similar to os.path.join().
path_segment_1 = "/Home/User"
path_segment_2 = "Codecademy/videos"
path_segment_3 = "cat_videos/surprised_cat.mp4"
def myjoin(*args):
joined_string = args[0]
return joined_string
def arbitrary_keyword_args(**kwargs):
print(type(kwargs))
print(kwargs)
# See if there's an "anything_goes" keyword arg
# and print it
print(kwargs.get('anything_goes'))
arbitrary_keyword_args(this_arg="wowzers",
anything_goes=101)
# Prints "<class 'dict'>
# Prints "{'this_arg': 'wowzers', 'anything_goes': 101}"
# Prints "101"
As you can see, **kwargs gives us a dictionary with all the keyword
arguments that were passed to arbitrary_keyword_args. We can access these
arguments using standard dictionary methods.
Since we’re not sure whether a keyword argument will exist, it’s probably
best to use the dictionary’s .get() method to safely retrieve the keyword
argument. Do you remember what .get() returns if the key is not in the
dictionary? It’s None!
Instructions
1.
The string .format() method can utilize keyword argument syntax if you give
placeholder names in curly braces in a string. For example:
# Checkpoint 1
name="Tim",
feeling="10/10",
))
# Checkpoint 2
def create_products(**products_dict):
create_product(name, price)
# Checkpoint 3
create_products(
Baseball='3',
Shirt='14',
Guitar='70')
if '-a' in args:
user_list = all_users()
if kwargs.get('active'):
user_list = [user for user_list if user.active]
main("files/users/userslist.txt",
"-d",
"-a",
save_all_records=True,
user_list=current_users)
In the body of main() these arguments would look like this:
filename == "files/users/userslist.txt"
args == ('-d', '-a)
user_list == current_users
kwargs == {'save_all_records': True}
We can use all four of these kinds of parameters to create functions that
handle a lot of possible arguments being passed to them.
Instructions
1.
In script.py you’ll find the function remove() has three parameters: the
required positional filename, the arbitrary positional args, and the arbitrary
keyword kwargs.
2.
Now iterate over every kwarg and replacement in kwargs.items() (recall this is
how to iterate over key-value pairs in a dictionary).
3.
Now remove the bottom comment and see the text of Robin Hood;
Being A Complete History Of All The Notable And Merry Exploits
Performed By Him And His Men, On Many Occasions.by William
Darton transformed!
text = file_obj.read()
def takes_many_args(*args):
print(','.join(args))
1.
We’ve got a latecomer to the new create_products syntax who wants to still
pass in a dictionary. Unpack new_product_dictwhile passing it
to create_products() as an argument.
def create_products(**products_dict):
create_product(name, price)
new_product_dict = {
'Apple': 1,
'Ice Cream': 3,
'Chocolate': 5,
# as kwargs!
create_products(**new_product_dict)
The Nile
The Nile fullfilment agency brings everything you could possibly want
straight to your door! Use your knowledge of Python functions and how to
manipulate arguments to help automate practices for the biggest world-
changing company.
If you get stuck during this project, check out the project walkthrough
video which can be found at the bottom of the page after the final step of
the project.
Tasks
0/24Complete
At The Nile our biggest concern is our consumers, and our biggest cost is
delivering their goods to them. We want to develop a program that will
help us minimize these costs so we can continue making everyone happy.
First we’ll need to calculate these costs using a function that you’re going
to write called calculate_shipping_cost().
2.
Both from_coords and to_coords are tuples, containing first the latitude and
then the longitude. Since our get_distance()function looks for all four as
separate arguments, we’ll need to separate these variables out.
Inside calculate_shipping_cost unpack those tuples
into from_lat, from_long, to_lat, and to_long.
Stuck? Get a hint
3.
Now call get_distance(from_lat, from_long, to_lat, to_long) and save the result
as distance.
There’s other ways to separate those two coordinates when calling this
function, how would you have done it?
Stuck? Get a hint
4.
5.
7.
What about our shoppers who hastily purchase goods without indicating
their shipping type? Let’s give our function a default argument
for shipping_type. Since they’re in such a hurry let’s make the default
argument 'Overnight'. They’ll be happier to get what they ordered earlier,
and we’ll be happier because they paid more money for it. It’s a win-win!
8.
Our team is important, and we want to make sure the hardest workers find
their home in our careers. In order to do that, we need to figure out who
the best person is for each job.
10.
In order to find the best person, we need to calculate how much it would
cost for any of the drivers to fulfill this order.
Now let’s iterate over every driver in drivers. Use a for loop.
Stuck? Get a hint
12.
Now we want to check if the current driver is the cheapest driver we’ve
looked at.
First, we’ll check if cheapest_driver is None, this likely means this is the first
driver we’ve looked at.
After outdenting out of our elif statement and the for loop,
return cheapest_driver_price and cheapest_driver.
Stuck? Get a hint
Great first day, friend! Let’s try and figure out all the money you’ve saved us
today.
This function will be passed a number of Trip IDs with corresponding trip
information as arguments, so let’s just take any keyword arguments passed
into it. Store them all as trips!
Stuck? Get a hint
18.
20.
22.
Script.py
# test_function(calculate_shipping_cost)
# test_function(calculate_driver_cost)
# Define calculate_money_made() here
# test_function(calculate_money_made)
Nile.py
c = 2 * atan2(sqrt(a), sqrt(1-a))
distance = a * c
return distance
SHIPPING_PRICES = {
'Ground': 1,
'Priority': 1.6,
'Overnight': 2.3,
def format_price(price):
return "${0:.2f}".format(price)
test.py
def test_function(fn):
if fn.__name__ == "calculate_shipping_cost":
test_shipping(fn)
if fn.__name__ == "calculate_driver_cost":
test_driver(fn)
if fn.__name__ == "calculate_money_made":
test_money(fn)
def test_shipping(f):
try:
except TypeError:
return
return
if costs != "$1.04":
return
class Driver:
self.speed = speed
self.salary = salary
def __repr__(self):
def test_driver(f):
try:
except TypeError:
return
return
if price != 1600:
print("calculate_driver_cost() did not provide correct final price (expected {}, received
{})".format(price,1600))
return
return
class Trip:
driver.cost = driver_cost
self.driver = driver
def test_money(f):
try:
except TypeError:
return
return
if money != 445:
print("calculate_driver_cost() did not provide correct final price (expected {}, received
{})".format(money, 445))
return