Skip to content

Commit eb93f88

Browse files
authored
Merge pull request #22 from gjbex/development
Development
2 parents dfb14dd + f7b0b7f commit eb93f88

9 files changed

Lines changed: 260 additions & 33 deletions

File tree

docs/README.md

Lines changed: 5 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -65,6 +65,11 @@ from scratch.
6565
If you plan to do Python programming in a Linux or HPC environment you should
6666
be familiar with these as well.
6767

68+
For following along hands-on, you need
69+
* laptop or desktop with internet access.
70+
* a Python environment that can run Jupyter Lab if you want to use your own system;
71+
* access to Google Colaboratory if you prefer not to install software.
72+
6873

6974
## Level
7075

docs/_config.yml

Lines changed: 2 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1 +1,2 @@
1-
theme: jekyll-theme-slate
1+
title: "Python software development"
2+
theme: jekyll-theme-slate

python_software_engineering.pptx

-18.8 KB
Binary file not shown.

source-code/decorators/README.md

Lines changed: 4 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -14,5 +14,9 @@ What is it?
1414
large. The decorated function in this example is the factorial.
1515
Also it illustrates `functools` wrap decorator to retain the wrapped
1616
function's name, docstring, etc.
17+
1. `decorator_arguments.py`: an example of a decorator that takes arguments.
18+
The `check_range` decorator takes a minimum and maximum value as arguments
19+
and checks if the wrapped function's argument falls within that range,
20+
throwing an exception if it does not.
1721
1. `memoize.py`: an example of adding a cache to a function using a simple
1822
custom decorator, as well as `functools`'s `lru_cache` decorator.
Lines changed: 53 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,53 @@
1+
#!/usr/bin/env python3
2+
3+
import functools
4+
5+
6+
def check_bounds(min_value, max_value):
7+
'''Check bounds of a function argument
8+
9+
Parameters
10+
----------
11+
min_value: float
12+
Smallest value allowed for the function's parameter
13+
max_value: float
14+
Largest value allowed for the function's parameter
15+
16+
Raises
17+
------
18+
ValueError:
19+
If the argument is outside the specified bounds
20+
'''
21+
def check_bounds_wrapper(_func):
22+
@functools.wraps(_func)
23+
def wrapper(x):
24+
if min_value > x or x > max_value:
25+
raise ValueError(f'argument {x} not in [{min_value}, {max_value}]')
26+
return _func(x)
27+
return wrapper
28+
return check_bounds_wrapper
29+
30+
31+
@check_bounds(min_value=-1.0, max_value=1.0)
32+
def silly(x):
33+
'''Compute a rather uninteresting function
34+
35+
Parameters
36+
----------
37+
x: float
38+
Argument for the function
39+
40+
Returns
41+
-------
42+
float:
43+
Value computed by the function
44+
'''
45+
return x**2 - 2.0
46+
47+
48+
if __name__ == '__main__':
49+
print(silly(0.5))
50+
try:
51+
print(silly(2.5))
52+
except ValueError as e:
53+
print(f'Exception raised as expected: {e}')

source-code/object-orientation/dynamic_classes.ipynb

Lines changed: 39 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -119,7 +119,15 @@
119119
"metadata": {},
120120
"outputs": [],
121121
"source": [
122-
"Cat = make_dataclass('Cat', [('species', str, 'cat'), ('nr_legs', int, 4), ('sound', str, 'meow')], bases=(Animal, ))"
122+
"Cat = make_dataclass(\n",
123+
" 'Cat',\n",
124+
" [\n",
125+
" ('species', str, 'cat'),\n",
126+
" ('nr_legs', int, 4),\n",
127+
" ('sound', str, 'meow')\n",
128+
" ],\n",
129+
" bases=(Animal, )\n",
130+
")"
123131
]
124132
},
125133
{
@@ -202,7 +210,15 @@
202210
"metadata": {},
203211
"outputs": [],
204212
"source": [
205-
"Dog = make_dataclass('Dog', [('nr_legs', int, 4), ('species', str, 'dog'), ('sound', str, 'woof')], bases=(Animal, ))"
213+
"Dog = make_dataclass(\n",
214+
" 'Dog',\n",
215+
" [\n",
216+
" ('nr_legs', int, 4),\n",
217+
" ('species', str, 'dog'),\n",
218+
" ('sound', str, 'woof')\n",
219+
" ],\n",
220+
" bases=(Animal, )\n",
221+
")"
206222
]
207223
},
208224
{
@@ -423,7 +439,16 @@
423439
"metadata": {},
424440
"outputs": [],
425441
"source": [
426-
"Animal = type('Animal', tuple(), {'name': None, 'species': None, 'nr_legs': None, '__init__': animal_init})"
442+
"Animal = type(\n",
443+
" 'Animal',\n",
444+
" tuple(),\n",
445+
" {\n",
446+
" 'name': None,\n",
447+
" 'species': None,\n",
448+
" 'nr_legs': None,\n",
449+
" '__init__': animal_init,\n",
450+
" }\n",
451+
")"
427452
]
428453
},
429454
{
@@ -579,7 +604,16 @@
579604
"metadata": {},
580605
"outputs": [],
581606
"source": [
582-
"Cat = type('Cat', (Animal, ), {'name': None, 'species': 'cat', 'nr_legs': 4, 'sound': 'meow'})"
607+
"Cat = type(\n",
608+
" 'Cat',\n",
609+
" (Animal, ),\n",
610+
" {\n",
611+
" 'name': None,\n",
612+
" 'species': 'cat',\n",
613+
" 'nr_legs': 4,\n",
614+
" 'sound': 'meow',\n",
615+
" }\n",
616+
")"
583617
]
584618
},
585619
{
@@ -970,7 +1004,7 @@
9701004
"name": "python",
9711005
"nbconvert_exporter": "python",
9721006
"pygments_lexer": "ipython3",
973-
"version": "3.9.7"
1007+
"version": "3.12.3"
9741008
}
9751009
},
9761010
"nbformat": 4,

source-code/oo_vs_functional.ipynb

Lines changed: 24 additions & 24 deletions
Large diffs are not rendered by default.

source-code/prime_time.ipynb

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -263,7 +263,7 @@
263263
"name": "python",
264264
"nbconvert_exporter": "python",
265265
"pygments_lexer": "ipython3",
266-
"version": "3.9.7"
266+
"version": "3.12.3"
267267
}
268268
},
269269
"nbformat": 4,

source-code/pydantic.ipynb

Lines changed: 132 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -10,7 +10,7 @@
1010
},
1111
{
1212
"cell_type": "code",
13-
"execution_count": 35,
13+
"execution_count": 1,
1414
"id": "b2ba87d0-2447-4a4d-9bae-eb0e86d39ce1",
1515
"metadata": {},
1616
"outputs": [],
@@ -239,6 +239,136 @@
239239
"As you can see, a Python `dataclass` will not perform validation out-of-the-box."
240240
]
241241
},
242+
{
243+
"cell_type": "markdown",
244+
"id": "f9d4e8b1-22b7-4c25-adc0-fb8eb46ed26f",
245+
"metadata": {},
246+
"source": [
247+
"## Default values"
248+
]
249+
},
250+
{
251+
"cell_type": "markdown",
252+
"id": "3b808ae6-0b73-4fb7-b570-593243b15448",
253+
"metadata": {},
254+
"source": [
255+
"Attributes can have default values. For types such as `int` or `str` these are simple constants for non-trivial types you can use `default_factory`."
256+
]
257+
},
258+
{
259+
"cell_type": "code",
260+
"execution_count": 2,
261+
"id": "e00b9f60-5be3-4cea-9dcd-02497911c33e",
262+
"metadata": {},
263+
"outputs": [],
264+
"source": [
265+
"class Agent(BaseModel):\n",
266+
" name: str\n",
267+
" interactive: bool = False\n",
268+
" tools: list[str] = Field(default_factory=list)"
269+
]
270+
},
271+
{
272+
"cell_type": "code",
273+
"execution_count": 7,
274+
"id": "98381d5a-c8ae-4c70-958d-43206747766c",
275+
"metadata": {},
276+
"outputs": [],
277+
"source": [
278+
"agent1 = Agent(name='my_agent')"
279+
]
280+
},
281+
{
282+
"cell_type": "code",
283+
"execution_count": 5,
284+
"id": "c664f054-64fc-4a35-8cd6-77f9429313d4",
285+
"metadata": {},
286+
"outputs": [
287+
{
288+
"data": {
289+
"text/plain": [
290+
"False"
291+
]
292+
},
293+
"execution_count": 5,
294+
"metadata": {},
295+
"output_type": "execute_result"
296+
}
297+
],
298+
"source": [
299+
"agent1.interactive"
300+
]
301+
},
302+
{
303+
"cell_type": "code",
304+
"execution_count": 6,
305+
"id": "f031bf63-9de5-4c96-b153-e510da32f223",
306+
"metadata": {},
307+
"outputs": [
308+
{
309+
"data": {
310+
"text/plain": [
311+
"[]"
312+
]
313+
},
314+
"execution_count": 6,
315+
"metadata": {},
316+
"output_type": "execute_result"
317+
}
318+
],
319+
"source": [
320+
"agent1.tools"
321+
]
322+
},
323+
{
324+
"cell_type": "code",
325+
"execution_count": 9,
326+
"id": "61bf4564-9266-4edd-a55a-3d796daeffa9",
327+
"metadata": {},
328+
"outputs": [],
329+
"source": [
330+
"agent1.tools.append('screwdriver')"
331+
]
332+
},
333+
{
334+
"cell_type": "code",
335+
"execution_count": 8,
336+
"id": "e5b860c1-530b-4cf7-8e79-30806039ce35",
337+
"metadata": {},
338+
"outputs": [],
339+
"source": [
340+
"agent2 = Agent(name='my_other_agent')"
341+
]
342+
},
343+
{
344+
"cell_type": "code",
345+
"execution_count": 10,
346+
"id": "edfcebbe-5a7d-4c1c-b47a-2d8c3ddd54f3",
347+
"metadata": {},
348+
"outputs": [
349+
{
350+
"data": {
351+
"text/plain": [
352+
"[]"
353+
]
354+
},
355+
"execution_count": 10,
356+
"metadata": {},
357+
"output_type": "execute_result"
358+
}
359+
],
360+
"source": [
361+
"agent2.tools"
362+
]
363+
},
364+
{
365+
"cell_type": "markdown",
366+
"id": "094eb08a-1ae8-4d42-9c18-d635ea3fca70",
367+
"metadata": {},
368+
"source": [
369+
"It is clear that using `default_factory` results in the right behavior."
370+
]
371+
},
242372
{
243373
"cell_type": "markdown",
244374
"id": "c31de1d8-2744-48b7-9914-bfeb90cfdac0",
@@ -608,7 +738,7 @@
608738
"name": "python",
609739
"nbconvert_exporter": "python",
610740
"pygments_lexer": "ipython3",
611-
"version": "3.12.7"
741+
"version": "3.12.3"
612742
}
613743
},
614744
"nbformat": 4,

0 commit comments

Comments
 (0)