From the course: Functional Programming with Python

Functions as data, part 1 - Python Tutorial

From the course: Functional Programming with Python

Start my 1-month free trial

Functions as data, part 1

- I mentioned in a previous video that functional programming treats functions as first class citizens. This means that we can treat functions in a very similar way to other types such as: strings, numbers or booleans. In this video we're going to take a look at what this means for our code, and a few useful things that we can do with it. The easiest way to demonstrate how first class functions can be used like other data types is this example. Lets start off by defining a simple function. We'll call it say hello. And all it's going to do is print the word hello to the console. Like this. Print hello. Now obviously we can just call this say hello function by adding parentheses after it, and it will do what it says inside the function body. But because of first class functions, what we can also do, is define another function. We'll call it say hello two, and simply set it equal to our say hello function, like this. Now we can call say hello two by adding parentheses after it just like we could call the regular say hello function. If we run our code, which we can do, by typing python three zero two zero one dot py, we see that the results exactly the same as if we had called the original say hello function. We can also do this same kind of thing when we add arguments to our function. For example if we were to add a name argument to our say hello function, like this... ...And put that name inside the string that we print out, we can now pass an argument to this new say hello two function, and once again it will be just the same as if we'd called the original say hello function with it's first argument. Now something to notice is that when we define out second function, and set it equal to our first function this is exactly the same syntax that we use when defining other types such as numbers or strings. This means that just with these other types we can do interesting things when working with functions. For example we can use a turnery operator to dynamically change what a given function does. One possible application of this is for mocking out pieces of code during development. If we have a piece of code that's especially time intensive such as network or database operations, or code that's destructive such as deleting a whole database, it can really get in the way of our development process. Now that we know this concept of treating functions like other data types, what we can do is define a mocked out version of our function that returns some fake data, without doing all the time intensive operations of the real version. We then define an environment flag that we pass while running our application in development mode. Say something like environment... ...equals dev, and then what we do is we define our function conditionally based on the value of this flag at run time. Don't worry if all that doesn't quite make sense just yet. That's a lot of ideas that we covered right there. So lets go through it piece-by-piece, and see what it looks like in code. So we've got our environment variable here. Lets define our network operation function. In the real world this function would probably do something like fetch data from an API. So we'll call our function fetch data real, and define it like this, define fetch data real. And for now we'll just have it print something. Doing some very time intensive operations. Of course in the real world this is where the time intensive operations would take place. But in order to keep this example simple, we're just going to leave that out for now. Now let's define a stub function that will just return some fake data. The idea here is that we can use this fake data for development without having to wait for whatever lengthy operations the real function has to complete. So we'll define a new function. We'll call it fetch data fake. And we'll say print, returning fake data. We'll have it return some fake data in the form of a dictionary. We'll just say name, age. Finally we'll define the function that would be used elsewhere in our program. This is where we use a turnery operator to decided which of the function definitions, the real or the fake one, will be used at run time. So we'll define this function, and we'll call it simply fetch data, since it decides between fetch data real and fetch data fake at run time. And again instead of defining this function using the def keyword we can just define it by saying fetch data equals fetch data real, and here's where we use the turnery operator. We'll say it equals fetch data real if our environment variable is equal to prod. In other words, if it's running in production. Otherwise we want it to be fetch data fake. And then what we would do if this was a real program is we would say something like data equals fetch data. And if our code was running in a production environment it would actually perform that fetching operation, and get the data from our API. Otherwise if it's running in the dev environment it will just return the fake data that we defined here. So if we run our code we will see that it says returning fake data, because our environment variable up here we defined as dev. If we were to change this to prod... ... and run it again, we see that the real version of the fetch data function is being called here. And that's really all there is to it. Now obviously in a real application we'd probably be using asynchronous IO in functions like this, but for illustration purposes this example gets the point across. There are many other applications of this sort of function assignment as well. For example if you want to do stuff like A/B Testing different implementations of a function, but I'll leave that to you to experiment with.

Contents