Back to Index

How to Use Type Annotations in Python


Chapters

0:0 Intro
0:55 Datatypes Example in C
2:53 Static and Dynamic Typed Languages
3:47 Type Annotations in Python
4:25 How to Define Simple Types
6:4 IDE Warnings
8:20 More Complex Types
9:53 dict[str, int]
11:38 Union Operator (Py 3.9)
12:34 Union Operator (Py 3.10)
13:21 Optional Operator

Transcript

Okay so in this video we're going to go through type annotations in Python. So before we actually get to type annotations in Python itself, I just want to explain a little bit of what type annotations are. So type annotations are also known as type signatures and they're used to indicate the data type of variables and the input and outputs of functions and methods in a programming language.

So in a lot of languages data types are explicitly stated, meaning that if you don't declare your data type your code won't run. So I want to show you an example of a hello world in C so that we can see what it means to explicitly state our data types in our code.

Over here I have this quick hello world script and you don't need to read or understand C to understand what is actually happening here. All I want you to focus on is that we have these two type declarations int and char. So what I'm going to do is first I'm just going to remove those.

So remove the char from hello and this also those square brackets tell us that it's an array of characters which is what char is. So I'll remove that as well and also remove the int from main. Now if I save and exit and then try to compile this you have to compile your code when it is at C which we do like this.

Okay we see that we get quite a few errors. So mainly it says hello is undeclared and the reason we're getting that issue is because we can't declare a variable without already initializing it with a data type. So it thinks that we're trying to access an existing variable. So let's go back into our script and we'll just add those two definitions back into our code.

Okay and let's try and compile that again. You see okay it works perfectly. So we can also just run that code and we get hello world. So we can see that everything is running perfectly. So I just wanted to use that example before we get into the Python to just demonstrate that in these other languages we do need to declare types and Python is more forgiving because we don't need to define types when we create a variable.

Now these two approaches to programming language and data types have a name. So with what we just saw in C where we have to explicitly define the types that's called a statically typed language. Whereas with Python where we don't have to explicitly state a data type that is called a dynamically typed language.

But I think that is more than enough on discussing generic type annotations. Let's dive into type annotations in Python. Now the first thing I want to say is that type annotations in Python are not make or break like they are in our C example. They're optional chunks of syntax that we can add to our code to make it more explicit.

So if we added type annotations and they didn't quite match up to what we wrote in our code we wouldn't receive an error. At most we'll get a warning in our ID. Okay so switching across to Python we define types like this. So this is from Python 3.9 onwards and what we do is say we have a string type here.

We would define it using this syntax you see in the middle. Okay and then just like usual we would have our string there. So if we just compare that to what we normally do when we're not defining types all we get is this. So all we're adding is this little bit here.

Now that's when we're assigning a variable and let's say that we want to do the same but for a function. So we're going to make this a very simple function where we take two integer values x and y and we're going to add those together. So what we do is return x plus y.

Okay and here what we've done is specified the actual input types to that function. Now if we just come down here and let's try this out. Let's create a new value that of course is going to be an integer because that's what we're expecting from our function up here.

And that will obviously be equal to add and here we put say four and five. Okay and everything looks good right. Now with type annotations in Python of course Python is a dynamically typed language so if we ran this and there was an error in our types unless it created an error somewhere else in the language like maybe we tried to add a string and an integer of course that would raise an error anyway but it wouldn't create an error due to type annotations.

And adding these type annotations won't make it so that we get type errors that wouldn't already be there. All it does is it allows our IDE to essentially warn us when something doesn't quite look right. So let's say here we replace that four with a string four. Okay and I'm going to save this and then we get this little warning underneath so it's highlighted and if I hover over that we see that it says argument one to add has incompatible type string expected an integer.

Okay now of course usually this would come up with an error anyway so we're not really doing anything special but say maybe we did both of these four and five. Now in Python we can add strings together so this wouldn't raise an error but using the type annotations we still don't get an error but we do get this warning so at least we're aware that there's something weird going on.

Now another thing that I just want to add to this here is that we can also specify the output type as well. So here of course we're adding two integers together we'd also expect an integer there. So if we string here we'd get another warning because we would be expecting to return a string as we specified here but this returns an integer.

So we're getting a lot of warnings now so let's put that all back to as it was before so this will remove the first warning then we do four and five. Now everything's good again. Now alongside these basic data types so we have integer, flow, dictionary, list we can also merge data types which is getting a little more interesting I think.

So with both of these objects here we would define both of them as dictionaries and so if we wanted to sum over every value within those dictionaries and we also wanted to include type annotations we could write something like this. So sum dict we have our variable which is just our dictionary so this is a dictionary type.

We could also put that we are expecting a integer out of that so we can include that in there as well and then I want to return the sum of each of the values within the dictionary values there. So we could just write that as say we're accessing the key for every key in the variable dictionary.

Now this isn't the best way to write it but this is fine and now that we've put that function together let's try processing both of our dictionaries with that function. So we have this string int dictionary now just copy that and we have int int. Okay and you know everything's fine there's no no warnings there and we wouldn't expect there to be any warnings there but maybe the dictionary types that we would be expecting would contain a string as a key and a integer as a value.

So we can actually specify that by adding a little bit more to this type here. So all we do is put in the square brackets now we have string which is the key type and integer which is the value type. Now let's save that and we see now we have this warning.

Okay so the argument to this function has an incompatible type which is dictionary integer integer whereas we expected a dictionary with a string and integer for the key value. Okay so that's pretty cool so we can also merge different types to create more complex structures and what I want to do is return now to our previous example.

So this is pretty good. So for this example do we really only want to allow integers and maybe in some cases we do but also it would be logical in this scenario to also allow floats. So to do that we can also use something called the union operator. Now I'm going to show you two versions of this I'm going to show you the Python 3.9 version and also the Python 3.10 version.

So for the Python 3.9 version we need to go from typing import the union and then here we add our union and we say integer or float and that's all it really is. So if we add both of those we can now add 4.5 5.5 and we won't return any error.

Now of course we'd also probably want it to be here as well and we could also specify that we expect it to return either of those. So that's how we would set up alternative integer or float data type annotations in Python 3.9. Now with Python 3.10 we can do this in what I think is a much cleaner way.

So first we can remove this top import so we get rid of that and instead of having union here all we do is add a bar in between our data types and that is all we need to do for it. So we do that for each of these and that will give us our Python 3.10 syntax.

Now let's just undo all of that and the final type that I want to show you or define operator is the optional operator. Now when we define a function with optional arguments we would use this optional function in order to allow us to actually do that. So this will allow us to say okay here I'm going to just make up another argument we're not going to use it but this will be optional string.

Now I've imported optional here and now we're using it here so this will allow either non or a string in our code. So immediately you can see that this means that we don't have to specify anything here so that's great and if we do specify something we can say op okay no warning and we can switch that around for the non variable and we won't see any warning.

Now as well if we didn't want to use the optional operator we could also just write this using a union and it actually produces the exact same type. So we could write string and non and this would work in exactly the same way there's no difference whatsoever. So that's everything that I wanted to cover for this introduction to type annotations in Python.

I hope it's been useful and I will see you in the next one.