Movatterモバイル変換


[0]ホーム

URL:


Skip to content
Join theFastAPI Cloud waiting list 🚀
Follow@fastapi onX (Twitter) to stay updated
FollowFastAPI onLinkedIn to stay updated
Subscribe to theFastAPI and friends newsletter 🎉
sponsor
sponsor
sponsor
sponsor
sponsor
sponsor
sponsor
sponsor
sponsor
sponsor
sponsor

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 Doe

The function does the following:

  • Takes afirst_name andlast_name.
  • Converts the first letter of each one to upper case withtitle().
  • 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_name

to:

first_name:str,last_name:str

That'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_age

Because 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_age

Declaring 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:

  • int
  • float
  • bool
  • bytes
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_e

typing 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):

  • list
  • tuple
  • set
  • dict

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_s

This means:

  • The variableitems_t is atuple with 3 items, anint, anotherint, and astr.
  • The variableitems_s is 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 variableprices is adict:
    • The keys of thisdict are of typestr (let's say, the name of each item).
    • The values of thisdict are of typefloat (let's say, the price of each item).

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.name

Then 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.name

And 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)# > 123

Info

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.


[8]ページ先頭

©2009-2026 Movatter.jp