Skip to content

Commit 16bcd8e

Browse files
committed
Add decorators for timing, debugging, and caching function results; enhance closure examples and add image asset
1 parent 3196be3 commit 16bcd8e

File tree

3 files changed

+163
-0
lines changed

3 files changed

+163
-0
lines changed

Decoraters/decoraters.py

Lines changed: 64 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,64 @@
1+
#Question No 1.
2+
import time
3+
4+
# Decorator to calculate execution time
5+
def timer(func):
6+
def wrapper(*args, **kwargs):
7+
start = time.time() # Record start time
8+
result = func(*args, **kwargs) # Call the actual function
9+
end = time.time() # Record end time
10+
print(f"{func.__name__} ran in {end - start:.4f} seconds")
11+
return result
12+
return wrapper
13+
14+
@timer
15+
def example_function(n):
16+
time.sleep(n) # Simulate a long task
17+
18+
example_function(2) # OUTPUT: example_function ran in 2.00XX seconds
19+
20+
21+
#Question No 2.
22+
# Decorator to debug function call
23+
def debug(func):
24+
def wrapper(*args, **kwargs):
25+
args_value = ', '.join(str(arg) for arg in args)
26+
kwargs_value = ', '.join(f"{k}={v}" for k, v in kwargs.items())
27+
print(f"Calling: {func.__name__} with args ({args_value}) and kwargs {{{kwargs_value}}}")
28+
return func(*args, **kwargs)
29+
return wrapper
30+
31+
@debug
32+
def greet(name, greeting="Hello"):
33+
print(f"{greeting}, {name}!")
34+
35+
greet("chai", greeting="hanji")
36+
# OUTPUT:
37+
# Calling: greet with args (chai) and kwargs {greeting=hanji}
38+
# hanji, chai!
39+
40+
41+
42+
#Question No 3.
43+
import time
44+
45+
# Decorator to cache function results
46+
def cache(func):
47+
cache_value = {} # Dictionary to store previous results
48+
def wrapper(*args):
49+
if args in cache_value:
50+
print(f"Fetching from cache for {args}")
51+
return cache_value[args]
52+
print(f"Computing result for {args}")
53+
result = func(*args)
54+
cache_value[args] = result # Save result to cache
55+
return result
56+
return wrapper
57+
58+
@cache
59+
def long_running_function(a, b):
60+
time.sleep(4) # Simulate expensive computation
61+
return a + b
62+
63+
print(long_running_function(2, 3)) # Takes 4 seconds first time
64+
print(long_running_function(2, 3)) # Instant the second time
Lines changed: 99 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,99 @@
1+
# ✅ Example 1: Global Variable Usage
2+
a = 5
3+
4+
def function():
5+
global a # Use the global 'a' instead of creating a local one
6+
print("Before changing, a =", a) # Should print 5
7+
a = 10 # Update global variable 'a'
8+
print("After changing, a =", a) # Should print 10
9+
10+
function()
11+
print("Global a after function call:", a) # Confirm global 'a' is now 10
12+
13+
14+
# ✅ Example 2: Closure Example – Function Inside a Function
15+
def f1():
16+
x = 88 # This is in the enclosing scope of f2
17+
def f2():
18+
print("Value from closure:", x) # f2 uses x from f1
19+
return f2 # Return the inner function
20+
21+
myResult = f1() # myResult now holds reference to f2, with access to x=88
22+
myResult() # Should print: Value from closure: 88
23+
24+
25+
# ✅ Example 3: Function Factory using Closure
26+
def chaicoder(num): # Outer function with parameter `num`
27+
def actual(x): # Inner function that uses `num` from outer scope
28+
return x ** num
29+
return actual # Return the inner function
30+
31+
f = chaicoder(2) # f becomes a function that squares numbers
32+
g = chaicoder(3) # g becomes a function that cubes numbers
33+
34+
print("Function f (square):", f) # Prints function reference
35+
print("Function g (cube):", g) # Prints function reference
36+
37+
print("f(3) =", f(3)) # 3^2 = 9
38+
print("g(3) =", g(3)) # 3^3 = 27
39+
40+
41+
42+
# Closure to keep track of count
43+
def counter():
44+
count = 0
45+
def increment():
46+
nonlocal count # Use 'nonlocal' to modify the outer function variable
47+
count += 1
48+
return count
49+
return increment
50+
51+
count1 = counter()
52+
print(count1()) # 1
53+
print(count1()) # 2
54+
55+
count2 = counter()
56+
print(count2()) # 1 (separate instance)
57+
print(count1()) # 3 (continues from previous count1)
58+
59+
#Add-On 2: Lambda with Closure
60+
def power_maker(n):
61+
return lambda x: x ** n # Returns an anonymous function that remembers n
62+
63+
square = power_maker(2)
64+
cube = power_maker(3)
65+
66+
print("square(4) =", square(4)) # 16
67+
print("cube(2) =", cube(2)) # 8
68+
69+
70+
#Add-On 3: Function Scope vs Global vs Nonlocal
71+
72+
x = "global"
73+
74+
def outer():
75+
x = "outer"
76+
def inner():
77+
nonlocal x
78+
x = "inner"
79+
print("Inner:", x)
80+
inner()
81+
print("Outer after inner:", x)
82+
83+
outer()
84+
print("Global:", x)
85+
86+
87+
88+
#Add-On 4: Common Mistake in Closures in Loops (Late Binding)
89+
functions = []
90+
91+
for i in range(3):
92+
def make_func(n):
93+
def f():
94+
print(n)
95+
return f
96+
functions.append(make_func(i))
97+
98+
for fn in functions:
99+
fn() # Correct: prints 0, 1, 2

scope_and_clousers/image.png

95.5 KB
Loading

0 commit comments

Comments
 (0)