Skip to content

Commit c3464ae

Browse files
Merge branch 'animator:main' into Ramya-korupolu-tree-traversal
2 parents 937f04f + dc42be5 commit c3464ae

Some content is hidden

Large Commits have some content hidden by default. Use the searchbox below for content that may be hidden.

49 files changed

+3114
-2
lines changed
Lines changed: 110 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,110 @@
1+
## Asynchronous Context Managers and Generators in Python
2+
Asynchronous programming in Python allows for more efficient use of resources by enabling tasks to run concurrently. Python provides support for asynchronous
3+
context managers and generators, which help manage resources and perform operations asynchronously.
4+
5+
### Asynchronous Context Managers
6+
Asynchronous context managers are similar to regular context managers but are designed to work with asynchronous code. They use the async with statement and
7+
typically include the '__aenter__' and '__aexit__' methods.
8+
9+
### Creating an Asynchronous Context Manager
10+
Here's a simple example of an asynchronous context manager:
11+
12+
```bash
13+
import asyncio
14+
15+
class AsyncContextManager:
16+
async def __aenter__(self):
17+
print("Entering context")
18+
await asyncio.sleep(1) # Simulate an async operation
19+
return self
20+
21+
async def __aexit__(self, exc_type, exc, tb):
22+
print("Exiting context")
23+
await asyncio.sleep(1) # Simulate cleanup
24+
25+
async def main():
26+
async with AsyncContextManager() as acm:
27+
print("Inside context")
28+
29+
asyncio.run(main())
30+
```
31+
32+
Output:
33+
34+
```bash
35+
Entering context
36+
Inside context
37+
Exiting context
38+
```
39+
40+
### Asynchronous Generators
41+
Asynchronous generators allow you to yield values within an asynchronous function. They use the async def syntax along with the yield statement and are
42+
iterated using the async for loop.
43+
44+
### Creating an Asynchronous Generator
45+
Here's a basic example of an asynchronous generator:
46+
47+
```bash
48+
import asyncio
49+
50+
async def async_generator():
51+
for i in range(5):
52+
await asyncio.sleep(1) # Simulate an async operation
53+
yield i
54+
55+
async def main():
56+
async for value in async_generator():
57+
print(value)
58+
59+
asyncio.run(main())
60+
```
61+
Output:
62+
```bash
63+
0
64+
1
65+
2
66+
3
67+
4
68+
```
69+
### Combining Asynchronous Context Managers and Generators
70+
You can combine asynchronous context managers and generators to create more complex and efficient asynchronous workflows.
71+
Example: Fetching Data with an Async Context Manager and Generator
72+
Consider a scenario where you need to fetch data from an API asynchronously and manage the connection using an asynchronous context manager:
73+
```bash
74+
import aiohttp
75+
import asyncio
76+
77+
class AsyncHTTPClient:
78+
def __init__(self, url):
79+
self.url = url
80+
81+
async def __aenter__(self):
82+
self.session = aiohttp.ClientSession()
83+
self.response = await self.session.get(self.url)
84+
return self.response
85+
86+
async def __aexit__(self, exc_type, exc, tb):
87+
await self.response.release()
88+
await self.session.close()
89+
90+
async def async_fetch(urls):
91+
for url in urls:
92+
async with AsyncHTTPClient(url) as response:
93+
data = await response.text()
94+
yield data
95+
96+
async def main():
97+
urls = ["http://example.com", "http://example.org", "http://example.net"]
98+
async for data in async_fetch(urls):
99+
print(data)
100+
101+
asyncio.run(main())
102+
```
103+
### Benefits of Asynchronous Context Managers and Generators
104+
1. Efficient Resource Management: They help manage resources like network connections or file handles more efficiently by releasing them as soon as they are no longer needed.
105+
2. Concurrency: They enable concurrent operations, improving performance in I/O-bound tasks such as network requests or file I/O.
106+
3. Readability and Maintainability: They provide a clear and structured way to handle asynchronous operations, making the code easier to read and maintain.
107+
### Summary
108+
Asynchronous context managers and generators are powerful tools in Python that enhance the efficiency and readability
109+
of asynchronous code. By using 'async with' for resource management and 'async for' for iteration, you can write more performant and maintainable asynchronous
110+
programs.

contrib/advanced-python/index.md

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -19,3 +19,5 @@
1919
- [List Comprehension](list-comprehension.md)
2020
- [Eval Function](eval_function.md)
2121
- [Magic Methods](magic-methods.md)
22+
- [Asynchronous Context Managers & Generators](asynchronous-context-managers-generators.md)
23+
- [Threading](threading.md)

contrib/advanced-python/threading.md

Lines changed: 198 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,198 @@
1+
# Threading in Python
2+
Threading is a sequence of instructions in a program that can be executed independently of the remaining process and
3+
Threads are like lightweight processes that share the same memory space but can execute independently.
4+
The process is an executable instance of a computer program.
5+
This guide provides an overview of the threading module and its key functionalities.
6+
7+
## Key Characteristics of Threads:
8+
* Shared Memory: All threads within a process share the same memory space, which allows for efficient communication between threads.
9+
* Independent Execution: Each thread can run independently and concurrently.
10+
* Context Switching: The operating system can switch between threads, enabling concurrent execution.
11+
12+
## Threading Module
13+
This module will allows you to create and manage threads easily. This module includes several functions and classes to work with threads.
14+
15+
**1. Creating Thread:**
16+
To create a thread in Python, you can use the Thread class from the threading module.
17+
18+
Example:
19+
```python
20+
import threading
21+
22+
# Create a thread
23+
thread = threading.Thread()
24+
25+
# Start the thread
26+
thread.start()
27+
28+
# Wait for the thread to complete
29+
thread.join()
30+
31+
print("Thread has finished execution.")
32+
```
33+
Output :
34+
```
35+
Thread has finished execution.
36+
```
37+
**2. Performing Task with Thread:**
38+
We can also perform a specific task by thread by giving a function as target and its argument as arg ,as a parameter to Thread object.
39+
40+
Example:
41+
42+
```python
43+
import threading
44+
45+
# Define a function that will be executed by the thread
46+
def print_numbers(arg):
47+
for i in range(arg):
48+
print(f"Thread: {i}")
49+
# Create a thread
50+
thread = threading.Thread(target=print_numbers,args=(5,))
51+
52+
# Start the thread
53+
thread.start()
54+
55+
# Wait for the thread to complete
56+
thread.join()
57+
58+
print("Thread has finished execution.")
59+
```
60+
Output :
61+
```
62+
Thread: 0
63+
Thread: 1
64+
Thread: 2
65+
Thread: 3
66+
Thread: 4
67+
Thread has finished execution.
68+
```
69+
**3. Delaying a Task with Thread's Timer Function:**
70+
We can set a time for which we want a thread to start. Timer function takes 4 arguments (interval,function,args,kwargs).
71+
72+
Example:
73+
```python
74+
import threading
75+
76+
# Define a function that will be executed by the thread
77+
def print_numbers(arg):
78+
for i in range(arg):
79+
print(f"Thread: {i}")
80+
# Create a thread after 3 seconds
81+
thread = threading.Timer(3,print_numbers,args=(5,))
82+
83+
# Start the thread
84+
thread.start()
85+
86+
# Wait for the thread to complete
87+
thread.join()
88+
89+
print("Thread has finished execution.")
90+
```
91+
Output :
92+
```
93+
# after three second output will be generated
94+
Thread: 0
95+
Thread: 1
96+
Thread: 2
97+
Thread: 3
98+
Thread: 4
99+
Thread has finished execution.
100+
```
101+
**4. Creating Multiple Threads**
102+
We can create and manage multiple threads to achieve concurrent execution.
103+
104+
Example:
105+
```python
106+
import threading
107+
108+
def print_numbers(thread_name):
109+
for i in range(5):
110+
print(f"{thread_name}: {i}")
111+
112+
# Create multiple threads
113+
thread1 = threading.Thread(target=print_numbers, args=("Thread 1",))
114+
thread2 = threading.Thread(target=print_numbers, args=("Thread 2",))
115+
116+
# Start the threads
117+
thread1.start()
118+
thread2.start()
119+
120+
# Wait for both threads to complete
121+
thread1.join()
122+
thread2.join()
123+
124+
print("Both threads have finished execution.")
125+
```
126+
Output :
127+
```
128+
Thread 1: 0
129+
Thread 1: 1
130+
Thread 2: 0
131+
Thread 1: 2
132+
Thread 1: 3
133+
Thread 2: 1
134+
Thread 2: 2
135+
Thread 2: 3
136+
Thread 2: 4
137+
Thread 1: 4
138+
Both threads have finished execution.
139+
```
140+
141+
**5. Thread Synchronization**
142+
When we create multiple threads and they access shared resources, there is a risk of race conditions and data corruption. To prevent this, you can use synchronization primitives such as locks.
143+
A lock is a synchronization primitive that ensures that only one thread can access a shared resource at a time.
144+
145+
Example:
146+
```Python
147+
import threading
148+
149+
lock = threading.Lock()
150+
151+
def print_numbers(thread_name):
152+
for i in range(10):
153+
with lock:
154+
print(f"{thread_name}: {i}")
155+
156+
# Create multiple threads
157+
thread1 = threading.Thread(target=print_numbers, args=("Thread 1",))
158+
thread2 = threading.Thread(target=print_numbers, args=("Thread 2",))
159+
160+
# Start the threads
161+
thread1.start()
162+
thread2.start()
163+
164+
# Wait for both threads to complete
165+
thread1.join()
166+
thread2.join()
167+
168+
print("Both threads have finished execution.")
169+
```
170+
Output :
171+
```
172+
Thread 1: 0
173+
Thread 1: 1
174+
Thread 1: 2
175+
Thread 1: 3
176+
Thread 1: 4
177+
Thread 1: 5
178+
Thread 1: 6
179+
Thread 1: 7
180+
Thread 1: 8
181+
Thread 1: 9
182+
Thread 2: 0
183+
Thread 2: 1
184+
Thread 2: 2
185+
Thread 2: 3
186+
Thread 2: 4
187+
Thread 2: 5
188+
Thread 2: 6
189+
Thread 2: 7
190+
Thread 2: 8
191+
Thread 2: 9
192+
Both threads have finished execution.
193+
```
194+
195+
A ```lock``` object is created using threading.Lock() and The ```with lock``` statement ensures that the lock is acquired before printing and released after printing. This prevents other threads from accessing the print statement simultaneously.
196+
197+
## Conclusion
198+
Threading in Python is a powerful tool for achieving concurrency and improving the performance of I/O-bound tasks. By understanding and implementing threads using the threading module, you can enhance the efficiency of your programs. To prevent race situations and maintain data integrity, keep in mind that thread synchronization must be properly managed.

0 commit comments

Comments
 (0)