0% found this document useful (0 votes)
12 views

Function Decorators

Decorator is a function that takes another function as an argument and extends the behavior of the original function. Decorators allow adding new functionality to existing functions without changing the function itself. For example, a decorator could check for certain conditions before calling the decorated function. Decorators are defined using the @syntax and can be chained so that multiple decorators modify a function's behavior sequentially. Generators are functions that produce a sequence of values instead of returning a single value like regular functions. They use the yield keyword to return each value. Generators improve memory efficiency over regular functions by producing values on demand instead of building a large list.

Uploaded by

Ankita Bhadule
Copyright
© © All Rights Reserved
Available Formats
Download as DOCX, PDF, TXT or read online on Scribd
0% found this document useful (0 votes)
12 views

Function Decorators

Decorator is a function that takes another function as an argument and extends the behavior of the original function. Decorators allow adding new functionality to existing functions without changing the function itself. For example, a decorator could check for certain conditions before calling the decorated function. Decorators are defined using the @syntax and can be chained so that multiple decorators modify a function's behavior sequentially. Generators are functions that produce a sequence of values instead of returning a single value like regular functions. They use the yield keyword to return each value. Generators improve memory efficiency over regular functions by producing values on demand instead of building a large list.

Uploaded by

Ankita Bhadule
Copyright
© © All Rights Reserved
Available Formats
Download as DOCX, PDF, TXT or read online on Scribd
You are on page 1/ 8

Function Decorators:

Decorator is a function which can take a function as argument and extend its functionality
and returns modified function with extended functionality.

Input Function Decorator new(add some functionality)


wish()
inner()

Input Decorator Output Function with


Function Function extended Functionality

The main objective of decorator functions is we can extend the functionality of existing
functions without modifies that function.

1) def wish(name):
2) print("Hello",name,"Good Morning")

This function can always print same output for any name

Hello Durga Good


Morning Hello Ravi Good
Morning Hello Sunny
Good Morning

But we want to modify this function to provide different message if name is


Sunny. We can do this without touching wish() function by using
decorator.

Eg:

1) def decor(func):
2) def inner(name):
3) if name=="Sunny":
4) print("Hello Sunny Bad
5) Morning")
else:
6) func(name)
7) return inner
8)
9) @decor
10) def wish(name):
11) print("Hello",name,"Good Morning")
12)
13) wish("Durga")
14) wish("Ravi")
15) wish("Sunny")
16)
17) Output
18) Hello Durga Good Morning
19) Hello Ravi Good Morning
20) Hello Sunny Bad Morning

In the above program whenever we call wish() function automatically decor function
will be executed.

How to call same function with decorator and without decorator:

We should not use @decor

1) def decor(func):
2) def inner(name):
3) if name=="Sunny":
4) print("Hello Sunny Bad
5) Morning")
else:
6) func(name)
7) return inner
8)
9)
10) def wish(name):
11) print("Hello",name,"Good Morning")
12)
13) decorfunction=decor(wish)
14)
15) wish("Durga") #decorator wont be executed
16) wish("Sunny") #decorator wont be executed
17)
18) decorfunction("Durga")#decorator will be executed
19) decorfunction("Sunny")#decorator will be
executed
20)

21) Hello
22) OutputDurga Good Morning

23) Hello Durga


24) Sunny Good
Good Morning
Morning

25) Hello Sunny Bad Morning


Eg 2:

1) def smart_division(func):
2) def inner(a,b):
3) print("We are dividing",a,"with",b)
4) if b==0:
5) print("OOPS...cannot divide")
6) return
7) else:
8) return func(a,b)
9) return inner
10)
11) @smart_division
12) def division(a,b):
13) return a/b
14)
15) print(division(20,2))
16) print(division(20,0))
17)
18) without decorator we will get Error.In this case output is:
19)
20) 10.0
21) Traceback (most recent call last):
22) File "test.py", line 16, in <module>
23) print(division(20,0))
24) File "test.py", line 13, in division
25) return a/b
26) ZeroDivisionError: division by zero

with decorator we won't get any error. In this case output is:

We are dividing 20 with 2


10.0
We are dividing 20 with
0 OOPS...cannot
divide None

Decorator Chaining
We can define multiple decorators for the same function and all these decorators will
form Decorator Chaining.

Eg:
@decor1
@decor
def num():

For num() function we are applying 2 decorator functions. First inner decorator will
work and then outer decorator.

Eg:

1) def decor1(func):
2) def inner():
3) x=func()
4) return x*x
5) return inner
6)
7) def decor(func):
8) def inner():
9) x=func()
10) return 2*x
11) return inner
12)
13) @decor1
14) @decor
15) def num():
16) return 10
17)
18) print(num())

Demo Program for decorator Chaining:


1) def decor(func):
2) def inner(name):
3) print("First Decor(decor) Function Execution")
4) func(name)
5) return inner
6)
7) def decor1(func):
8) def inner(name):
9) print("Second Decor(decor1) Execution")
10) func(name)
11) return inner
12)
13) @decor1
14) @decor
15) def wish(name):
16) print("Hello",name,"Good Morning")
17)
18) wish("Durga")

D:\durgaclasses>py decaratordemo1.py
Second Decor(decor1) Execution
First Decor(decor) Function Execution
Hello Durga Good Morning
Generators
Generator is a function which is responsible to generate a sequence of values.
We can write generator functions just like ordinary functions, but it uses yield keyword to
return values.

Generator
A Sequence of Values
Function

yield

Eg 1:

1) def mygen():
2) yield 'A'
3) yield 'B'
4) yield 'C'
5)
6) g=mygen()
7) print(type(g))
8)
9) print(next(g))
10) print(next(g))
11) print(next(g))
12) print(next(g))
13)
14) Output
15) <class 'generator'>
16) A
17) B
18) C
19) Traceback (most recent call last):
20) File "test.py", line 12, in <module>
21) print(next(g))
22) StopIteration

Eg 2:

1) def countdown(num):
2) print("Start Countdown")
3) while(num>0):
4) yield num
5) num=num-1
6)
7) values=countdown(5)
8) for x in values:
9) print(x)
10)
11) Output
12) Start Countdown
13) 5
14)
4 3
15)
16)
2 1
17)

Eg 3: To generate first n numbers:

1) def firstn(num):
2) n=1
3) while n<=num:
4) yield n
5) n=n+1
6)
7) values=firstn(5)
8) for x in values:
9) print(x)
10)
11) Output
12) 1
13) 2
14) 3
15) 4
16) 5

We can convert generator into list as


follows: values=firstn(10)
l1=list(values)
print(l1) #[1, 2, 3, 4, 5, 6, 7, 8, 9, 10]
Eg 4: To generate Fibonacci Numbers...

The next is the sum of previous 2 numbers

Eg: 0,1,1,2,3,5,8,13,21,...
10)
11) Output
12) 0
13) 1
14) 1
15) 2
16) 3
17) 5
18) 8
19) 13
20) 21
21) 34
22) 55
23) 89

Advantages of Generator Functions:


1. when compared with class level iterators, generators are very easy to use
2. Improves memory utilization and performance.
3. Generators are best suitable for reading data from large number of large files
4. Generators work great for web scraping and crawling.

Generators vs Normal Collections wrt


performance:
1) import random
2) import time
3)
4) names = ['Sunny','Bunny','Chinny','Vinny']
5) subjects =
['Python','Java','Blockchain']
6)

7) defresults
8) people_list(num_people):
= []

9)
10) forperson
i in range(num_people):
=
11) { 'id':i,
12) 'name': random.choice(names),
13) 'subject':random.choice(subjects)
14) }
15) results.append(person)
16) return results
17)
18) def people_generator(num_people):
19) for i in
range(num_people):
20) person =
21) { 'id':i,
22) 'name': random.choice(names),
23) 'major':random.choice(subjects)
24) }
25) yield person
26)
27) '''''t1 = time.clock()
28) people = people_list(10000000)
29) t2 = time.clock()'''
30)
31) t1 = time.clock()
32) people = people_generator(10000000)
33) t2 = time.clock()
34)
35) print('Took {}'.format(t2-t1))

Note: In the above program observe the differnce wrt execution time by
using list and generators

Generators vs Normal Collections wrt Memory


Utilization:
Normal Collection:
l=[x*x for x in
range(10000000000000000)]
print(l[0])

We will get MemoryError in this case because all these values are required to
store in the memory.

Generators:
g=(x*x for x in
range(10000000000000000))
print(next(g))

Output: 0
We won't get any MemoryErr

You might also like