You can use the function property __annotations__
:
def my_func(a: Union[int, None], b: int, c: str):
print(a,b,c)
print(my_func.__annotations__) # {'a': typing.Union[int, NoneType], 'b': <class 'int'>, 'c': <class 'str'>}
Now we can do something to check it programatically:
from typing
import _GenericAlias
def check_if_func_accepts_none(func):
for key in func.__annotations__:
if isinstance(func.__annotations__[key], type(None)):
return True
elif isinstance(func.__annotations__[key], _GenericAlias) and type(None) in func.__annotations__[key].__args__:
return True
return False
Examples:
>>> def b(a: int, b: None):
...print('hi')
...
>>>
def c(x: Union[None, str], y: int):
...print('hi')
...
>>>
def d(z: int, s: str):
...print('hi')
...
>>>
check_if_func_accepts_none(b)
True
>>>
check_if_func_accepts_none(c)
True
>>>
check_if_func_accepts_none(d)
False
>>>
A special kind of type is Any. A static type checker will treat every type as being compatible with Any and Any as being compatible with every type.,A type alias is defined by assigning the type to the alias. In this example, Vector and list[float] will be treated as interchangeable synonyms:,Use object to indicate that a value could be any type in a typesafe manner. Use Any to indicate that a value is dynamically typed.,Deprecated since version 3.8, will be removed in version 3.12: The typing.re namespace is deprecated and will be removed. These types should be directly imported from typing instead.
def greeting(name: str) - > str:
return 'Hello ' + name
Vector = list[float] def scale(scalar: float, vector: Vector) - > Vector: return [scalar * num for num in vector ] # typechecks; a list of floats qualifies as a Vector. new_vector = scale(2.0, [1.0, -4.2, 5.4])
from collections.abc import Sequence ConnectionOptions = dict[str, str] Address = tuple[str, int] Server = tuple[Address, ConnectionOptions] def broadcast_message(message: str, servers: Sequence[Server]) - > None: ... # The static type checker will treat the previous type signature as # being exactly equivalent to this one. def broadcast_message( message: str, servers: Sequence[tuple[tuple[str, int], dict[str, str]]]) - > None: ...
from typing
import NewType
UserId = NewType('UserId', int)
some_id = UserId(524313)
def get_user_name(user_id: UserId) - > str: ... # typechecks user_a = get_user_name(UserId(42351)) # does not typecheck; an int is not a UserId user_b = get_user_name(-1)
# 'output' is of type 'int', not 'UserId' output = UserId(23413) + UserId(54341)
Last Updated : 09 Aug, 2022
Output:
A U B: {
2,
4,
5,
6,
7,
8
}
Output:
set1 U set2: {
2,
4,
5,
6,
7,
8
}
set1 U set2 U set3: {
2,
4,
5,
6,
7,
8,
9,
10
}
The query parameter q is of type Union[str, None] (or str | None in Python 3.10), that means that it's of type str but could also be None, and indeed, the default value is None, so FastAPI will know it's not required.,You can declare that a parameter can accept None, but that it's still required. This would force clients to send a value, even if the value is None.,The Union[str, None] part allows your editor to provide better support, but it is not what tells FastAPI that this parameter is not required.,you would receive the multiple q query parameters' values (foo and bar) in a Python list inside your path operation function, in the function parameter q.
from typing
import Union
from fastapi
import FastAPI
app = FastAPI()
@app.get("/items/")
async def read_items(q: Union[str, None] = None):
results = {
"items": [{
"item_id": "Foo"
}, {
"item_id": "Bar"
}]
}
if q:
results.update({
"q": q
})
return results
from fastapi
import FastAPI
app = FastAPI()
@app.get("/items/")
async def read_items(q: str | None = None):
results = {
"items": [{
"item_id": "Foo"
}, {
"item_id": "Bar"
}]
}
if q:
results.update({
"q": q
})
return results
from typing
import Union
from fastapi
import FastAPI, Query
app = FastAPI()
@app.get("/items/")
async def read_items(q: Union[str, None] = Query(
default = None, max_length = 50)):
results = {
"items": [{
"item_id": "Foo"
}, {
"item_id": "Bar"
}]
}
if q:
results.update({
"q": q
})
return results
from fastapi
import FastAPI, Query
app = FastAPI()
@app.get("/items/")
async def read_items(q: str | None = Query(
default = None, max_length = 50)):
results = {
"items": [{
"item_id": "Foo"
}, {
"item_id": "Bar"
}]
}
if q:
results.update({
"q": q
})
return results
q: Union[str, None] = Query(
default = None)
q: Union[str, None] = None
January 3, 2022 19 min read 5429
When declaring a variable in statically-typed languages like C and Java, you are mandated to declare the data type of the variable. As a result, you cannot assign a value that does not conform to the data type you specified for the variable. For example, if you declare a variable to be an integer, you can’t assign a string value to it at any point in time.
int x = 4;
x = "hello"; // this would trigger a type error
In Python, you can define a variable with a type hint using the following syntax:
variable_name: type = value
Let’s look at the following variable:
name = "rocket”
In Python, you can read the type hints defined on variables using the __annotations__
dictionary:
>>> name: str = "rocket"
>>> __annotations__
{'name': <class 'str'>}
As mentioned earlier, the Python interpreter does not enforce types, so defining a variable with a wrong type won’t trigger an error:
>>> name: int = "rocket" >>>
The function containing the error is not annotated. Functions that do not have any annotations (neither for any argument nor for the return type) are not type-checked, and even the most blatant type errors (e.g. 2 + 'a') pass silently. The solution is to add annotations. Where that isn’t possible, functions without annotations can be checked using --check-untyped-defs.,The main difference is that the target of an alias is precisely known statically, and this means that they can be used in type annotations and other type contexts. Type aliases can’t be defined conditionally (unless using supported Python version and platform checks):,__init__ method has no annotated arguments or return type annotation. __init__ is considered fully-annotated if at least one argument is annotated, while mypy will infer the return type as None. The implication is that, for a __init__ method that has no argument, you’ll have to explicitly annotate the return type as None to type-check this __init__ method:,Idiomatic use of type annotations can sometimes run up against what a given version of Python considers legal code. These can result in some of the following errors when trying to run your code:
def foo(a):
return '(' + a.split() + ')'
# No error!
def foo(a: str) - > str:
return '(' + a.split() + ')'
# error: Unsupported operand types
for +("str"
and List[str])
def foo(a) - > str:
return '(' + a.split() + ')'
# No error!
def foo(s: str) - > str:
return s
class A():
def __init__(self, value: str): # Return type inferred as None, considered as typed method
self.value = value
foo(1) # error: Argument 1 to "foo"
has incompatible type "int";
expected "str"
class B():
def __init__(self): # No argument is annotated, considered as untyped method
foo(1) # No error!
class C():
def __init__(self) - > None: # Must specify
return type to type - check
foo(1) # error: Argument 1 to "foo"
has incompatible type "int";
expected "str"
def foo() - > str: return None # No error!
import frobnicate # Error: No module "frobnicate"
frobnicate.start()