Get Even More Visitors To Your Blog, Upgrade To A Business Listing >>

Advanced Python: Functions

Tags: function

Ilija LazarevicFollowTowards Data Science--ListenShareAfter reading the title, you probably ask yourself something along the lines of, “Functions in Python are an advanced concept? How? All courses introduce functions as the basic block in language.” And you are right and wrong at the same time.Most courses on Python introduce functions as a basic concept and building block because, without them, you would not be able to write functional code at all. This is totally different from the functional programming paradigm, which is a separate concept, but I shall touch upon this one too.Before we delve into the advanced intricacies of Python functions, let’s briefly go over some basic concepts and things you probably already know.So you start writing your program, and at some point you end up writing the same sequence of code. You start repeating yourself and the code blocks. This proves to be a good time and place to introduce functions. At least, that is. In Python, you define a Function as:In the world of software engineering, we make a distinction between parts of function definition:A function can return a value or have no return value at all, like the one we previously defined. When function returns value it can return one or more of them:What you get as a result is a tuple that you can unpack or pick any of the tuple elements to proceed with.For those of you who are not informed, functions in Python are first-class citizens. What does this mean? It means you can work with functions as you would with any other variable. You can pass them as arguments to other functions, return them from functions, and even store them in variables. Here is one of the examples:Wait, what was this lambda ? This is another way you can define functions in Python. This is the so-called unnamed or anonymous function. Well, in this example, we are assigning it to a variable named name_decorator but you can pass the lambda expression as an argument of another function without the need to name it. I will cover this shortly.What is left is to give an example of how functions can be passed as arguments or returned as values from another function. This is the part where we are moving toward advanced concepts, so bear with me.So this is how it looks to pass functions as arguments to another function. What about the lambda function? Well, take a look at the next example:Now the default decorating function is lambda and returns the argument's value as it is (idempotent). Here, it is anonymous because there is no name attached to it.Notice that print is also a function, and we are passing a function shout inside of it as an argument. In essence, we are chaining functions. And this can lead us to a functional programming paradigm, which is a path that you can choose in Python. I will try to write another blog post specifically on this subject because it is very interesting to me. For now, we will keep to the procedural programming paradigm; that is, we will continue with what we have been doing so far.As stated previously, a function can be assigned to a variable, passed as an argument to another function, and returned from that function. I have shown you some simple examples for the first two cases, but what about returning a function from a function? At first I wanted to keep it really simple, but then again, this is an advanced Python!This will by no means be THE guide to functions and advanced concepts around functions in Python. There are a lot of great materials, which I will leave at the end of this post. However, I want to talk about a couple of interesting aspects that I have found to be very intriguing.Functions in Python are objects. How can we figure this out? Well, each object in Python is an instance of a class that eventually inherits from one specific class called type. The details of this are convoluted, but to be able to see what this has to do with functions, here is an example:When you define a class in Python, it automatically inherits the object class. And which class does object inherit?And should I tell you that classes in Python are objects too? Indeed, this is mind-boggling for beginners. But as Andrew Ng would say, this is not that important; don’t worry about it.Okay, so functions are objects. Certainly functions should have some magic methods, then, right?The magic method __call__ is defined for objects that are callable. So our shout object (function) is callable. We can call it with or without arguments. But this is interesting. What we did previously was define a shout function and get an object that is callable with the __call__ magic method that is a function. Have you ever watched the Inception movie?So, our function is not really a function but an object. Objects are instances of classes and contain methods and attributes, right? This is something you should know from OOP. How can we find out what the attributes of our object are? There is this Python function called vars that returns a dictionary of object attributes with their values. Let's see what happens in the next example:This is interesting. Not that you could figure out the use case for this straight away. And even if you could find it, I would highly discourage you from doing this black magic. It’s just not easy to follow, even though it is an interesting flex. The reason I have shown you this is because we wanted proof that functions are indeed objects. Remember, everything in Python is an object. That is how we roll in Python.Now, long-awaited functions are returning. This concept is also very interesting since it gives you a lot of utility. With a little bit of syntactic sugar, you get very expressive. Let’s dive in.First, a function’s definition can contain another function’s definition. Even more than one. Here is a perfectly fine example:If you are thinking that this is just a convoluted version of name.upper() you are right. But wait, we are getting there.So, given the previous example, which is fully functional Python code, you can experiment with multiple functions defined inside your function. What is the value of this neat trick? Well, you could be in a situation where your function is huge with repeating blocks of code. This way, defining a subfunction would increase readability. In practice, huge functions are a sign of code smell, and it is highly encouraged to break them into a few smaller ones. So, following this advice, you will rarely have the need to define multiple functions inside each other. One thing to notice is that the _upper_case function is hidden and out of reach in the scope where the shout function ends up being defined and available to call. This way, you can't test it easily, which is yet another issue with this approach.However, there is one specific case where defining a function inside another is a way to go. This is when you implement the decorator of a function. This has nothing to do with the function we used to decorate the name string in one of previous examples.What is a decorator function? Think of it as a function that wraps your function. The goal of doing this is to introduce additional functionality to an already existing function. For example, say you want to log every time your function is called:Pay attention to how we decorate our function; we pass it as an argument to the decorating one. But this is not enough! Remember, decorator returns function, and this function needs to be invoked (called). This is what the last call does.Now, in practice, what you really want is for decoration to persist under the original function’s name. In our case, we would like that after the interpreter parses our code, my_function is the name of the decorated function. This way, we keep things simple to follow, and we are making sure that any part of our code won't be able to call an undecorated version of our function. Example:You will admit that the part where we reassign the function’s name to a decorated one is troublesome. You have to keep this in mind. If there are many function calls you want to log, there will be a lot of repeating code. Here is where the syntactic sugar comes in. After the decorator function is defined, you can use it to decorate the another function by prefixing the function definition with an @ and the name of the decorator function. Example:This is Python’s Zen. Look at the expressiveness of the code and its simplicity.One important thing to note here! Even though the output makes sense, it is not what you would expect! At the time of loading your Python code, the interpreter will call the my_logger function and effectively run it! You will get the log output, yet this will not be what we wanted in the first place. Look at the code now:To be able to run decorator code once the original function is called, we have to wrap it around another function. This is where things can get messy. Here is an example:In this example, there are some updates as well, so let’s go over them:Always keep in mind that the decorator function has to return a function that accepts the same arguments (number and their respective types) and returns the same output (again, number and their respective types). That is, if you want to make the function user not confused and the code reader not trying to figure out what the hell is happening.For example, say you have two functions that are different in results but also require arguments:In our example, the decorator function accepts just the function it decorates. But what if you wanted to pass additional parameters and dynamically change decorator behavior? Say you want to tune the logger decorator’s verbosity. So far, our decorator function has accepted one argument: the function it decorates. However, when the decorator function has its own arguments, these are passed to it first. Then, the decorator function has to return a function that accepts the decorated one. Essentially, things are getting more convoluted. Remember the movie Inception reference?Here is an example:I won’t go into describing code unrelated to decorator, but I encourage you to look it up and learn. Here we have a decorator that logs function calls with different verbosity. As already described, my_logger decorator now accepts arguments that dynamically change its behavior. After arguments are passed to it, the resulting function that it returns should accept a function to decorate. This is the _inner_logger function. By now, you should understand what the rest of the decorator code is doing.My first idea for this post was to write about advanced topics like decorators in Python. However, as you probably know by now, I have mentioned and used a lot of other advanced topics as well. In future posts, I’ll tackle some of these to a certain extent. Nevertheless, my advice for you is to go and learn about the things mentioned here from other sources as well. Grasping the functions is a must if you are developing in any programming language, but grasping all of the aspects of your programming language of choice can give you great leverage in how you write your code.I hope I have introduced something new for you and that you are now confident about writing functions as an advanced Python programmer.----Towards Data ScienceAI powered Real Estate Automated Valuation www.linkedin.com/in/ilijalazarevicIlija Lazarevic--Heiko HotzinTowards Data Science--16Giuseppe ScalamognainTowards Data Science--12Ilija Lazarevic--Nicolet JuniorinStackademic--Ernest Asena--TechClaw--NehemiabosireinPython in Plain English--Rohit Saroj--Nathan Rosidi--HelpStatusWritersBlogCareersPrivacyTermsAboutText to speechTeams



This post first appeared on VedVyas Articles, please read the originial post: here

Share the post

Advanced Python: Functions

×

Subscribe to Vedvyas Articles

Get updates delivered right to your inbox!

Thank you for your subscription

×