A look at @dataclass - Printable Version +- Python Forum (https://python-forum.io) +-- Forum: General (https://python-forum.io/forum-1.html) +--- Forum: Tutorials (https://python-forum.io/forum-4.html) +--- Thread: A look at @dataclass (/thread-11397.html) |
A look at @dataclass - snippsat - Jul-07-2018 This will be a look/tutorial at the biggest news with Python 3.7 which was @dataclass. Can just write a class and test it out. from dataclasses import dataclass, field from decimal import Decimal @dataclass() class Vehicle: vehicle_type: str name : str price: DecimalSo this @dataclass generate .__init__(), .__repr__(), and .__eq__() for you.An other thing to look at is that type hint (class annotations) is mandatory when defining the fields in your data. These are hints and will not affect when say use a wrong type at run time. To actually catch type errors, type checkers like Mypy can be run on your source code. Use class: # Instantiate class >>> vehicle_1 = Vehicle('Car', 'Bmw', Decimal('200_000.55')) >>> vehicle_2 = Vehicle('Bus', 'Volvo', Decimal('1000_000.99')) # Call instance objects >>> vehicle_1.name 'Bmw' >>> vehicle_1.price Decimal('200000.55') >>> vehicle_2.vehicle_type 'Bus' >>> # __repr__ in action >>> vehicle_1 Vehicle(vehicle_type='Car', name='Bmw', price=Decimal('200000.55')) >>> print(vehicle_2) Vehicle(vehicle_type='Bus', name='Volvo', price=Decimal('1000000.99')) >>> # __eq__ in action >>> vehicle_1 == Vehicle('Car', 'Bmw', Decimal('200_000.55')) True >>> vehicle_1 == Vehicle('Bus', 'Volvo', Decimal('1000_000.99')) False # Some use of class with f-string >>> print(f'The bus cost {vehicle_2.price - vehicle_1.price}$ more than the car') The bus cost 800000.44$ more than the car >>> # They are type hint,nothing happen if assassin a int to vehicle_type: str >>> vehicle_1 = Vehicle('Car', 'Bmw', 200_000.55) >>> vehicle_1.name = 9999999 >>> vehicle_1.name 9999999So what is going on in background,here my take on it writing the generated code for this @dataclass. I used Raymond Hettinger Pycon 2018 Talk about @dataclasse as help to write this. Generated code: Here we see the missing method like eg __init__ .Adding to decorator and field from dataclasses import dataclass, field from decimal import Decimal @dataclass(order=True, frozen=True) class Vehicle: kind: str = field(compare=False) name : str = field(compare=False) price: DecimalJust doing this will generate about 40 more lines of code. Quote: Now is @dataclass orderable(not bye default) immutability and hashability is added. Also tell that str shall not be in comparison when eg using sorted() .So now it will only sort bye price of vehicle. Use class: # Instantiate class >>> vehicle_1 = Vehicle('Car', 'Bmw', 200_000.55) >>> vehicle_2 = Vehicle('Bus', 'Volvo', 1000_000.99) >>> vehicle_3 = Vehicle('Cycle', 'Colnago', 10000) # Sort bye price >>> sorted((vehicle_1, vehicle_2, vehicle_3)) [Vehicle(kind='Cycle', name='Colnago', price=10000), Vehicle(kind='Car', name='Bmw', price=200000.55), Vehicle(kind='Bus', name='Volvo', price=1000000.99)] # High to low >>> sorted((vehicle_1, vehicle_2, vehicle_3), reverse=True) [Vehicle(kind='Bus', name='Volvo', price=1000000.99), Vehicle(kind='Car', name='Bmw', price=200000.55), Vehicle(kind='Cycle', name='Colnago', price=10000)] # Instantiate class Duplicate >>> vehicle_1 = Vehicle('Car', 'Bmw', 200_000.55) >>> vehicle_2 = Vehicle('Bus', 'Volvo', 1000_000.99) >>> vehicle_2 = Vehicle('Bus', 'Volvo', 1000_000.99) >>> vehicle_3 = Vehicle('Cycle', 'Colnago', 10000) # Using set() to remove >>> set((vehicle_1, vehicle_2, vehicle_3)) {Vehicle(kind='Cycle', name='Colnago', price=10000), Vehicle(kind='Car', name='Bmw', price=200000.55), Vehicle(kind='Bus', name='Volvo', price=1000000.99)} # Is immutable >>> vehicle_1.name = 'Taxi' Traceback (most recent call last): File "<string>", line 428, in runcode File "<interactive input>", line 1, in <module> File "<string>", line 3, in __setattr__ dataclasses.FrozenInstanceError: cannot assign to field 'name' Adding methods This works as normal method in a class,here can use Type hint or not. from dataclasses import dataclass, field from decimal import Decimal @dataclass(order=True) class Vehicle: vehicle_type: str = field(compare=False) name : str = field(compare=False) price: Decimal def __str__(self): return f'{self.vehicle_type} {self.name} at cost of {self.price}$' @property def color_choice(self): if self.name == 'Bmw': print(f'Color choice is Black | Blue | Red for {self.name} {self.vehicle_type}') if self.name == 'Volvo': print(f'Color choice is Orange | Yellow | Green for {self.name} {self.vehicle_type}') if self.name == 'Colnago': print(f'Color choice is Dark Blue | Red/yellows(Mixed in Green)', f'| Stealth for {self.name} {self.vehicle_type}') Use class: # Instantiate class >>> vehicle_1 = Vehicle('Car', 'Bmw', Decimal('200_000.55')) >>> vehicle_2 = Vehicle('Bus', 'Volvo', Decimal('1000_000.99')) >>> vehicle_3 = Vehicle('Cycle', 'Colnago', Decimal('10_000')) >>> >>> # New added __str__ >>> print(vehicle_1) Car Bmw at cost of 200000.55$ >>> # Generated __repr___ >>> vehicle_1 Vehicle(vehicle_type='Car', name='Bmw', price=Decimal('200000.55')) >>> >>> # The new method >>> vehicle_2.color_choice Color choice is Orange | Yellow | Green for Volvo Bus >>> vehicle_3.color_choice Color choice is Dark Blue | Red/yellows(Mixed in Green) | Stealth for Colnago Cycle Mixed Stuff __post_init__ can be used to add stuff.It run after the auto-generated __init__ .from dataclasses import dataclass, field @dataclass class MakeUpper: field_a: str field_b: str = field(default='') def __post_init__(self): self.orginal = self.field_b self.field_b = self.field_b.upper() Use class: >>> result = MakeUpper(field_a='a', field_b='all lowercase given') >>> result.field_a 'a' >>> result.orginal 'all lowercase given' >>> result.field_b 'ALL LOWERCASE GIVEN' >>> result.__annotations__ {'field_a': <class 'str'>, 'field_b': <class 'str'>}Inheritance @dataclass support inheritance like normal python classes. from dataclasses import dataclass, field @dataclass class Plantes: name: str size: int = 0 def __str__(self): return f'{self.name} size is {self.size}-km and temprature is {self.celsius}°C' @dataclass class Temprature(Plantes): celsius: int = 0 Use class: >>> obj = Temprature('Venus', 6052, 465) >>> >>> # __repr__ >>> obj Temprature(name='Venus', size=6052, celsius=465) >>> >>> # __str__ >>> print(obj) Venus size is 6052-km and temprature is 465°C >>> >>> obj.celsius 465 __post_init__ is can be called in inheritance using super() as a normal __init__ def __post_init__(self): super().__post_init__() #Call post init of Parent class print("Something") Thoughts? I can like the look and use of it,as get a lot of stuff generated for free This is a open tutorials,so if someone has tested or have a opinions about @dataclass feel free to posts about it here. |