Skip to content

Commit 9d2c9ae

Browse files
add content
1 parent dd9ddc2 commit 9d2c9ae

File tree

13 files changed

+1550
-90
lines changed

13 files changed

+1550
-90
lines changed

Chapter2/pydantic.ipynb

Lines changed: 159 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -182,6 +182,165 @@
182182
"source": [
183183
"[Learn more about Pydantic's numeric constraints](https://bit.ly/4bbhthb)."
184184
]
185+
},
186+
{
187+
"cell_type": "markdown",
188+
"id": "62e5e4b0-4aa9-4956-ae10-a8aacef31ed3",
189+
"metadata": {},
190+
"source": [
191+
"### Python Data Models: Pydantic or attrs?"
192+
]
193+
},
194+
{
195+
"cell_type": "markdown",
196+
"id": "3d55d578-3ce8-4f86-b03f-d10e8d2fe6b1",
197+
"metadata": {},
198+
"source": [
199+
"Pydantic is a popular library that provides built-in data validation and type checking. This makes it an excellent choice for web APIs and external data handling. However, this added functionality comes at a cost:\n",
200+
"\n",
201+
"* Performance overhead\n",
202+
"* High memory usage\n",
203+
"* Harder to debug\n",
204+
"\n",
205+
"Here's an example of a Pydantic model:"
206+
]
207+
},
208+
{
209+
"cell_type": "code",
210+
"execution_count": 18,
211+
"id": "7cb1a209-4441-4485-83d1-9e5f090af709",
212+
"metadata": {},
213+
"outputs": [],
214+
"source": [
215+
"from pydantic import BaseModel\n",
216+
"\n",
217+
"class UserPydantic(BaseModel):\n",
218+
" name: str\n",
219+
" age: int"
220+
]
221+
},
222+
{
223+
"cell_type": "markdown",
224+
"id": "d7682d4c-9cdb-4e9d-9497-155b03a21357",
225+
"metadata": {},
226+
"source": [
227+
"Attrs, on the other hand, is a lighter-weight library that provides a simpler way to define data models. While it doesn't have built-in data validation, it's ideal for internal data structures and simpler class creation:"
228+
]
229+
},
230+
{
231+
"cell_type": "code",
232+
"execution_count": 19,
233+
"id": "0ccff22f-e90b-4f4c-926c-168d3c7c2810",
234+
"metadata": {},
235+
"outputs": [],
236+
"source": [
237+
"from attrs import define, field\n",
238+
"\n",
239+
"@define\n",
240+
"class UserAttrs:\n",
241+
" name: str\n",
242+
" age: int"
243+
]
244+
},
245+
{
246+
"cell_type": "markdown",
247+
"id": "285909fb-c516-4adf-bf2f-7067deae955d",
248+
"metadata": {},
249+
"source": [
250+
"Let's compare the performance of Pydantic and attrs using a simple benchmark:"
251+
]
252+
},
253+
{
254+
"cell_type": "code",
255+
"execution_count": 20,
256+
"id": "d61af650-9fc2-4a16-8201-db14674ce964",
257+
"metadata": {},
258+
"outputs": [
259+
{
260+
"name": "stdout",
261+
"output_type": "stream",
262+
"text": [
263+
"Pydantic: 0.1071 seconds\n",
264+
"attrs: 0.0155 seconds\n",
265+
"Using attrs is 6.90 times faster than using Pydantic\n"
266+
]
267+
}
268+
],
269+
"source": [
270+
"from timeit import timeit\n",
271+
"\n",
272+
"# Test data\n",
273+
"data = {\"name\": \"Bob\", \"age\": 30}\n",
274+
"\n",
275+
"# Benchmark\n",
276+
"pydantic_time = timeit(lambda: UserPydantic(**data), number=100000)\n",
277+
"attrs_time = timeit(lambda: UserAttrs(**data), number=100000)\n",
278+
"\n",
279+
"print(f\"Pydantic: {pydantic_time:.4f} seconds\")\n",
280+
"print(f\"attrs: {attrs_time:.4f} seconds\")\n",
281+
"print(f\"Using attrs is {pydantic_time/attrs_time:.2f} times faster than using Pydantic\")"
282+
]
283+
},
284+
{
285+
"cell_type": "markdown",
286+
"id": "0d3a6234-b28c-437a-b6bb-ac2e17101d99",
287+
"metadata": {},
288+
"source": [
289+
"The results show that attrs is approximately 6.9 times faster than Pydantic.\n",
290+
"\n",
291+
"While attrs doesn't have built-in data validation, you can easily add validation using a decorator:"
292+
]
293+
},
294+
{
295+
"cell_type": "code",
296+
"execution_count": 21,
297+
"id": "de240c6b-6f26-49e5-b2ec-18733265a5cf",
298+
"metadata": {},
299+
"outputs": [
300+
{
301+
"name": "stdout",
302+
"output_type": "stream",
303+
"text": [
304+
"ValueError: Age can't be negative\n"
305+
]
306+
}
307+
],
308+
"source": [
309+
"from attrs import define, field\n",
310+
"\n",
311+
"@define\n",
312+
"class UserAttrs:\n",
313+
" name: str\n",
314+
" age: int = field()\n",
315+
"\n",
316+
" @age.validator\n",
317+
" def check_age(self, attribute, value):\n",
318+
" if value < 0:\n",
319+
" raise ValueError(\"Age can't be negative\")\n",
320+
" return value # accepts any positive age\n",
321+
"\n",
322+
"\n",
323+
"try:\n",
324+
" user = UserAttrs(name=\"Bob\", age=-1)\n",
325+
"except ValueError as e:\n",
326+
" print(\"ValueError:\", e)"
327+
]
328+
},
329+
{
330+
"cell_type": "markdown",
331+
"id": "d2998fe6-0f29-458d-9795-fd1a7db45f15",
332+
"metadata": {},
333+
"source": [
334+
"In this example, we've added a validator to the `age` field to ensure it's not negative. If you try to create a `UserAttrs` instance with a negative age, it will raise a `ValueError`."
335+
]
336+
},
337+
{
338+
"cell_type": "markdown",
339+
"id": "9b8ca536-164e-42f7-ae5d-b0389711b45c",
340+
"metadata": {},
341+
"source": [
342+
"[Link to attrs](https://github.com/python-attrs/attrs)."
343+
]
185344
}
186345
],
187346
"metadata": {

Chapter5/better_pandas.ipynb

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -5004,7 +5004,7 @@
50045004
"metadata": {
50055005
"celltoolbar": "Tags",
50065006
"kernelspec": {
5007-
"display_name": "Python 3 (ipykernel)",
5007+
"display_name": "venv",
50085008
"language": "python",
50095009
"name": "python3"
50105010
},

0 commit comments

Comments
 (0)