Skip to content

Commit 379e747

Browse files
authored
Merge pull request animator#117 from drishangupta/main
Have added a section in advanced python dedicated to decorators, kwargs, args
2 parents 229b2a2 + 378dbdd commit 379e747

File tree

2 files changed

+147
-1
lines changed

2 files changed

+147
-1
lines changed
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,146 @@
1+
# Advanced Python
2+
## Functions as First class objects
3+
Functions in Python are so called first class objects, which means they can be treated as variables, viz. functions can be used as arguments or they can be returned using the return keyword.
4+
5+
**Example**
6+
7+
```python
8+
def func1():
9+
def func2():
10+
print("Printing from the inner function, func2")
11+
return func2
12+
13+
```
14+
Assigning func1 to function_call object
15+
```python
16+
function_call=func1()
17+
```
18+
Calling the function
19+
```python
20+
>>> function_call()
21+
```
22+
**Output**
23+
```
24+
Printing from the inner function, func2
25+
```
26+
Here we have seen the use of function as a first class object, func2 was returned as the result of the execution of the outer function, func1.
27+
28+
## *args
29+
\* is an iterating operator used to unpack datatypes such as lists, tuples etc.
30+
**For example**
31+
```python
32+
tuple1=(1,2,4,5,6,7)
33+
print(tuple1)
34+
print(*tuple1)
35+
```
36+
In the above we have defined a tuple called tuple1 with the items (1,2,4,5,6,7).
37+
First we print normally and the output for that is:
38+
```
39+
(1, 2, 4, 5, 6, 7)
40+
41+
```
42+
Then we print with the \* operator, where we will get the output as:
43+
```
44+
1 2 4 5 6 7
45+
```
46+
47+
Here the \* operator has unpacked the tuple, tuple1.
48+
49+
Now that you have understood why \* is used, we can take a look at *args. *args is used in functions so that positional arguments are stored in the variable args. *args is just a naming convention, *anything can be used
50+
*args makes python functions flexible to handle dynamic arguments.
51+
```python
52+
def test1(*args):
53+
print(args)
54+
print(f"The number of elements in args = {len(args)}")
55+
a=list(range(0,10))
56+
test1(*a)
57+
```
58+
In the above snippet, we are sending a list of numbers to the test function which returns the following output:
59+
```
60+
(0, 1, 2, 3, 4, 5, 6, 7, 8, 9)
61+
The number of elements in args = 10
62+
```
63+
If in the test1 we do not use \* in the argument
64+
65+
```python
66+
def test1(*args):
67+
print(args)
68+
print(f"The number of elements in args = {len(args)}")
69+
a=list(range(0,10))
70+
test1(a)
71+
```
72+
we get the following result. This is a tuple containing a list.
73+
```
74+
([0, 1, 2, 3, 4, 5, 6, 7, 8, 9],)
75+
The number of elements in args = 1
76+
```
77+
## **kwargs
78+
**kwargs stands for keyword arguments. This is used for key and value pairs and similar to *args, this makes functions flexible enough to handle dynamic key value pairs in arguments.
79+
```python
80+
def test2(**kwargs):
81+
print(kwargs)
82+
print(f"The number of elements in kwargs = {len(kwargs)}")
83+
test2(a=1,b=2,c=3,d=4,e=5)
84+
```
85+
The above snippet uses some key-value pairs and out test2 function gives the following output:
86+
```
87+
{'a': 1, 'b': 2, 'c': 3, 'd': 4, 'e': 5}
88+
The number of elements in kwargs = 5
89+
```
90+
A dictionary with keys and values is obtained.
91+
92+
## Decorators (@decorators)
93+
Now that we understand what first class object, *args, **kwargs is, we can move to decorators. Decorators are used to perform a task that needs to be performed for existing functions. If some task has to be performed for each function, we can write a function which will perform the task without us having to make changes in each function.
94+
95+
**Sample Code:**
96+
```python
97+
import time
98+
def multiplication(a,b):
99+
start=time.time()
100+
c=a*b
101+
total=time.time()-start
102+
print("Time taken for execution of multiplication",total)
103+
return c
104+
105+
def addition(a,b):
106+
start=time.time()
107+
c=a+b
108+
total=time.time()-start
109+
print("Time taken for execution of addition ",total)
110+
return c
111+
112+
multiplication(4,5)
113+
addition(4,5)
114+
```
115+
116+
In the above code, we had to calculate time and print the execution time seperately for each function leading to repeatation of code. This is where decorators come in handy.
117+
The same functionality can be achieved with the help of a decorator.
118+
119+
**Here's how:**
120+
```python
121+
import time
122+
def time_find(function):
123+
def wrapper(*args, **kwargs):
124+
starttime=time.time()
125+
function(*args, **kwargs)
126+
total=time.time()-starttime
127+
print(f"Time Taken by {function.__name__} to run is ",total)
128+
return wrapper
129+
130+
@time_find #to use a decorator, simply use @<decorator name> above a function.
131+
def multiply(a, b):
132+
print(a*b)
133+
134+
@time_find
135+
def addition(a,b):
136+
print(a+b)
137+
138+
multiply(4,5)
139+
addition(4,5)
140+
```
141+
142+
The above method eleminates redundant code and makes the code cleaner. You may have observed that we have used *args and **kwargs in the wrapper function. This is so that this decorator function is flexible for all types of functions and their parameters and this way it can find out the execution time of any function with as many parameters as needed, we just need to use our decorator @time_find.
143+
144+
145+
146+

contrib/advanced-python/index.md

+1-1
Original file line numberDiff line numberDiff line change
@@ -1,3 +1,3 @@
11
# List of sections
22

3-
- [Section title](filename.md)
3+
- [Decorators/\*args/**kwargs](decorator-kwargs-args.md)

0 commit comments

Comments
 (0)