Before addressing the topic “The Data Classes in Python” it is necessary to make a small introduction on what are the Type Annotations.
TYPE ANNOTATIONS
Annotations are Python features that suggest to developers the data types of function variables or parameters and the return type. There are mainly two types of annotations in Python: function annotations and variable annotations. The syntax for the function annotation is shown below:
def func(a: <expression>, b: <expression>) -> <expression>:
pass
Examples of function and variable annotations are shown in the code below. It is important to underline that even the classes as reported in the code can make use of annotations, this aspect is fundamental for the Data Classes.
from typing import List #Let's look at a couple of examples. The following three examples # take two parameters and return their sum. # As you can see, <expression> can be anything. #All three are valid function definitions but it shows #different ways to use function annotations. #Note that if there are parameters with default values, #notations must always precede the default value #of a parameter as you saw in the third example below. def func1(num1:str = "1st param", num2:str = "2nd param") -> str: return num1 + num2 def func2(num1: int, num2: int) -> int: return num1 + num2 def func3(num1: int, num2: int=10) -> int : return num1 + num2 print(func1('pippo','pluto')) print(func2(20,30)) print(func3(80)) print(func2.__annotations__) #Annotazioni di variabili name: str city: str = 'Mysor' age: int = 35 # In the following example, the square function expects # an integer parameter num and returns all squares # the numbers from 0 to num. The squares variable is declared as # List [int] to indicate that it contains a list of integers. # Similarly, the return type of the function is also List [int]. # Next, square .__ annotations__ provides # local annotations to the function and __annotations__ provides # global annotations. squares: List[int] = [] def square(num: int) -> List[int]: for i in range(num): squares.append(i**2) return squares print(square(10)) print(square.__annotations__) print(__annotations__) #Class can also use data Annotations class MyClass: nome: str cognome:str def __init__(self,nome,cognome): self.nome=nome self.cognome=cognome def denominazione(self): print(f'{self.nome} {self.cognome}') m=MyClass(nome='Mario',cognome='Rossi') print(m.denominazione()) print(m.nome) print(m.cognome)
ANNOTATIONS OF VARIABLES
For variable annotations we will look at those introduced in PEP 526 in Python 3.6. The simplest example of variable annotation is shown below. Some of the rules for writing variable annotations are:
- There should be no space before the colon.
- There should be a space after the colon.
myVar: int=10
Function annotations can be accessed by using the __annotations__ attribute on the function object. The __annotations__ gives results in the form of a dictionary in which keys and values are mapped to parameters and annotations respectively.
WHY USE THE ANNOTATIONS
- Type checking: Third party tools like mypy can be used to detect type errors before runtime.
- Type hints for IDEs: If the annotation was used to indicate the data type of the parameters, the IDEs suggest the data type of the parameter, etc. This will help detect type errors as you write code in the IDE like Pycharm.
- Autocomplete: Annotations also help in automatic code completion and make life easier.
- Readability: Annotations also make the code more readable.
THE DATA CLASSES
The data classes are an addition that has been made to the standard Python library that serves to enrich the meaning of the classes themselves mainly when they contain the definition of data. The principle of data classes revolves around a decorator called dataclass. This decorator automatically adds a number of methods. The first of these is __init__ in the form shown in the figure:
The decorator then implements __repr__, __eq__, __ne__. __repr__serves to represent the content of an instance through its data. Equal (__eq__) and Not Equal (__ne__) are implemented to suit the specific class. In particular, it compares all the fields in sequence and establishes whether the instances of a class are equal or not.
PARAMETERS OF THE DECORATOR
The decorator admits some parameters, the most important are illustrated in the figure.
- init: by default it is True and allows you to generate the __init__ initializer. If we set it to false we can no longer generate instances of the class as shown in the figure.
- repr: by default this value is True so the method is generated. Allows you to have a clearer view of the instance fields. If you set it to False you get a coarser display when doing the print(mc).
- order: allows you to generate a series of methods that represent the redefinition of standard operators such as <, <=,>,> =. By default its value is set to False, so these methods are not generated. Comparisons if set to True are made based on the current values of the instances fields.
- frozen: by default the value of this parameter is False, this means that we can modify the data values in the class instances. If set to True it is no longer possible to change these values.
from dataclasses import dataclass #Starting from annotations of type Python is able to infer #the presence of Field object types that represent the fields of the class @dataclass(init=True,repr=True,order=True,frozen=False) class MyClass(object): nome: str cognome: str mc1=MyClass(nome='Luigi',cognome='Rossi') # The repr method doing a print(mc) gives us a more compact print. print(mc1) mc2=MyClass(nome='Luigi',cognome='Rossi') print(mc2) print(mc1<mc2) print(mc1>mc2) print(mc1==mc2)
DEEPENING
Type Annotations in Python are a feature first introduced in version 3.5 that allows type information to be added to function parameters and return values. Although Python is a dynamic language, i.e., it does not require explicit declaration of types, type annotations provide greater code readability and can be used by static type checking tools, such as mypy, to detect errors.
Basic Syntax
The syntax of type annotations in Python is simple and intuitive. Here is an example of how to annotate types for a function:
def sum(a: int, b: int) -> int:
return a + b
In this example:
– a: int indicates that parameter a should be an integer (int).
– b: int indicates that parameter b should be an integer (int).
-> int indicates that the function should return an integer (int).
Annotations for Other Types
Python supports annotation of a wide range of types, including complex types such as lists, dictionaries, tuples, etc., using the typing module. The following are some common examples:
– Lists: List[int] to denote a list of integers.
– Dictionaries: Dict[str, int] to denote a dictionary with keys of type string and values of type integer.
– Tuples: Tuple[int, str] to denote a tuple that contains an integer and a string.
– Optional: Optional[int] to indicate that a value can be an integer or None.
Example usage:
from typing import List, Dict, Tuple, Optional
def process_data(data: List[int], settings: Dict[str, str]) -> Tuple[int, Optional[str]]:
# Implementazione della funzione
pass
Complex and Custom Types
Custom types can be defined using classes or NamedTuples:
from typing import NamedTuple
class Punto(NamedTuple):
x: int
y: int
def distanza(p1: Punto, p2: Punto) -> float:
return ((p1.x – p2.x) ** 2 + (p1.y – p2.y) ** 2) ** 0.5
Annotations in Dictionaries and Variables.
Type annotations are not limited to functions. Variables and dictionary fields can also be annotated:
from typing import Dict
eta: Dict[str, int] = {‘Alice‘: 30, ‘Bob‘: 25}
Static Type Verification.
Type annotations are purely informative and are not applied at runtime. However, tools such as mypy can be used to verify compliance with annotations and detect errors before execution.
Full Example
from typing import List, Dict, Tuple
def analizza_voti(voti: List[int]) -> Tuple[float, Dict[str, int]]:
media = sum(voti) / len(voti)
conteggio = {str(voto): voti.count(voto) for voto in set(voti)}
return media, conteggio
voti_studenti = [8, 7, 10, 6, 8, 9, 10, 7]
media_voti, distribuzione = analizza_voti(voti_studenti)
In this example:
-votes: List[int] indicates that the votes parameter is a list of integers.
–> Tuple[float, Dict[str, int]] indicates that the function returns a tuple containing a float and a dictionary.
Conclusion
Type annotations in Python offer a powerful tool to improve code readability and help with static type checking. Although they are not mandatory, their use is highly recommended in medium to large projects where code clarity and consistency are crucial.
DATA CLASSES IN PYTHON
In Python, data classes (or “data classes”) are a feature introduced in version 3.7 to simplify the creation of classes that are primarily used to store data. Data classes automate the generation of common methods such as __init__, __repr__, __eq__, reducing boilerplate code.
Description of the main features of data classes:
1. @dataclass decorator:
Data classes are defined using the @dataclass decorator, which is applied on top of a class. This decorator automates the creation of special methods.
2. Data class fields:
Within a data class, every attribute defined is automatically considered a field. Fields can have default values and can be typed using type annotations.
3. Automatic method generation:
– __init__: Automatically generates a constructor method that initializes the fields of the class.
– __repr__: Provides a readable string representation of the class, useful for debugging.
– __eq__: Implements structural equality, comparing field values.
– Other methods such as __lt__, __le__, __gt__, __ge__ can be automatically generated by adding optional parameters to the decorator.
4. Inheritance:
Data classes support inheritance like regular classes in Python. You can extend a data class with other data classes or with regular classes.
5. Field order:
You can specify whether the data class should have an order, thus making objects comparable based on their fields.
6. Immutability:
Data classes can be made immutable using the frozen=True option in the decorator, turning them into immutable data structures similar to tuples.
Example of using a data class:
from dataclasses import dataclass
@dataclass
class Persona:
nome: str
cognome: str
età: int
p = Persona(nome=”Mario“, cognome=”Rossi“, età=30)
print(p) # Output: Persona(nome=’Mario’, cognome=’Rossi’, età=30)
In this example, the class Person is defined as a dataclass. The @dataclass decorator takes care of automatically generating the __init__, __repr__ and other special methods based on the defined fields (first name, last name, age).
Data classes are especially useful when dealing with objects that represent structured data, making the code cleaner and less error-prone.
Leave A Comment