Tutorial 6 (Part 2)¶
This continuation covers more advanced function concepts, including preventing list modification, handling arbitrary arguments, and importing modules.
Golden Rule: Write and design your functions so that they do exactly one job. Nobody likes spaghetti code!
1. Preventing a Function from Modifying a List¶
As learned in Part 1, passing a list to a function allows the function to modify the original list permanently. If you want to protect your original list, you can pass a copy of it using slice notation [:].
# The [:] makes a copy of the list to send to the function
print_models(unprinted_designs[:], completed_models)
Developer Note: Even though you can do this, you should generally pass the original list unless you have a specific reason not to. Passing copies of large lists eats up time and memory.
2. Passing an Arbitrary Number of Arguments (*args)¶
Sometimes you don't know ahead of time how many arguments a user will pass to your function. You can use the * operator to tell Python to collect as many arguments as provided and pack them into a Tuple.
def make_pizza(*toppings):
"""Print the list of toppings that have been requested."""
print(toppings)
make_pizza('pepperoni')
make_pizza('mushrooms', 'green peppers', 'extra cheese')
# Output:
# ('pepperoni',)
# ('mushrooms', 'green peppers', 'extra cheese')
Note: You will often see developers use the generic parameter name *args for this.
Mixing Positional and Arbitrary Arguments¶
If your function needs to accept different kinds of arguments, the arbitrary parameter (*toppings) must be placed last in the function definition. Python matches positional arguments first, then tosses everything else into the tuple.
def make_pizza(size, *toppings):
print(f"\nMaking a {size}-inch pizza with the following toppings:")
for topping in toppings:
print(f"- {topping}")
make_pizza(16, 'pepperoni')
make_pizza(12, 'mushrooms', 'green peppers', 'extra cheese')
3. Using Arbitrary Keyword Arguments (kwargs)¶
If you expect extra data from the user but aren't sure exactly what kind of data it will be (like a user profile where some people provide ages, others provide locations), you can use the `` operator.
This tells Python to create an empty Dictionary to hold all the extra key-value pairs the user provides.
def build_profile(first, last, **user_info):
"""Build a dictionary containing everything we know about a user."""
# We add the mandatory positional arguments into the dictionary
user_info['first_name'] = first
user_info['last_name'] = last
return user_info
user_profile = build_profile('albert', 'einstein', location='princeton', field='physics')
print(user_profile)
# Output:
# {'location': 'princeton', 'field': 'physics', 'first_name': 'albert', 'last_name': 'einstein'}
Note: You will often see the generic parameter name kwargs (keyword arguments) used for this.
Addressing Your Doubt: Why is the dictionary showing
locationandfieldbeforefirst_nameandlast_name? Modern Python does remember the order items are added to a dictionary. When you called the function,locationandfieldwere instantly packed into the**user_infodictionary. After that happened, your code explicitly addedfirst_nameandlast_nameto that existing dictionary. Since they were added last, they show up at the end!
4. Using Modules¶
A module is just a normal .py file. A package is a collection of modules, and a library is a collection of packages. Modules allow you to reuse functions without writing them from scratch every time.
Assume we have a file called pizza.py containing our make_pizza function. We can import it into a new file, making_pizzas.py, located in the same folder.
Importing an Entire Module¶
This imports the whole file. You must use dot notation (module_name.function_name()) to use its functions.
Importing Specific Functions¶
This imports just the function you need. You no longer need to use dot notation.
Using as to Give an Alias¶
If a function or module name is too long (or conflicts with something else in your code), you can give it a nickname using the as keyword.
Aliasing a Function:
Aliasing a Module:
Importing All Functions (Be Careful!)¶
You can import every single function from a module using the * operator.
Danger: Avoid this in production code! If you import a large module this way and it happens to have a function with the exact same name as a function you already wrote, Python will silently overwrite yours, causing massive bugs.
5. Styling Conventions for Functions¶
When specifying a default value for a parameter, or when passing keyword arguments during a function call, do not use spaces on either side of the equal sign (=).