0 - Pre-reqs#

Python Types#

def get_full_name(first_name, last_name):
    full_name = first_name.title() + " " + last_name.title()
    return full_name

print(get_full_name("john", "doe"))
John Doe
def get_full_name(first_name: str, last_name: str):
    full_name = first_name.title() + " " + last_name.title()
    return full_name


print(get_full_name("john", "doe"))
John Doe
def process_items(items: list[str]):
    for item in items:
        print(item)

process_items(['a', 'b', 'c'])
a
b
c
def process_items(prices: dict[str, float]):
    for item_name, item_price in prices.items():
        print(item_name)
        print(item_price)

process_items({
    'Banana': 1.23,
    'Bread': 2.23
})
Banana
1.23
Bread
2.23

Union#

Union from typing allows you to use one of a list of types.

from typing import Union

def process_item(item: Union[int, str]):
    print(item)

process_item('Hello')
process_item(42)
Hello
42

As of Python 3.10, you can also do the following:

def process_item(item: int | str):
    print(item)

process_item('Hello')
process_item(42)
Hello
42

Optional#

Optional from typing allows the type to also be None.

from typing import Optional

def say_hi(name: Optional[str] = None):
    if name is not None:
        print(f"Hey {name}!")
    else:
        print("Hello World")

say_hi('Michael')
say_hi()
Hey Michael!
Hello World

As of Python 3.10, you can also do the following:

def say_hi(name: str | None = None):
    if name is not None:
        print(f"Hey {name}!")
    else:
        print("Hello World")

say_hi('Michael')
say_hi()
Hey Michael!
Hello World

Pydantic#

pydantic is a Python library to perform data validation.

from datetime import datetime
from pydantic import BaseModel

class User(BaseModel):
    id: int
    name: str = "John Doe"
    signup_ts: datetime | None = None
    friends: list[int] = []

external_data = {
    "id": "123",
    "signup_ts": "2017-06-01 12:22",
    "friends": [1, "2", b"3"],
}

# Note that ** unpacks the dictionary into a list of keyword arguments
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
id=123 name='John Doe' signup_ts=datetime.datetime(2017, 6, 1, 12, 22) friends=[1, 2, 3]
123

Annotated#

This allows you to include metadata in your type hints.

from typing import Annotated

def say_hello(name: Annotated[str, "this is just metadata"]) -> str:
    return f"Hello {name}"

say_hello('Michael')
'Hello Michael'

Concurrency and async / await#

if you are using third party libraries that tell you to call them with await, then declare your path operation function with async def:

@app.get('/')
async def read_results():
    results = await some_library()
    return results

Note that you can only use await inside functions created with async def. async basically gives Python permission to stop a function and go do something else.

Note that async functions can only be run inside of other async functions. FastAPI handles this, so there isn’t too much to worry about when using it.