Skip to content

Commit ca13136

Browse files
update file
1 parent 8d89039 commit ca13136

File tree

1 file changed

+300
-0
lines changed

1 file changed

+300
-0
lines changed

Decorators.ipynb

Lines changed: 300 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,300 @@
1+
{
2+
"cells": [
3+
{
4+
"cell_type": "markdown",
5+
"id": "57d57c9e",
6+
"metadata": {},
7+
"source": [
8+
"#### 🧩 Python Decorators & First-Class Functions\n",
9+
"\n",
10+
"---\n",
11+
"\n",
12+
"#### 1. What are Decorators?\n",
13+
"\n",
14+
"Decorators in Python are **functions that modify the behavior of other functions** without changing their code. They allow us to extend or enhance functions in a clean and reusable way.\n",
15+
"\n",
16+
"##### 🔑 Key Features:\n",
17+
"- Allow code reusability.\n",
18+
"- Improve code readability.\n",
19+
"- Commonly used for:\n",
20+
" - Logging\n",
21+
" - Authentication\n",
22+
" - Measuring execution time\n",
23+
" - Access control\n",
24+
"\n",
25+
"---\n",
26+
"\n",
27+
"#### 2. Why Do We Use Decorators?\n",
28+
"\n",
29+
"Decorators help us **avoid code duplication** by adding extra functionality **without modifying the core function logic**.\n",
30+
"\n",
31+
"#### 💡 Common Use Cases:\n",
32+
"- Logging function execution\n",
33+
"- Measuring execution time\n",
34+
"- Checking user authentication before executing a function\n",
35+
"\n",
36+
"---\n",
37+
"\n",
38+
"#### 3. Functions as First-Class Citizens\n",
39+
"\n",
40+
"In Python, functions are treated like **first-class citizens**, meaning they can:\n",
41+
"- Be **assigned** to a variable\n",
42+
"- Be **passed** as an argument to another function\n",
43+
"- Be **returned** from another function\n",
44+
"\n",
45+
"#### 🧪 Example: Passing a Function as an Argument"
46+
]
47+
},
48+
{
49+
"cell_type": "code",
50+
"execution_count": 5,
51+
"id": "1eb196a4",
52+
"metadata": {},
53+
"outputs": [
54+
{
55+
"name": "stdout",
56+
"output_type": "stream",
57+
"text": [
58+
"Hello\n"
59+
]
60+
}
61+
],
62+
"source": [
63+
"def greet():\n",
64+
" return \"Hello\"\n",
65+
"\n",
66+
"def call_function(func):\n",
67+
" return func() # Provide the required argument\n",
68+
"# Pass the greet function as an argument\n",
69+
"result = call_function(greet)\n",
70+
"print(result) # Output: Hello, Alice!"
71+
]
72+
},
73+
{
74+
"cell_type": "markdown",
75+
"id": "708a9a5e",
76+
"metadata": {},
77+
"source": [
78+
"#### 4. 🧱 Basic Structure of a Decorator\n",
79+
"\n",
80+
"A **decorator** is a function that wraps another function to modify or extend its behavior without permanently changing it.\n",
81+
"\n",
82+
"### 📝 Syntax:\n",
83+
"\n",
84+
"```python\n",
85+
"def decorator_function(original_function):\n",
86+
" def wrapper_function():\n",
87+
" # Add extra functionality\n",
88+
" return original_function()\n",
89+
" return wrapper_function\n"
90+
]
91+
},
92+
{
93+
"cell_type": "code",
94+
"execution_count": 13,
95+
"id": "8debd928",
96+
"metadata": {},
97+
"outputs": [
98+
{
99+
"name": "stdout",
100+
"output_type": "stream",
101+
"text": [
102+
"Calling function: say_hello\n",
103+
"Hello!\n"
104+
]
105+
}
106+
],
107+
"source": [
108+
"## 💡 Example: A Simple Decorator That Logs Function Calls\n",
109+
"# This example demonstrates how to create a simple decorator that logs function calls.\n",
110+
"\n",
111+
"\n",
112+
"# Define a decorator function that takes another function as an argument\n",
113+
"def log_decorator(func):\n",
114+
" # Define a wrapper function that will modify the behavior of the original function\n",
115+
" def wrapper():\n",
116+
" print(f'Calling function: {func.__name__}') # Print the name of the function being called\n",
117+
" return func() # Call the original function\n",
118+
" return wrapper # Return the modified function\n",
119+
"\n",
120+
"# Apply the decorator to the 'say_hello' function using @log_decorator\n",
121+
"@log_decorator\n",
122+
"def say_hello():\n",
123+
" print(\"Hello!\") # This function simply prints \"Hello!\"\n",
124+
"\n",
125+
"# Call the decorated function\n",
126+
"say_hello()\n",
127+
"# Decorator to measure consultation time\n",
128+
"def timer_decorator(func):\n",
129+
" def wrapper(patient_name):\n",
130+
" start = time.time() # Record start time\n",
131+
" result = func(patient_name) # Call the original function\n",
132+
" end = time.time() # Record end time\n",
133+
" print(f\"Consultation time: {end - start:.4f} seconds\")\n",
134+
" return result\n",
135+
" return wrapper"
136+
]
137+
},
138+
{
139+
"cell_type": "markdown",
140+
"id": "6cd4d24d",
141+
"metadata": {},
142+
"source": [
143+
"##### 5. Using Multiple Decorators\n",
144+
"\n",
145+
"Multiple decorators can be applied by **stacking them on top of a function**.\n",
146+
"\n",
147+
"##### 💡 Example Use Case: Checking Consultation Time \n",
148+
"*(Timing how long it takes)*\n",
149+
"\n",
150+
"Instead of making every doctor manually do these extra steps, a hospital can create a system that **automatically ensures every doctor follows these steps** — this is what a **decorator** does in programming!\n",
151+
"\n",
152+
"Decorators help wrap extra functionality around functions in a clean and reusable way.\n"
153+
]
154+
},
155+
{
156+
"cell_type": "code",
157+
"execution_count": 14,
158+
"id": "ac742f91",
159+
"metadata": {},
160+
"outputs": [
161+
{
162+
"name": "stdout",
163+
"output_type": "stream",
164+
"text": [
165+
"Patient details logged for Alice\n",
166+
"Doctor is consulting Alice\n",
167+
"Prescription given to Alice\n",
168+
"Consultation time: 1.0117 seconds\n"
169+
]
170+
}
171+
],
172+
"source": [
173+
"import time\n",
174+
"\n",
175+
"# Decorator to log patient details\n",
176+
"def log_decorator(func):\n",
177+
" def wrapper(patient_name):\n",
178+
" print(f\"Patient details logged for {patient_name}\")\n",
179+
" return func(patient_name)\n",
180+
" return wrapper\n",
181+
"\n",
182+
"# Decorator to measure consultation time\n",
183+
"def timer_decorator(func):\n",
184+
" def wrapper(patient_name):\n",
185+
" start = time.time() # Record start time\n",
186+
" result = func(patient_name) # Call the original function\n",
187+
" end = time.time() # Record end time\n",
188+
" print(f\"Consultation time: {end - start:.4f} seconds\")\n",
189+
" return result\n",
190+
" return wrapper\n",
191+
"\n",
192+
"# Applying decorators to the doctor's consultation function\n",
193+
"@log_decorator # Log patient details\n",
194+
"@timer_decorator # Measure consultation time\n",
195+
"def doctor_consultation(patient_name):\n",
196+
" print(f\"Doctor is consulting {patient_name}\")\n",
197+
" time.sleep(1) # Simulate consultation time\n",
198+
" print(f\"Prescription given to {patient_name}\")\n",
199+
"\n",
200+
"# Simulating a patient visiting the doctor\n",
201+
"doctor_consultation(\"Alice\")\n"
202+
]
203+
},
204+
{
205+
"cell_type": "markdown",
206+
"id": "61771d2f",
207+
"metadata": {},
208+
"source": [
209+
"#### 6. Passing Arguments to Decorators:\n",
210+
"\n",
211+
"Extra Function for Argument Passing:\n",
212+
"\n",
213+
"A decorator can take arguments, for example, to repeat a function multiple times:"
214+
]
215+
},
216+
{
217+
"cell_type": "code",
218+
"execution_count": 15,
219+
"id": "753e6eba",
220+
"metadata": {},
221+
"outputs": [
222+
{
223+
"name": "stdout",
224+
"output_type": "stream",
225+
"text": [
226+
"Hello!\n",
227+
"Hello!\n",
228+
"Hello!\n"
229+
]
230+
}
231+
],
232+
"source": [
233+
"\n",
234+
"\n",
235+
"### 🎯 Goal: Repeat a function multiple times using a decorator\n",
236+
"\n",
237+
"\n",
238+
"# Outer function that takes a parameter 'times' (how many times to repeat)\n",
239+
"def repeat(times):\n",
240+
" # Inner function that acts as the actual decorator\n",
241+
" def decorator(func):\n",
242+
" # Wrapper function that modifies the behavior of 'func'\n",
243+
" def wrapper(*args, **kwargs):\n",
244+
" for _ in range(times): # Loop 'times' number of times\n",
245+
" func(*args, **kwargs) # Call the original function\n",
246+
" return wrapper # Return the modified function\n",
247+
" return decorator # Return the decorator function\n",
248+
"\n",
249+
"# Applying the 'repeat' decorator with 'times = 3'\n",
250+
"@repeat(3)\n",
251+
"def greet():\n",
252+
" print(\"Hello!\")\n",
253+
"\n",
254+
"# Calling 'greet()' will now run 3 times due to the decorator\n",
255+
"greet()\n"
256+
]
257+
},
258+
{
259+
"cell_type": "markdown",
260+
"id": "b237b8a6",
261+
"metadata": {},
262+
"source": [
263+
"## ✅ Best Practices for Decorators\n",
264+
"\n",
265+
"- Use decorators to **extend functionality** without modifying the original code.\n",
266+
"- Preserve function metadata using `functools.wraps()`.\n",
267+
"- Avoid unnecessary nesting of decorators to keep the code **readable and maintainable**.\n",
268+
"\n",
269+
"---\n",
270+
"\n",
271+
"## 🧠 Conclusion\n",
272+
"\n",
273+
"Decorators provide a **clean, reusable** way to add additional functionality to functions, such as **logging** or **timing**, without changing the core function.\n",
274+
"\n",
275+
"They are an essential concept for writing **cleaner**, more **modular** code in Python — especially when the same functionality needs to be applied to **multiple functions**.\n"
276+
]
277+
}
278+
],
279+
"metadata": {
280+
"kernelspec": {
281+
"display_name": "Python 3",
282+
"language": "python",
283+
"name": "python3"
284+
},
285+
"language_info": {
286+
"codemirror_mode": {
287+
"name": "ipython",
288+
"version": 3
289+
},
290+
"file_extension": ".py",
291+
"mimetype": "text/x-python",
292+
"name": "python",
293+
"nbconvert_exporter": "python",
294+
"pygments_lexer": "ipython3",
295+
"version": "3.10.16"
296+
}
297+
},
298+
"nbformat": 4,
299+
"nbformat_minor": 5
300+
}

0 commit comments

Comments
 (0)