24
loading...
This website collects cookies to deliver better user experience
def double(x):
return x + x
x
will take.def double(x: int) -> int:
return x + x
x: int
part means that the function expects an int
object to be passed to the function; and the -> int
means that the function will return an int
object.mypy
or pyright
can now check that you're not doing something shady, like here:def double(x: int) -> int:
return x + "FizzBuzz" # error!
mypy
— one of the tools that does type checking — by following the instructions here;answer: int = 42
int
.def double(x: int) -> int:
return x + x
def print(*args: Any, end: str = " ") -> None:
...
class Point:
x: int
y: int
int
, str
, float
, range
, list
etc. — are valid type hints.typing
module provides some more sophisticated type hints. print
function.Any
denotes the "wildcard type". It can be treated as any type, and any value can be treated as Any.
from typing import Any
def print_twice(something: Any) -> None:
print(something, something, "!")
-> None
here — Pyright [and mypy
if you set the appropriate option] will infer it for you)Any
, though — there are often better ways.# str OR int --V
def print_thing(thing: ???) -> None:
if isinstance(thing, str):
print("string", thing)
else:
print("number", thing)
typing.Union
.from typing import Union
def print_thing(thing: Union[str, int]) -> None:
if isinstance(thing, str):
print("string", thing)
else:
print("number", thing)
def print_thing(thing: Union[str, int]) -> None:
if isinstance(thing, str):
print("string", thing + "!")
else:
print("number", thing + 10)
if
clause thing
has type str
, and in the second clause it has type int
.Optional[YourType]
is just a shorthand for Union[YourType, None]
. Union
with None
is very commonly used to indicate a potentially missing result. For example, when you call .get(some_key)
on a dictionary, you get either an item or None
. None
.from typing import Optional
_cache = {"питон": "python"}
def translate(word: str) -> Optional[str]:
from_cache = _cache.get(word)
if from_cache is not None:
return from_cache
fetched = fetch_translation(word, "ru", "en")
if fetched is None:
return None
_cache[word] = fetched
return fetched
word: str
from_cache: Optional[str] (or Union[str, None])
if
it's str
, because of narrowing) fetched: Optional[str] (or Union[str, None])
if
it's str
, because of narrowing)_cache: dict[str, str]
(see next section))def zip_add(list1: list, list2: list) -> list:
if len(list1) != len(list2):
raise ValueError("Expected lists of the same length")
return [a + b for a, b in zip(list1, list2)]
zip_add
an integer and a string. But it will happily allow you to zip_add
a list of integers and a list of strings. There must be a better way!typing.List
comes in — it allows you to say what elements the list must contain.from typing import List
def zip_add(list1: List[int], list2: List[int]) -> List[int]:
if len(list1) != len(list2):
raise ValueError("Expected lists of the same length")
return [a + b for a, b in zip(list1, list2)]
List[int]
stands for a list
object, in which all elements are int
objects. For example: [1, 2, 3]
, [1 + 2]
and []
.zip_add([1, 2, 3], [4, 5, 6])
will satisfy the type checker, but zip_add(["foo", "bar"], [1, 0])
will not.dict
has a similar typing counterpart — Dict
. It's different in that it's parametrized by two types, not one: the key type and the value type:def print_salaries(employees: Dict[str, int]) -> None:
for name, salary in employee.items():
print(f"{name} has salary of ${salary}")
print_salaries
accepts a dictionary where all keys are strings, and all values are integers. An example would be {"alice": 420, "bob": 420}
.Set
, Frozenset
, Deque
, OrderedDict
, DefaultDict
, Counter
, ChainMap
:from typing import Set
def extract_bits(numbers: Set[int]) -> Set[int]:
return number & {0, 1}
list
, dict
, tuple
, type
, set
, frozenset
, ... allow being subscripted. So our first example would look like this:def zip_add(list1: list[int], list2: list[int]) -> list[int]:
if len(list1) != len(list2):
raise ValueError("Expected lists of the same length")
return [a + b for a, b in zip(list1, list2)]
typing
required — just index into the list
class!('alice', 420)
or (1, 2)
[meaning a point on a 2D grid])(1, 2, 3, 4, 5)
)typing.Tuple
(or, if you're on 3.9 or above, just use tuple
), and simply pass the expected types in order (there can be as many as you want.def print_salary_entry(entry: Tuple[str, int]) -> None:
name, salary = entry
print(f"Salary of {name}: {salary}")
print_salary_entry
accepts a tuple of length 2 where the first element is a string and the second element is an integer.def print_salary_entry(entry: Tuple[str, Optional[int]]) -> None:
name, salary = entry
if salary is None:
print(f"{name} is a volunteer")
else:
print(f"Salary of {name}: {salary}")
print_salary_entry
accepts a tuple of length 2 where the first element is a string and the second element is an integer or None
.*args
). In this case, the tuple value is annotated as Tuple[YourType, ...]
. ...
is an ellipsis — a very niche object that found its use here. Example:from typing import Tuple
def sum_numbers(numbers: Tuple[int, ...]) -> int:
total = 0
for number in numbers:
total += number
return total
tuple
instead of typing.Tuple
.TypeVar
s explained