Book a Demo!
CoCalc Logo Icon
StoreFeaturesDocsShareSupportNewsAboutPoliciesSign UpSign In
DataScienceUWL
GitHub Repository: DataScienceUWL/DS775
Path: blob/main/Lessons/Lesson 01 - LP 1/extras/Python Refresher - Functions.ipynb
871 views
Kernel: Python 3 (system-wide)

Functions in Python

Functions are reusable bits of code that can be your new best friend. Any time you find yourself writing the same bit of code more than once, just changing a variable value here or there, you should consider writing a function instead.

Functions, themselves, consist of two parts:

  • The definition line

  • The guts

You'll also need to "call" the function to get it to do something.

Let's look at an example.

Reading through the above code, the definition line declares that the name of the function is "sayHello" and that when we call it, we'll pass in 2 parameters - a name and a species.

A note on names: I like to use function names that start with a verb, because typically functions are performing some action. But, it's really up to you who you name them. Just keep them descriptive but short. You should be able to tell what your function is going to do by looking at the name.

The guts of the code initialize a blank variable, run a conditional statement to check if the species is dog, and fill in text for the variable.

The function ends with returning a single variable, in this case the string held in the variable "response."

Finally, we call the function, telling it that the name we're dealing with is Bob, and that Bob is a dog.

Before you run the code below, read the code try to figure out what the output will be.

def sayHello(name, species): response = '' if species == 'dog': response = "Come here " + name + "! Who's the best doggo in the world? You are!" else: response = 'Hello ' + name + ". Pleased to meet you" return response sayHello('Bob', 'dog')

Scope

Functions have a local scope. Variables that you set inside the function will not be available outside the function. Look at the code below and try to figure out what will happen before you run it. Will the code work?

def tryScopes(passedInVariable): insideVariable = passedInVariable return insideVariable outsideVariable = tryScopes('I got passed in.') print(outsideVariable) print(insideVariable)

Printing the insideVariable will fail, because it was created inside the function. It lives and dies inside the function, and it will get rewritten every time the function is called.

In many languages, functions don't have access to variables that are initialized outside the function. You'd need to pass any variable you need inside the function into the function when you call it.

That's not true with Python. Python will let you access variables as long as they were created before the function call. But doing that is a terrible habit to get into, and will definitely cause difficult-to-debug errors. (You can all chastise Dr. B. when he does it in some upcoming lessons.)

See the example below:

def breakDeannasHeart(): return brokenHeart brokenHeart = 'This variable should never be used directly in a function because it was created outside the function and not passed in.' breakDeannasHeart()

Functions with Pyomo

Now that you have some basics under your belt, you'll need to think about a few things as you're designing your own functions for this course:

  • What data does your function need (what variables are you passing in)?

  • What does your function need to return?

Let's go back to our favorite example, Wyndor. Remember that in the Wyndor model, we're trying to determine the number of doors and windows to make to maximize our profit. But, what if we have a three different markets for our products, and each market has slightly different profits? If we wanted to solve the problem for each market, we could write the code 3 times, with 3 different profit sets. Or, we could write a single function, and call it three times.

In this case, the function needs the profits per batch passed in, and we want to return the maximum profit and the number of windows and doors. Let's see what that looks like.

# This bit of code can be outside the function, because we don't need to import each time we call the function from pyomo.environ import * import babel.numbers as numbers # needed to display as currency #here's where our function starts def calcWyndorProfit(doorProfit, windowProfit): # Concrete Model model = ConcreteModel(name="Wyndor") products = ['drs', 'wdw'] bounds_dict = {'drs': (0, 4), 'wdw': (0, 6)} def bounds_rule(model, product): return (bounds_dict[product]) model.x = Var(products, domain=Reals, bounds=bounds_rule) # Objective - Here we swapped out our hard-coded door and window profit for the variables we passed in model.profit = Objective(expr=doorProfit * model.x['drs'] + windowProfit * model.x['wdw'], sense=maximize) # Constraints model.Constraint3 = Constraint( expr=3.0 * model.x['drs'] + 2.0 * model.x['wdw'] <= 18) # Solve solver = SolverFactory('glpk') solver.solve(model) #Here we are going to set up the variables to return profit = numbers.format_currency(1000 * model.profit(), 'USD', locale='en_US') doors = model.x['drs']() windows = model.x['wdw']() return(profit,doors,windows)

Now we have our function, but we haven't run it yet. To run it, we'll need to call it. We can call it a single time. Note that because our function returns 3 variables, we need to assign the output to three variables. In this case, the variables I'm assigning outside are the same names as the ones that are assigned inside.

profit, doors, windows = calcWyndorProfit(3, 5) print("Profit = ",profit) print("Batches of Doors = ", doors) print("Batches of Windows = ", windows)

But, we can use any variable names when we assign the results of our functions. This works, too: (But don't name your variables like this. Future you will hate yourself.)

zipp, zapp, zoop = calcWyndorProfit(3, 5) print("Profit = ",zipp) print("Batches of Doors = ", zapp) print("Batches of Windows = ", zoop)

We didn't really gain anything by writing a function yet.

But, we can run the function in a loop. Here's where we just got way more efficient. Instead of copy/pasting all that code 3 times, we'll just call it with 3 different sets of profits!

#set up a list of tuples - these are our window and door profits profits = [(3,5), (3,6),(2,5)] #loop through our list of tuples, calling the function once per tuple for p in profits: profit, doors, windows = calcWyndorProfit(p[0], p[1]) #this is how you fetch each value out of the tuple print("Profit = ",profit) print("Batches of Doors = ", doors) print("Batches of Windows = ", windows)

Returning vs. Printing

Functions can return variables or they can just print things out. In this example, I could have reduced my coding even more by just printing in the function. (But, hey, I wanted to show returning multiple variables.)

As an exercise for you, rewrite the Wyndor function so that it prints results instead of returning them.