Type Hints

Python type hints were introduced in PEP 484 to bring the benefits of static typing to a dynamically typed language. Although type hints do not enforce type checking at runtime, they provide a way to specify the expected types of variables, function parameters, and return values, which can be checked by static analysis tools such as mypy. This enhances code readability, facilitates debugging, and improves the overall maintainability of the code.

Type hints in Python use annotations for function parameters, return values and variable assignments.

Python’s type hints can be used to specify a wide variety of types such as basic data types, collections, complex types and custom user-defined types. The typing module provides many built-in types to represent these various types −

Let’s see each one, one after another in detail.

Basic Data Types

In Python when using type hints to specify basic types we can simply use the name of the type as the annotation.

Example

Following is the example of using the basic data types such as integer, float, string etc −

Open Compiler

from typing import Optional

# Integer typedefcalculate_square_area(side_length:int)->int:return side_length **2# Float typedefcalculate_circle_area(radius:float)->float:return3.14* radius * radius

# String typedefgreet(name:str)->str:returnf"Hello, {name}"# Boolean typedefis_adult(age:int)->bool:return age >=18# None typedefno_return_example()->None:print("This function does not return anything")# Optional type (Union of int or None)defsafe_divide(x:int, y: Optional[int])-> Optional[float]:if y isNoneor y ==0:returnNoneelse:return x / y

# Example usageprint(calculate_square_area(5))print(calculate_circle_area(3.0))print(greet("Alice"))print(is_adult(22))                   
no_return_example()print(safe_divide(10,2))print(safe_divide(10,0))print(safe_divide(10,None))

On executing the above code we will get the following output −

25
28.259999999999998
Hello, Alice
True
This function does not return anything
5.0
None
None

Collections Types

In Python when dealing with collections such as liststuplesdictionaries, etc. in type hints we typically use the typing module to specify the collection types.

Example

Below is the example of the Collections using in type hints −

Open Compiler

from typing import List, Tuple, Dict, Set, Iterable, Generator

# List of integersdefprocess_numbers(numbers: List[int])-> List[int]:return[num *2for num in numbers]# Tuple of floatsdefcoordinates()-> Tuple[float,float]:return(3.0,4.0)# Dictionary with string keys and integer valuesdeffrequency_count(items: List[str])-> Dict[str,int]:
   freq ={}for item in items:
  freq[item]= freq.get(item,0)+1return freq
# Set of unique characters in a stringdefunique_characters(word:str)-> Set[str]:returnset(word)# Iterable of integersdefprint_items(items: Iterable[int])->None:for item in items:print(item)# Generator yielding squares of integers up to ndefsquares(n:int)-> Generator[int,None,None]:for i inrange(n):yield i * i # Example usage numbers =[1,2,3,4,5]print(process_numbers(numbers))print(coordinates()) items =["apple","banana","apple","orange"]print(frequency_count(items)) word ="hello"print(unique_characters(word)) print_items(range(5)) gen = squares(5)print(list(gen))

On executing the above code we will get the following output −

[2, 4, 6, 8, 10]
(3.0, 4.0)
{'apple': 2, 'banana': 1, 'orange': 1}
{'l', 'e', 'h', 'o'}
0
1
2
3
4
[0, 1, 4, 9, 16]

Optional Types

In Python, Optional types are used to indicate that a variable can either be of a specified type or None. This is particularly useful when a function may not always return a value or when a parameter can accept a value or be left unspecified.

Example

Here is the example of using the optional types in type hints −

Open Compiler

from typing import Optional

defdivide(a:float, b:float)-> Optional[float]:if b ==0:returnNoneelse:return a / b

result1: Optional[float]= divide(10.0,2.0)# result1 will be 5.0
result2: Optional[float]= divide(10.0,0.0)# result2 will be Noneprint(result1)print(result2)

On executing the above code we will get the following output −

5.0
None

Union Types

Python uses Union types to allow a variable to accept values of different types. This is useful when a function or data structure can work with various types of inputs or produce different types of outputs.

Example

Below is the example of this −

Open Compiler

from typing import Union

defsquare_root_or_none(number: Union[int,float])-> Union[float,None]:if number >=0:return number **0.5else:returnNone

result1: Union[float,None]= square_root_or_none(50)   
result2: Union[float,None]= square_root_or_none(-50)print(result1)print(result2)

On executing the above code we will get the following output −

7.0710678118654755
None

Any Type

In Python, Any type is a special type hint that indicates that a variable can be of any type. It essentially disables type checking for that particular variable or expression. This can be useful in situations where the type of a value is not known beforehand or when dealing with dynamic data.

Example

Following is the example of using Any type in Type hint −

Open Compiler

from typing import Any

defprint_value(value: Any)->None:print(value)

print_value(10)         
print_value("hello")    
print_value(True)       
print_value([1,2,3])  
print_value({'key':'value'})

On executing the above code we will get the following output −

10
hello
True
[1, 2, 3]
{'key': 'value'}

Type Aliases

Type aliases in Python are used to give alternative names to existing types. They can make code easier to read by giving clear names to complicated type annotations or combinations of types. This is especially helpful when working with nested structures or long-type hints.

Example

Below is the example of using the Type Aliases in the Type hints −

Open Compiler

from typing import List, Tuple

# Define a type alias for a list of integers
Vector = List[int]# Define a type alias for a tuple of coordinates
Coordinates = Tuple[float,float]# Function using the type aliasesdefscale_vector(vector: Vector, factor:float)-> Vector:return[int(num * factor)for num in vector]defcalculate_distance(coord1: Coordinates, coord2: Coordinates)->float:
   x1, y1 = coord1
   x2, y2 = coord2
   return((x2 - x1)**2+(y2 - y1)**2)**0.5# Using the type aliases
v: Vector =[1,2,3,4]
scaled_v: Vector = scale_vector(v,2.5)print(scaled_v)  

c1: Coordinates =(3.0,4.0)
c2: Coordinates =(6.0,8.0)
distance:float= calculate_distance(c1, c2)print(distance)

On executing the above code we will get the following output −

[2, 5, 7, 10]
5.0

Generic Types

Generic types create functions, classes or data structures that can handle any type while maintaining type safety. The typing module’s TypeVar and Generic constructs make this possible. They are helpful for making reusable components that can work with various types without compromising type checking.

Example

Here is the example of it −

Open Compiler

from typing import TypeVar, List

# Define a type variable T
T = TypeVar('T')# Generic function that returns the first element of a listdeffirst_element(items: List[T])-> T:return items[0]# Example usage
int_list =[1,2,3,4,5]
str_list =["apple","banana","cherry"]

first_int = first_element(int_list)# first_int will be of type int
first_str = first_element(str_list)# first_str will be of type strprint(first_int)print(first_str)

On executing the above code we will get the following output −

1
apple

Callable Types

Python’s Callable type is utilized to show that a type is a function or a callable object. It is found in the typing module and lets you define the types of the arguments and the return type of a function. This is handy for higher-order functions.

Example

Following is the example of using Callable type in type hint −

Open Compiler

from typing import Callable

# Define a function that takes another function as an argumentdefapply_operation(x:int, y:int, operation: Callable[[int,int],int])->int:return operation(x, y)# Example functions to be passed as argumentsdefadd(a:int, b:int)->int:return a + b

defmultiply(a:int, b:int)->int:return a * b

# Using the apply_operation function with different operations
result1 = apply_operation(5,3, add)# result1 will be 8
result2 = apply_operation(5,3, multiply)# result2 will be 15print(result1)print(result2)

On executing the above code we will get the following output −

8
15

Literal Types

The Literal type is used to specify that a value must be exactly one of a set of predefined values.

Example

Below is the example −

from typing import Literal

defmove(direction: Literal["left","right","up","down"])->None:print(f"Moving {direction}")

move("left")# Valid
move("up")# Valid                                     

On executing the above code we will get the following output −

Moving left
Moving up

NewType

NewType is a function in the typing module that allows us to create distinct types derived from existing ones. This can be useful for adding type safety to our code by distinguishing between different uses of the same underlying type. For example we might want to differentiate between user IDs and product IDs even though both are represented as integers.

Example

Below is the example −

from typing import NewType

# Create new types
UserId = NewType('UserId',int)
ProductId = NewType('ProductId',int)# Define functions that use the new typesdefget_user_name(user_id: UserId)->str:returnf"User with ID {user_id}"defget_product_name(product_id: ProductId)->str:returnf"Product with ID {product_id}"# Example usage
user_id = UserId(42)
product_id = ProductId(101)print(get_user_name(user_id))# Output: User with ID 42print(get_product_name(product_id))# Output: Product with ID 101                                   

On executing the above code we will get the following output −

User with ID 42
Product with ID 101

Comments

Leave a Reply

Your email address will not be published. Required fields are marked *