Has this ever happened to you? Creating a class with a lot of parameters, and you need to initialize one by one manually.
class Product(object):
def __init__(self, id, author_id, category_id, brand_id, spu_id,
title, item_id, n_comments, creation_time, update_time,
source='', parent_id=0, ancestor_id=0):
self.id = id
self.author_id = author_id
...
Sometimes you also have to write a __repr__
or __str__
to express the classes with these variables, or __eq__
, __gt__
to compare the classes. This is very annoying and tedious, so python provides a dataclasses module in version 3.7 to simplify the process in building classes.
dataclasses
will automatically add __repr__
, __eq__
and other magic methods for us. Just install and import the dataclasses
module and add the dataclass
decorator to the class.
from dataclasses import dataclass
@dataclass
class InventoryItem:
name: str
unit_price: float
quantity_on_hand: int = 0
def total_cost(self) -> float:
return self.unit_price * self.quantity_on_hand
item = InventoryItem("product", 100, 1)
print(item) # InventoryItem(name='product', unit_price=100, quantity_on_hand=1)
You can see that the class of each variable is marked with variable: type
, which is python 3 function annotations. The typing module that helps annotations will also be used later on.
There are some variables that we want to change in __init__
, so dataclasses provides a __post_init__
function to do that.
from dataclasses import dataclass
@dataclass
class InventoryItem:
name: str
unit_price: float
quantity_on_hand: int = 0
def __post_init__(self):
self.unit_price *= 1.1
dataclasses
provides a field()
method to initialize each variable differently. The parameters to field()
are:
default
: default valuedefault_factory
: zero-argument callable used to define default valueinit
: bool, indicates whether this variable should be put into__init__
or not.compare
: bool, indicates whether this variable should be put into a comparable magic method such as__eq__
,__gt__
, etc.
from dataclasses import dataclass, field
from typing import List
@dataclass
class InventoryItem:
name: str
unit_price: float = field(default=0.0)
quantity_on_hand: int = field(default=0, repr=False)
parts: List[str] = field(default_factory=list)
item = InventoryItem("product")
print(item) # InventoryItem(name='product', unit_price=0.0, parts=[])
dataclasses
also provides variables that only appear in __post_init__
and don't really become class or instance variables. Just define the variable as InitVar
and pass it as __post_init__
.
from dataclasses import InitVar, dataclass, field
from typing import List
@dataclass
class InventoryItem:
name: str
unit_price: float = field(default=0.0)
quantity_on_hand: int = field(default=0, repr=False)
parts: List[str] = field(default_factory=list)
parts_number: InitVar[int] = 0
def __post_init__(self, parts_number):
self.parts.extend([f"part{i}" for i in range(1, parts_number + 1)])
item = InventoryItem("product", parts_number=2)
print(item) # InventoryItem(name='product', unit_price=0.0, parts=['part1', 'part2'])
Article | Link |
---|---|
attrs 和 Python3.7 的 dataclasses | https://zhuanlan.zhihu.com/p/34963159 |
Using Python dataclasses to simplify managing class objects | https://www.youtube.com/watch?v=FcVCfGJrkUQ |