{"id":220,"date":"2024-01-25T16:22:31","date_gmt":"2024-01-25T16:22:31","guid":{"rendered":"https:\/\/learnpython.elegantwallp.com\/?p=220"},"modified":"2024-01-25T16:22:44","modified_gmt":"2024-01-25T16:22:44","slug":"python-dataclass","status":"publish","type":"post","link":"https:\/\/learnpython.elegantwallp.com\/2024\/01\/25\/python-dataclass\/","title":{"rendered":"Python dataclass"},"content":{"rendered":"\n<p><strong>Summary<\/strong>: in this tutorial, you\u2019ll learn about the Python dataclass decorator and how to use it effectively.<\/p>\n\n\n\n<h2 class=\"wp-block-heading\">Introduction to the Python dataclass<\/h2>\n\n\n\n<p>Python introduced the dataclass in version 3.7 (PEP 557). The dataclass allows you to define\u00a0classes\u00a0with less code and more functionality out of the box.<\/p>\n\n\n\n<p>The following defines a regular\u00a0<code>Person<\/code>\u00a0class with two instance attributes\u00a0<code>name<\/code>\u00a0and\u00a0<code>age<\/code>:<\/p>\n\n\n\n<pre class=\"wp-block-code\"><code><code>class Person: def __init__(self, name, age): self.name = name self.age = age <\/code><small>Code language: Python (python)<\/small><\/code><\/pre>\n\n\n\n<p>This&nbsp;<code>Person<\/code>&nbsp;class has the&nbsp;<code>__init__<\/code>&nbsp;method that initializes the&nbsp;<code>name<\/code>&nbsp;and&nbsp;<code>age<\/code>&nbsp;attributes.<\/p>\n\n\n\n<p>If you want to have a string representation of the\u00a0<code>Person<\/code>\u00a0object, you need to implement the\u00a0<code>__str__<\/code>\u00a0or\u00a0<code>__repr__<\/code>\u00a0method. Also, if you want to compare two instances of the\u00a0<code>Person<\/code>\u00a0class by an attribute, you need to implement the\u00a0<code>__eq__<\/code>\u00a0method.<\/p>\n\n\n\n<p>However, if you use the dataclass, you\u2019ll have all of these features (and even more) without implementing these dunder methods.<\/p>\n\n\n\n<p>To make the&nbsp;<code>Person<\/code>&nbsp;class a data class, you follow these steps:<\/p>\n\n\n\n<p>First, import the&nbsp;<code>dataclass<\/code>&nbsp;decorator from the&nbsp;<code>dataclasses<\/code>&nbsp;module:<code>from dataclasses import dataclass<\/code><small>Code language: Python (python)<\/small><\/p>\n\n\n\n<p>Second, decorate the\u00a0<code>Person<\/code>\u00a0class with the\u00a0<code>dataclass<\/code>\u00a0decorator and declare the attributes:<\/p>\n\n\n\n<pre class=\"wp-block-code\"><code><code>@dataclass class Person: name: str age: int<\/code><small>Code language: Python (python)<\/small><\/code><\/pre>\n\n\n\n<p>In this example, the\u00a0<code>Person<\/code>\u00a0class has two attributes\u00a0<code>name<\/code>\u00a0with the type\u00a0<code>str<\/code>\u00a0and\u00a0<code>age<\/code>\u00a0with the type\u00a0<code>int<\/code>. By doing this, the @dataclass decorator implicitly creates the\u00a0<code>__init__<\/code>\u00a0method like this:<\/p>\n\n\n\n<pre class=\"wp-block-code\"><code><code>def __init__(name: str, age: int)<\/code><small>Code language: Python (python)<\/small><\/code><\/pre>\n\n\n\n<p>Note that the order of the attributes declared in the class will determine the orders of the parameters in the&nbsp;<code>__init__<\/code>&nbsp;method.<\/p>\n\n\n\n<p>And you can create the\u00a0<code>Person<\/code>\u2018s object:<\/p>\n\n\n\n<pre class=\"wp-block-code\"><code><code>p1 = Person('John', 25)<\/code><small>Code language: Python (python)<\/small><\/code><\/pre>\n\n\n\n<p>When printing out the\u00a0<code>Person<\/code>\u2018s object, you\u2019ll get a readable format:<\/p>\n\n\n\n<pre class=\"wp-block-code\"><code><code>print(p1)<\/code><small>Code language: Python (python)<\/small><\/code><\/pre>\n\n\n\n<p>Output:<\/p>\n\n\n\n<pre class=\"wp-block-code\"><code><code>Person(name='John', age=25)<\/code><small>Code language: Python (python)<\/small><\/code><\/pre>\n\n\n\n<p>Also, if you compare two\u00a0<code>Person<\/code>\u2018s objects with the same attribute value, it\u2019ll return\u00a0<code>True<\/code>. For example:<\/p>\n\n\n\n<pre class=\"wp-block-code\"><code><code>p1 = Person('John', 25) p2 = Person('John', 25) print(p1 == p2)<\/code><small>Code language: Python (python)<\/small><\/code><\/pre>\n\n\n\n<p>Output:<\/p>\n\n\n\n<pre class=\"wp-block-code\"><code><code>True<\/code><small>Code language: Python (python)<\/small><\/code><\/pre>\n\n\n\n<p>The following discusses other functions that a data class provides.<\/p>\n\n\n\n<h2 class=\"wp-block-heading\">Default values<\/h2>\n\n\n\n<p>When using a regular class, you can define default values for attributes. For example, the following\u00a0<code>Person<\/code>\u00a0class has the\u00a0<code>iq<\/code>\u00a0parameter with the default value of\u00a0<code>100<\/code>.<\/p>\n\n\n\n<pre class=\"wp-block-code\"><code><code>class Person: def __init__(self, name, age, iq=100): self.name = name self.age = age self.iq = iq<\/code><small>Code language: Python (python)<\/small><\/code><\/pre>\n\n\n\n<p>To define a default value for an attribute in the dataclass, you assign it to the attribute like this:<\/p>\n\n\n\n<pre class=\"wp-block-code\"><code><code>from dataclasses import dataclass @dataclass class Person: name: str age: int iq: int = 100 print(Person('John Doe', 25))<\/code><small>Code language: Python (python)<\/small><\/code><\/pre>\n\n\n\n<p>Like the parameter rules, the attributes with the default values must appear after the ones without default values. Therefore, the following code will not work:<\/p>\n\n\n\n<pre class=\"wp-block-code\"><code><code>from dataclasses import dataclass @dataclass class Person: iq: int = 100 name: str age: int<\/code><small>Code language: Python (python)<\/small><\/code><\/pre>\n\n\n\n<h2 class=\"wp-block-heading\">Convert to a tuple or a dictionary<\/h2>\n\n\n\n<p>The\u00a0<code>dataclasses<\/code>\u00a0module has the\u00a0<code>astuple()<\/code>\u00a0and\u00a0<code>asdict()<\/code>\u00a0functions that convert an instance of the dataclass to a\u00a0tuple\u00a0and a\u00a0dictionary. For example:<\/p>\n\n\n\n<pre class=\"wp-block-code\"><code><code>from dataclasses import dataclass, astuple, asdict @dataclass class Person: name: str age: int iq: int = 100 p = Person('John Doe', 25) print(astuple(p)) print(asdict(p))<\/code><small>Code language: Python (python)<\/small><\/code><\/pre>\n\n\n\n<p>Output:<\/p>\n\n\n\n<pre class=\"wp-block-code\"><code><code>('John Doe', 25, 100) {'name': 'John Doe', 'age': 25, 'iq': 100}<\/code><small>Code language: Python (python)<\/small><\/code><\/pre>\n\n\n\n<h2 class=\"wp-block-heading\">Create immutable objects<\/h2>\n\n\n\n<p>To create readonly objects from a dataclass, you can set the frozen argument of the dataclass decorator to\u00a0<code>True<\/code>. For example:<\/p>\n\n\n\n<pre class=\"wp-block-code\"><code><code>from dataclasses import dataclass, astuple, asdict @dataclass(frozen=True) class Person: name: str age: int iq: int = 100<\/code><small>Code language: Python (python)<\/small><\/code><\/pre>\n\n\n\n<p>If you attempt to change the attributes of the object after it is created, you\u2019ll get an error. For example:<\/p>\n\n\n\n<pre class=\"wp-block-code\"><code><code>p = Person('Jane Doe', 25) p.iq = 120<\/code><small>Code language: Python (python)<\/small><\/code><\/pre>\n\n\n\n<p>Error:<\/p>\n\n\n\n<pre class=\"wp-block-code\"><code><code>dataclasses.FrozenInstanceError: cannot assign to field 'iq'<\/code><small>Code language: Python (python)<\/small><\/code><\/pre>\n\n\n\n<h2 class=\"wp-block-heading\">Customize attribute behaviors<\/h2>\n\n\n\n<p>If don\u2019t want to initialize an attribute in the __init__ method, you can use the&nbsp;<code>field()<\/code>&nbsp;function from the&nbsp;<code>dataclasses<\/code>&nbsp;module.<\/p>\n\n\n\n<p>The following example defines the\u00a0<code>can_vote<\/code>\u00a0attribute that is initialized using the\u00a0<code>__init__<\/code>\u00a0method:<\/p>\n\n\n\n<pre class=\"wp-block-code\"><code><code>from dataclasses import dataclass, field class Person: name: str age: int iq: int = 100 can_vote: bool = field(init=False)<\/code><small>Code language: Python (python)<\/small><\/code><\/pre>\n\n\n\n<p>The&nbsp;<code>field()<\/code>&nbsp;function has multiple interesting parameters such as&nbsp;<code>repr<\/code>,&nbsp;<code>hash<\/code>,&nbsp;<code>compare<\/code>, and&nbsp;<code>metadata<\/code>.<\/p>\n\n\n\n<p>If you want to initialize an attribute that depends on the value of another attribute, you can use the&nbsp;<code>__post_init__<\/code>&nbsp;method. As its name implies, Python calls the&nbsp;<code>__post_init__<\/code>&nbsp;method after the&nbsp;<code>__init__<\/code>&nbsp;method.<\/p>\n\n\n\n<p>The following use the\u00a0<code>__post_init__<\/code>\u00a0method to initialize the\u00a0<code>can_vote<\/code>\u00a0attribute based on the\u00a0<code>age<\/code>\u00a0attribute:<\/p>\n\n\n\n<pre class=\"wp-block-code\"><code><code>from dataclasses import dataclass, field @dataclass class Person: name: str age: int iq: int = 100 can_vote: bool = field(init=False) def __post_init__(self): print('called __post_init__ method') self.can_vote = 18 &lt;= self.age &lt;= 70 p = Person('Jane Doe', 25) print(p)<\/code><small>Code language: Python (python)<\/small><\/code><\/pre>\n\n\n\n<p>Output:<\/p>\n\n\n\n<pre class=\"wp-block-code\"><code><code>called the __post_init__ method Person(name='Jane Doe', age=25, iq=100, can_vote=True)<\/code><small>Code language: Python (python)<\/small><\/code><\/pre>\n\n\n\n<h2 class=\"wp-block-heading\">Sort objects<\/h2>\n\n\n\n<p>By default, a dataclass implements the&nbsp;<code>__eq__<\/code>&nbsp;method.<\/p>\n\n\n\n<p>To allow different types of comparisons like\u00a0<code>__lt__<\/code>,\u00a0<code>__lte__<\/code>,\u00a0<code>__gt__<\/code>,\u00a0<code>__gte__<\/code>, you can set the order argument of the\u00a0<code>@dataclass<\/code>\u00a0decorator to True:<\/p>\n\n\n\n<pre class=\"wp-block-code\"><code><code>@dataclass(order=True)<\/code><small>Code language: CSS (css)<\/small><\/code><\/pre>\n\n\n\n<p>By doing this, the dataclass will sort the objects by every field until it finds a value that\u2019s not equal.<\/p>\n\n\n\n<p>In practice, you often want to compare objects by a particular attribute, not all attributes. To do that, you need to define a field called&nbsp;<code>sort_index<\/code>&nbsp;and set its value to the attribute that you want to sort.<\/p>\n\n\n\n<p>For example, suppose you have a list of\u00a0<code>Person<\/code>\u2018s objects and want to sort them by age:<\/p>\n\n\n\n<pre class=\"wp-block-code\"><code><code>members = &#91; Person('John', 25), Person('Bob', 35), Person('Alice', 30) ]<\/code><small>Code language: Python (python)<\/small><\/code><\/pre>\n\n\n\n<p>To do that, you need to:<\/p>\n\n\n\n<ul class=\"wp-block-list\">\n<li>First, pass the&nbsp;<code>order=True<\/code>&nbsp;parameter to the&nbsp;<code>@dataclass<\/code>&nbsp;decorator.<\/li>\n\n\n\n<li>Second, define the&nbsp;<code>sort_index<\/code>&nbsp;attribute and set its&nbsp;<code>init<\/code>&nbsp;parameter to&nbsp;<code>False<\/code>.<\/li>\n\n\n\n<li>Third, set the&nbsp;<code>sort_index<\/code>&nbsp;to the&nbsp;<code>age<\/code>&nbsp;attribute in the&nbsp;<code>__post_init__<\/code>&nbsp;method to sort the&nbsp;<code>Person<\/code>\u2018s object by age.<\/li>\n<\/ul>\n\n\n\n<p>The following shows the code for sorting\u00a0<code>Person<\/code>\u2018s objects by age:<\/p>\n\n\n\n<pre class=\"wp-block-code\"><code><code>from dataclasses import dataclass, field @dataclass(order=True) class Person: sort_index: int = field(init=False, repr=False) name: str age: int iq: int = 100 can_vote: bool = field(init=False) def __post_init__(self): self.can_vote = 18 &lt;= self.age &lt;= 70 <em># sort by age<\/em> self.sort_index = self.age members = &#91; Person(name='John', age=25), Person(name='Bob', age=35), Person(name='Alice', age=30) ] sorted_members = sorted(members) for member in sorted_members: print(f'{member.name}(age={member.age})')<\/code><small>Code language: Python (python)<\/small><\/code><\/pre>\n\n\n\n<p>Output:<\/p>\n\n\n\n<pre class=\"wp-block-code\"><code><code>John(age=25) Alice(age=30) Bob(age=35)<\/code><\/code><\/pre>\n","protected":false},"excerpt":{"rendered":"<p>Summary: in this tutorial, you\u2019ll learn about the Python dataclass decorator and how to use it effectively. Introduction to the Python dataclass Python introduced the dataclass in version 3.7 (PEP 557). The dataclass allows you to define\u00a0classes\u00a0with less code and more functionality out of the box. The following defines a regular\u00a0Person\u00a0class with two instance attributes\u00a0name\u00a0and\u00a0age: [&hellip;]<\/p>\n","protected":false},"author":1,"featured_media":0,"comment_status":"open","ping_status":"open","sticky":false,"template":"","format":"standard","meta":{"footnotes":""},"categories":[32],"tags":[],"class_list":["post-220","post","type-post","status-publish","format-standard","hentry","category-8-metaprogramming-exceptions"],"_links":{"self":[{"href":"https:\/\/learnpython.elegantwallp.com\/wp-json\/wp\/v2\/posts\/220","targetHints":{"allow":["GET"]}}],"collection":[{"href":"https:\/\/learnpython.elegantwallp.com\/wp-json\/wp\/v2\/posts"}],"about":[{"href":"https:\/\/learnpython.elegantwallp.com\/wp-json\/wp\/v2\/types\/post"}],"author":[{"embeddable":true,"href":"https:\/\/learnpython.elegantwallp.com\/wp-json\/wp\/v2\/users\/1"}],"replies":[{"embeddable":true,"href":"https:\/\/learnpython.elegantwallp.com\/wp-json\/wp\/v2\/comments?post=220"}],"version-history":[{"count":1,"href":"https:\/\/learnpython.elegantwallp.com\/wp-json\/wp\/v2\/posts\/220\/revisions"}],"predecessor-version":[{"id":221,"href":"https:\/\/learnpython.elegantwallp.com\/wp-json\/wp\/v2\/posts\/220\/revisions\/221"}],"wp:attachment":[{"href":"https:\/\/learnpython.elegantwallp.com\/wp-json\/wp\/v2\/media?parent=220"}],"wp:term":[{"taxonomy":"category","embeddable":true,"href":"https:\/\/learnpython.elegantwallp.com\/wp-json\/wp\/v2\/categories?post=220"},{"taxonomy":"post_tag","embeddable":true,"href":"https:\/\/learnpython.elegantwallp.com\/wp-json\/wp\/v2\/tags?post=220"}],"curies":[{"name":"wp","href":"https:\/\/api.w.org\/{rel}","templated":true}]}}