Python Types Intro¶
Python has support for optional "type hints" (also called "type annotations").
These"type hints" or annotations are a special syntax that allow declaring thetype of a variable.
By declaring types for your variables, editors and tools can give you better support.
This is just aquick tutorial / refresher about Python type hints. It covers only the minimum necessary to use them withFastAPI... which is actually very little.
FastAPI is all based on these type hints, they give it many advantages and benefits.
But even if you never useFastAPI, you would benefit from learning a bit about them.
Note
If you are a Python expert, and you already know everything about type hints, skip to the next chapter.
Motivation¶
Let's start with a simple example:
defget_full_name(first_name,last_name):full_name=first_name.title()+" "+last_name.title()returnfull_nameprint(get_full_name("john","doe"))Calling this program outputs:
John DoeThe function does the following:
- Takes a
first_nameandlast_name. - Converts the first letter of each one to upper case with
title(). - Concatenates them with a space in the middle.
defget_full_name(first_name,last_name):full_name=first_name.title()+" "+last_name.title()returnfull_nameprint(get_full_name("john","doe"))Edit it¶
It's a very simple program.
But now imagine that you were writing it from scratch.
At some point you would have started the definition of the function, you had the parameters ready...
But then you have to call "that method that converts the first letter to upper case".
Was itupper? Was ituppercase?first_uppercase?capitalize?
Then, you try with the old programmer's friend, editor autocompletion.
You type the first parameter of the function,first_name, then a dot (.) and then hitCtrl+Space to trigger the completion.
But, sadly, you get nothing useful:

Add types¶
Let's modify a single line from the previous version.
We will change exactly this fragment, the parameters of the function, from:
first_name,last_nameto:
first_name:str,last_name:strThat's it.
Those are the "type hints":
defget_full_name(first_name:str,last_name:str):full_name=first_name.title()+" "+last_name.title()returnfull_nameprint(get_full_name("john","doe"))That is not the same as declaring default values like would be with:
first_name="john",last_name="doe"It's a different thing.
We are using colons (:), not equals (=).
And adding type hints normally doesn't change what happens from what would happen without them.
But now, imagine you are again in the middle of creating that function, but with type hints.
At the same point, you try to trigger the autocomplete withCtrl+Space and you see:

With that, you can scroll, seeing the options, until you find the one that "rings a bell":

More motivation¶
Check this function, it already has type hints:
defget_name_with_age(name:str,age:int):name_with_age=name+" is this old: "+agereturnname_with_ageBecause the editor knows the types of the variables, you don't only get completion, you also get error checks:

Now you know that you have to fix it, convertage to a string withstr(age):
defget_name_with_age(name:str,age:int):name_with_age=name+" is this old: "+str(age)returnname_with_ageDeclaring types¶
You just saw the main place to declare type hints. As function parameters.
This is also the main place you would use them withFastAPI.
Simple types¶
You can declare all the standard Python types, not onlystr.
You can use, for example:
intfloatboolbytes
defget_items(item_a:str,item_b:int,item_c:float,item_d:bool,item_e:bytes):returnitem_a,item_b,item_c,item_d,item_e🤓 Other versions and variants
defget_items(item_a:str,item_b:int,item_c:float,item_d:bool,item_e:bytes):returnitem_a,item_b,item_c,item_d,item_etyping module¶
For some additional use cases, you might need to import some things from the standard librarytyping module, for example when you want to declare that something has "any type", you can useAny fromtyping:
fromtypingimportAnydefsome_function(data:Any):print(data)Generic types¶
Some types can take "type parameters" in square brackets, to define their internal types, for example a "list of strings" would be declaredlist[str].
These types that can take type parameters are calledGeneric types orGenerics.
You can use the same builtin types as generics (with square brackets and types inside):
listtuplesetdict
List¶
For example, let's define a variable to be alist ofstr.
Declare the variable, with the same colon (:) syntax.
As the type, putlist.
As the list is a type that contains some internal types, you put them in square brackets:
defprocess_items(items:list[str]):foriteminitems:print(item)Info
Those internal types in the square brackets are called "type parameters".
In this case,str is the type parameter passed tolist.
That means: "the variableitems is alist, and each of the items in this list is astr".
By doing that, your editor can provide support even while processing items from the list:

Without types, that's almost impossible to achieve.
Notice that the variableitem is one of the elements in the listitems.
And still, the editor knows it is astr, and provides support for that.
Tuple and Set¶
You would do the same to declaretuples andsets:
defprocess_items(items_t:tuple[int,int,str],items_s:set[bytes]):returnitems_t,items_sThis means:
- The variable
items_tis atuplewith 3 items, anint, anotherint, and astr. - The variable
items_sis aset, and each of its items is of typebytes.
Dict¶
To define adict, you pass 2 type parameters, separated by commas.
The first type parameter is for the keys of thedict.
The second type parameter is for the values of thedict:
defprocess_items(prices:dict[str,float]):foritem_name,item_priceinprices.items():print(item_name)print(item_price)This means:
- The variable
pricesis adict:- The keys of this
dictare of typestr(let's say, the name of each item). - The values of this
dictare of typefloat(let's say, the price of each item).
- The keys of this
Union¶
You can declare that a variable can be any ofseveral types, for example, anint or astr.
To define it you use thevertical bar (|) to separate both types.
This is called a "union", because the variable can be anything in the union of those two sets of types.
defprocess_item(item:int|str):print(item)This means thatitem could be anint or astr.
PossiblyNone¶
You can declare that a value could have a type, likestr, but that it could also beNone.
defsay_hi(name:str|None=None):ifnameisnotNone:print(f"Hey{name}!")else:print("Hello World")Usingstr | None instead of juststr will let the editor help you detect errors where you could be assuming that a value is always astr, when it could actually beNone too.
Classes as types¶
You can also declare a class as the type of a variable.
Let's say you have a classPerson, with a name:
classPerson:def__init__(self,name:str):self.name=namedefget_person_name(one_person:Person):returnone_person.nameThen you can declare a variable to be of typePerson:
classPerson:def__init__(self,name:str):self.name=namedefget_person_name(one_person:Person):returnone_person.nameAnd then, again, you get all the editor support:

Notice that this means "one_person is aninstance of the classPerson".
It doesn't mean "one_person is theclass calledPerson".
Pydantic models¶
Pydantic is a Python library to perform data validation.
You declare the "shape" of the data as classes with attributes.
And each attribute has a type.
Then you create an instance of that class with some values and it will validate the values, convert them to the appropriate type (if that's the case) and give you an object with all the data.
And you get all the editor support with that resulting object.
An example from the official Pydantic docs:
fromdatetimeimportdatetimefrompydanticimportBaseModelclassUser(BaseModel):id:intname:str="John Doe"signup_ts:datetime|None=Nonefriends:list[int]=[]external_data={"id":"123","signup_ts":"2017-06-01 12:22","friends":[1,"2",b"3"],}user=User(**external_data)print(user)# > User id=123 name='John Doe' signup_ts=datetime.datetime(2017, 6, 1, 12, 22) friends=[1, 2, 3]print(user.id)# > 123Info
To learn more aboutPydantic, check its docs.
FastAPI is all based on Pydantic.
You will see a lot more of all this in practice in theTutorial - User Guide.
Type Hints with Metadata Annotations¶
Python also has a feature that allows puttingadditionalmetadata in these type hints usingAnnotated.
You can importAnnotated fromtyping.
fromtypingimportAnnotateddefsay_hello(name:Annotated[str,"this is just metadata"])->str:returnf"Hello{name}"Python itself doesn't do anything with thisAnnotated. And for editors and other tools, the type is stillstr.
But you can use this space inAnnotated to provideFastAPI with additional metadata about how you want your application to behave.
The important thing to remember is thatthe firsttype parameter you pass toAnnotated is theactual type. The rest, is just metadata for other tools.
For now, you just need to know thatAnnotated exists, and that it's standard Python. 😎
Later you will see howpowerful it can be.
Tip
The fact that this isstandard Python means that you will still get thebest possible developer experience in your editor, with the tools you use to analyze and refactor your code, etc. ✨
And also that your code will be very compatible with many other Python tools and libraries. 🚀
Type hints inFastAPI¶
FastAPI takes advantage of these type hints to do several things.
WithFastAPI you declare parameters with type hints and you get:
- Editor support.
- Type checks.
...andFastAPI uses the same declarations to:
- Define requirements: from request path parameters, query parameters, headers, bodies, dependencies, etc.
- Convert data: from the request to the required type.
- Validate data: coming from each request:
- Generatingautomatic errors returned to the client when the data is invalid.
- Document the API using OpenAPI:
- which is then used by the automatic interactive documentation user interfaces.
This might all sound abstract. Don't worry. You'll see all this in action in theTutorial - User Guide.
The important thing is that by using standard Python types, in a single place (instead of adding more classes, decorators, etc),FastAPI will do a lot of the work for you.
Info
If you already went through all the tutorial and came back to see more about types, a good resource isthe "cheat sheet" frommypy.







