Skip to content

Commit 1f5c16f

Browse files
committed
Add dataclass and property lessons
1 parent 9be45dd commit 1f5c16f

11 files changed

Lines changed: 248 additions & 0 deletions

File tree

‎README.de.md‎

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -94,8 +94,10 @@ Es gibt zwei Möglichkeiten, die Module auszuführen:
9494
- Comprehension: [list | tuple | set | dict](ultimatepython/fundamentals/comprehension.py)
9595
3. **Objektorientierte Programmierung**
9696
- Basic class: [Basic definition](ultimatepython/oop/basic_class.py) ( 🍰 )
97+
- Data class: [dataclass](ultimatepython/oop/dataclass.py) ( 🍰 )
9798
- Inheritance: [Inheritance](ultimatepython/oop/inheritance.py) ( 🍰 )
9899
- Encapsulation: [Encapsulation definition](ultimatepython/oop/encapsulation.py)
100+
- Property: [property | setter | deleter](ultimatepython/oop/property.py) ( 🍰 )
99101
- Abstract class: [Abstract definition](ultimatepython/oop/abstract_class.py)
100102
- Exception class: [Exception definition](ultimatepython/oop/exception_class.py)
101103
- Iterator class: [Iterator definition | yield](ultimatepython/oop/iterator_class.py) ( 🤯 )

‎README.es.md‎

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -92,8 +92,10 @@ Hay dos maneras de ejecutar los módulos:
9292
- Comprensión: [list | tuple | set | dict](ultimatepython/fundamentals/comprehension.py)
9393
3. **Programación orientada a objetos**
9494
- Clase básica: [Definición de básica](ultimatepython/oop/basic_class.py) ( 🍰 )
95+
- Clase de datos: [dataclass](ultimatepython/oop/dataclass.py) ( 🍰 )
9596
- Herencia: [Herencia](ultimatepython/oop/inheritance.py) ( 🍰 )
9697
- Encapsulación: [Definición de encapsulación](ultimatepython/oop/encapsulation.py)
98+
- Propiedad: [property | setter | deleter](ultimatepython/oop/property.py) ( 🍰 )
9799
- Clase abstracta: [Definición de abstracta](ultimatepython/oop/abstract_class.py)
98100
- Clase de excepción: [Definición de excepción](ultimatepython/oop/exception_class.py)
99101
- Clase iteradora: [Definición de iteradora | yield](ultimatepython/oop/iterator_class.py) ( 🤯 )

‎README.fr.md‎

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -101,8 +101,10 @@ Deux méthodes sont possibles :
101101
- Compréhension : [list | tuple | set | dict](ultimatepython/fundamentals/comprehension.py)
102102
3. **Programmation orientée objet**
103103
- Classe basique : [Définition basique](ultimatepython/oop/basic_class.py) ( 🍰 )
104+
- Classe de données : [dataclass](ultimatepython/oop/dataclass.py) ( 🍰 )
104105
- Héritage : [Héritage](ultimatepython/oop/inheritance.py) ( 🍰 )
105106
- Encapsulation : [Définition de l’encapsulation](ultimatepython/oop/encapsulation.py)
107+
- Propriété : [property | setter | deleter](ultimatepython/oop/property.py) ( 🍰 )
106108
- Classe abstraite : [Définition abstraite](ultimatepython/oop/abstract_class.py)
107109
- Classe d’exception : [Définition d’exception](ultimatepython/oop/exception_class.py)
108110
- Itérateur : [Définition d’itérateur | yield](ultimatepython/oop/iterator_class.py) ( 🤯 )

‎README.hi.md‎

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -75,8 +75,10 @@ print("Ultimate Python स्टडी गाइड")
7575
- संकलन: [लिसट | ट्यूपल | सेट | डिक्ट](ultimatepython/fundamentals/comprehension.py)
7676
3. **ऑब्जेक्ट-ओरिएंटेड प्रोग्रामिंग**
7777
- बेसिक क्लास: [बेसिक परिभाषा](ultimatepython/oop/basic_class.py) ( 🍰 )
78+
- डेटा क्लास: [dataclass](ultimatepython/oop/dataclass.py) ( 🍰 )
7879
- इन्हरिटैंस: [इन्हरिटैंस](ultimatepython/oop/inheritance.py) ( 🍰 )
7980
- ऐनकैपसुलेषन: [ऐनकैपसुलेषन परिभाषा](ultimatepython/oop/encapsulation.py)
81+
- प्रॉपर्टी: [property | setter | deleter](ultimatepython/oop/property.py) ( 🍰 )
8082
- एैबस्टराक्ट क्लास: [बेसिक परिभाषा](ultimatepython/oop/abstract_class.py)
8183
- एक्सेपशन क्लास: [बेसिक परिभाषा](ultimatepython/oop/exception_class.py)
8284
- इटरेटर क्लास: [इटरेटर परिभाषा | yield](ultimatepython/oop/iterator_class.py) ( 🤯 )

‎README.ko.md‎

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -83,8 +83,10 @@ print("Ultimate Python 학습 가이드")
8383
- 컴프리헨션: [list | tuple | set | dict](ultimatepython/fundamentals/comprehension.py)
8484
3. **객체 지향 프로그래밍**
8585
- 기본 클래스: [기본 정의](ultimatepython/oop/basic_class.py) ( 🍰 )
86+
- 데이터 클래스: [dataclass](ultimatepython/oop/dataclass.py) ( 🍰 )
8687
- 상속: [상속](ultimatepython/oop/inheritance.py) ( 🍰 )
8788
- 캡슐화: [캡슐화 정의](ultimatepython/oop/encapsulation.py)
89+
- 프로퍼티: [property | setter | deleter](ultimatepython/oop/property.py) ( 🍰 )
8890
- 추상 클래스: [추상 정의](ultimatepython/oop/abstract_class.py)
8991
- 예외 클래스: [예외 정의](ultimatepython/oop/exception_class.py)
9092
- 반복자 클래스: [반복자 정의 | yield](ultimatepython/oop/iterator_class.py) ( 🤯 )

‎README.md‎

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -95,8 +95,10 @@ There are two ways of running the modules:
9595
- Comprehension: [list | tuple | set | dict](ultimatepython/fundamentals/comprehension.py)
9696
3. **Object-Oriented Programming**
9797
- Basic class: [Basic definition](ultimatepython/oop/basic_class.py) ( 🍰 )
98+
- Data class: [dataclass](ultimatepython/oop/dataclass.py) ( 🍰 )
9899
- Inheritance: [Inheritance](ultimatepython/oop/inheritance.py) ( 🍰 )
99100
- Encapsulation: [Encapsulation definition](ultimatepython/oop/encapsulation.py)
101+
- Property: [property | setter | deleter](ultimatepython/oop/property.py) ( 🍰 )
100102
- Abstract class: [Abstract definition](ultimatepython/oop/abstract_class.py)
101103
- Exception class: [Exception definition](ultimatepython/oop/exception_class.py)
102104
- Iterator class: [Iterator definition | yield](ultimatepython/oop/iterator_class.py) ( 🤯 )

‎README.pt_br.md‎

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -81,8 +81,10 @@ Existem duas maneiras de rodar os módulos:
8181
- Comprehension: [list | tuple | set | dict](ultimatepython/fundamentals/comprehension.py)
8282
3. **Programação orientada a objetos**
8383
- O básico de classes: [Definição de classe](ultimatepython/oop/basic_class.py) ( 🍰 )
84+
- Classe de dados: [dataclass](ultimatepython/oop/dataclass.py) ( 🍰 )
8485
- Herança: [Herança](ultimatepython/oop/inheritance.py) ( 🍰 )
8586
- Encapsulamento: [Definição de encapsulamento](ultimatepython/oop/encapsulation.py)
87+
- Propriedade: [property | setter | deleter](ultimatepython/oop/property.py) ( 🍰 )
8688
- Classe abstrata: [Definição de classe abstrata](ultimatepython/oop/abstract_class.py)
8789
- Classe de exceção: [Definição de Classe de exceção](ultimatepython/oop/exception_class.py)
8890
- Classe Iterator: [Definição de classe Iterator | yield](ultimatepython/oop/iterator_class.py) ( 🤯 )

‎README.zh_tw.md‎

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -78,8 +78,10 @@ print("Ultimate Python 學習大綱")
7878
- 綜合:[list | tuple | set | dict](ultimatepython/fundamentals/comprehension.py)
7979
3. **物件導向程式設計**
8080
- 基本類別:[基本定義](ultimatepython/oop/basic_class.py) ( 🍰 )
81+
- 資料類別:[dataclass](ultimatepython/oop/dataclass.py) ( 🍰 )
8182
- 繼承:[繼承](ultimatepython/oop/inheritance.py) ( 🍰 )
8283
- 封裝:[封裝定義](ultimatepython/oop/encapsulation.py)
84+
- 屬性:[property | setter | deleter](ultimatepython/oop/property.py) ( 🍰 )
8385
- 抽象類別:[抽象定義](ultimatepython/oop/abstract_class.py)
8486
- 異常類別:[異常定義](ultimatepython/oop/exception_class.py)
8587
- 迭代類別:[迭代器定義](ultimatepython/oop/iterator_class.py) ( 🤯 )

‎ui/parse_lessons.py‎

Lines changed: 11 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -81,6 +81,15 @@ def humanize_name(name: str) -> str:
8181
"syntax": "/",
8282
"summary": "Enforce clean API boundaries and prevent callers from coupling to internal parameter names.",
8383
},
84+
# Python 3.7
85+
"dataclass": {
86+
"version": "Python 3.7",
87+
"pep": "PEP 557",
88+
"pep_url": "https://peps.python.org/pep-0557/",
89+
"feature": "Data Classes",
90+
"syntax": "@dataclass",
91+
"summary": "Declarative classes with auto-generated dunder methods (__init__, __repr__, __eq__) to eliminate boilerplate.",
92+
},
8493
}
8594

8695

@@ -137,8 +146,10 @@ def parse_file(filepath: str, relative_path: str) -> dict:
137146
],
138147
"oop": [
139148
"basic_class",
149+
"dataclass",
140150
"inheritance",
141151
"encapsulation",
152+
"property",
142153
"abstract_class",
143154
"exception_class",
144155
"iterator_class",

‎ultimatepython/oop/dataclass.py‎

Lines changed: 123 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,123 @@
1+
"""
2+
Data classes (PEP 557), introduced in Python 3.7, provide a declarative syntax
3+
for classes that primarily store state.
4+
5+
Before dataclasses, developers wrote repetitive boilerplate for `__init__`,
6+
`__repr__`, and `__eq__` methods. The `@dataclass` decorator inspects type
7+
annotations on class attributes to automatically generate these standard methods.
8+
9+
Advanced capabilities include:
10+
- `field(default_factory=...)` to avoid mutable default argument pitfalls
11+
- `frozen=True` for immutable instances that can be hashed and stored in sets
12+
- `kw_only=True` to enforce keyword-only arguments during initialization
13+
- `__post_init__` for validation and derived attribute computation
14+
"""
15+
16+
from dataclasses import FrozenInstanceError, asdict, dataclass, field
17+
18+
19+
@dataclass
20+
class Point:
21+
"""Simple 2D coordinate with auto-generated constructor and equality."""
22+
23+
x: float
24+
y: float
25+
26+
27+
@dataclass
28+
class UserProfile:
29+
"""User profile demonstrating default values and default factories."""
30+
31+
username: str
32+
email: str
33+
tags: list[str] = field(default_factory=list)
34+
active: bool = True
35+
36+
37+
@dataclass(frozen=True)
38+
class ServerConfig:
39+
"""Immutable server configuration that is safely hashable."""
40+
41+
host: str
42+
port: int
43+
44+
45+
@dataclass(kw_only=True)
46+
class InventoryItem:
47+
"""Item demonstrating keyword-only parameters and post-initialization."""
48+
49+
name: str
50+
unit_price: float
51+
quantity: int = 1
52+
total_cost: float = field(init=False)
53+
54+
def __post_init__(self) -> None:
55+
if self.unit_price < 0 or self.quantity < 0:
56+
raise ValueError("Price and quantity must be non-negative")
57+
self.total_cost = round(self.unit_price * self.quantity, 2)
58+
59+
60+
def main() -> None:
61+
# 1. Automatic __init__, __repr__, and __eq__
62+
p1 = Point(10.0, 20.0)
63+
p2 = Point(10.0, 20.0)
64+
p3 = Point(5.0, 15.0)
65+
66+
assert p1 == p2
67+
assert p1 != p3
68+
assert repr(p1) == "Point(x=10.0, y=20.0)"
69+
70+
# 2. Default factories prevent shared mutable state across instances
71+
user_a = UserProfile("alice", "alice@example.com")
72+
user_b = UserProfile("bob", "bob@example.com")
73+
user_a.tags.append("admin")
74+
75+
assert user_a.tags == ["admin"]
76+
assert user_b.tags == []
77+
assert user_a.tags is not user_b.tags
78+
79+
# Convert dataclass instance directly to a dictionary
80+
assert asdict(user_a) == {
81+
"username": "alice",
82+
"email": "alice@example.com",
83+
"tags": ["admin"],
84+
"active": True,
85+
}
86+
87+
# 3. Frozen dataclasses guarantee immutability and allow hashing
88+
config = ServerConfig("127.0.0.1", 8080)
89+
mutation_failed = False
90+
try:
91+
config.port = 9090 # type: ignore[misc]
92+
except FrozenInstanceError:
93+
mutation_failed = True
94+
assert mutation_failed is True
95+
96+
# Because config is immutable, it can be used in sets and as dictionary keys
97+
config_set = {config, ServerConfig("127.0.0.1", 8080)}
98+
assert len(config_set) == 1
99+
100+
# 4. Keyword-only instantiation and __post_init__ computation
101+
item = InventoryItem(name="Mechanical Keyboard", unit_price=99.99, quantity=2)
102+
assert item.total_cost == 199.98
103+
104+
# Keyword-only enforcement ensures clear call sites
105+
positional_failed = False
106+
try:
107+
InventoryItem("Monitor", 299.99) # type: ignore[misc]
108+
except TypeError:
109+
positional_failed = True
110+
assert positional_failed is True
111+
112+
# Post-init validation prevents invalid state
113+
invalid_item_failed = False
114+
try:
115+
InventoryItem(name="Faulty Item", unit_price=-10.0)
116+
except ValueError as e:
117+
invalid_item_failed = True
118+
assert str(e) == "Price and quantity must be non-negative"
119+
assert invalid_item_failed is True
120+
121+
122+
if __name__ == "__main__":
123+
main()

0 commit comments

Comments
 (0)