Last chance! 50% off DataCamp Premium
Sale ends in
asyncio मॉड्यूल को Python 3.4 में एक प्रावधिक पैकेज के रूप में जोड़ा गया था। इसका अर्थ यह है कि भविष्य के Python रिलीज़ में asyncio में पीछे-संगत न रहने वाले बदलाव आ सकते हैं या इसे हटाया भी जा सकता है।
प्रलेखन के अनुसार, asyncio “coroutines का उपयोग करके single-threaded concurrent कोड लिखने, sockets और अन्य संसाधनों पर I/O एक्सेस को multiplex करने, नेटवर्क क्लाइंट और सर्वर चलाने और अन्य संबंधित प्रिमिटिव उपलब्ध कराने का ढांचा प्रदान करता है।” यह अध्याय asyncio से संबंधित हर चीज़ को कवर करने के लिए नहीं है, लेकिन आप यह अवश्य सीखेंगे कि इस मॉड्यूल का उपयोग कैसे करें और यह उपयोगी क्यों है।
यदि आपको Python के पुराने संस्करण में asyncio जैसा कुछ चाहिए, तो आप Twisted या gevent को देख सकते हैं।
asyncio मॉड्यूल एक ऐसे फ्रेमवर्क की सुविधा देता है जो event loop के इर्द-गिर्द घूमता है। एक event loop मूलतः किसी घटना के होने का इंतजार करता है और फिर उस पर कार्रवाई करता है। यह I/O और सिस्टम इवेंट जैसे कार्यों को संभालने के लिए जिम्मेदार होता है।
वास्तव में asyncio के पास event loop के कई इम्प्लीमेंटेशन उपलब्ध हैं। मॉड्यूल डिफ़ॉल्ट रूप से उस इम्प्लीमेंटेशन को चुनता है जो उसके चलने वाले ऑपरेटिंग सिस्टम के लिए सबसे कुशल होने की संभावना रखता है; हालाँकि, यदि आप चाहें तो आप स्पष्ट रूप से event loop भी चुन सकते हैं। एक event loop मूल रूप से कहता है “जब घटना A हो, तो फ़ंक्शन B से प्रतिक्रिया दो।”
किसी सर्वर की कल्पना करें जो किसी के आने और वेब पेज जैसे किसी संसाधन के लिए अनुरोध करने का इंतजार करता है। यदि वेबसाइट बहुत लोकप्रिय नहीं है, तो सर्वर लंबे समय तक निष्क्रिय रहेगा। लेकिन जैसे ही अनुरोध मिलता है, सर्वर को प्रतिक्रिया देनी होती है। इस प्रतिक्रिया को event handling कहा जाता है। जब कोई उपयोगकर्ता वेब पेज लोड करता है, तो सर्वर एक या अधिक event handlers की जाँच करता है और उन्हें कॉल करता है। एक बार जब वे event handlers पूरा हो जाते हैं, तो उन्हें नियंत्रण वापस event loop को देना होता है। Python में यह करने के लिए, asyncio coroutines का उपयोग करता है।
Coroutine एक विशेष प्रकार का फ़ंक्शन है जो अपनी अवस्था खोए बिना नियंत्रण अपने कॉलर को वापस दे सकता है। Coroutine एक generator का उपभोक्ता और विस्तार है। Threads की तुलना में उनका एक बड़ा लाभ यह है कि उन्हें निष्पादन के लिए बहुत कम मेमोरी चाहिए। ध्यान दें कि जब आप किसी coroutine फ़ंक्शन को कॉल करते हैं, तो वह वास्तव में निष्पादित नहीं होता। इसके बजाय, यह एक coroutine ऑब्जेक्ट लौटाता है जिसे आप event loop को पास कर सकते हैं ताकि वह इसे तुरंत या बाद में चलाए।
asyncio मॉड्यूल का उपयोग करते समय आपको एक और शब्द संभवतः मिलेगा: future। Future मूलतः एक ऐसा ऑब्जेक्ट होता है जो उस काम के परिणाम का प्रतिनिधित्व करता है जो अभी पूरा नहीं हुआ है। आपका event loop future ऑब्जेक्ट्स पर नज़र रख सकता है और उनके पूरा होने का इंतजार कर सकता है। जब कोई future पूरा हो जाता है, तो उसे done पर सेट कर दिया जाता है। Asyncio locks और semaphores का भी समर्थन करता है।
अंत में, Task का उल्लेख करना आवश्यक है। Task एक coroutine का wrapper और Future की subclass है। आप किसी Task को event loop का उपयोग करके schedule भी कर सकते हैं।
async और awaitPython 3.5 में async और await कीवर्ड्स जोड़े गए थे ताकि native coroutine परिभाषित किए जा सकें और उन्हें generator-आधारित coroutine की तुलना में एक पृथक प्रकार बनाया जा सके। यदि आप async और await का गहन विवरण चाहते हैं, तो PEP 492 देखें।
Python 3.4 में आप इस तरह coroutine बनाते थे:
यह डेकोरेटर Python 3.5 में अब भी काम करता है, लेकिन types मॉड्यूल में coroutine फ़ंक्शन के रूप में एक अपडेट आया, जो अब आपको यह बता देगा कि आप जिस वस्तु के साथ इंटरैक्ट कर रहे हैं, वह native coroutine है या नहीं। Python 3.5 से, आप सिंटैक्स के रूप में coroutine फ़ंक्शन परिभाषित करने के लिए async def का उपयोग कर सकते हैं।
तो ऊपर वाला फ़ंक्शन इस तरह दिखेगा:
जब आप इस तरीके से coroutine परिभाषित करते हैं, तो आप coroutine फ़ंक्शन के अंदर yield का उपयोग नहीं कर सकते। इसके बजाय इसमें return या await स्टेटमेंट होना चाहिए, जिनका उपयोग कॉलर को मान वापस करने के लिए किया जाता है। ध्यान दें कि await कीवर्ड का उपयोग केवल async def फ़ंक्शन के अंदर ही किया जा सकता है।
async/await कीवर्ड्स को asynchronous प्रोग्रामिंग के लिए एक API माना जा सकता है। asyncio मॉड्यूल केवल एक फ्रेमवर्क है जो संयोगवश asynchronous प्रोग्रामिंग के लिए async/await का उपयोग करता है। वास्तव में curio नामक एक प्रोजेक्ट है जो इस अवधारणा को सिद्ध करता है क्योंकि यह event loop का एक अलग इम्प्लीमेंटेशन है जो अंदरूनी तौर पर async/await का उपयोग करता है।
जहाँ यह जानना उपयोगी है कि यह सब कैसे काम करता है, वहीं कभी-कभी आप सिर्फ कुछ उदाहरण देखना चाहते हैं ताकि सिंटैक्स और चीज़ों को जोड़ने का तरीका समझ सकें।
इसी सोच के साथ, आइए एक साधारण उदाहरण से शुरू करें!
एक आम कार्य जो आप अक्सर करना चाहेंगे, वह है किसी स्थान से फ़ाइल डाउनलोड करना, चाहे वह कोई आंतरिक संसाधन हो या इंटरनेट पर कोई फ़ाइल। आमतौर पर आप एक से अधिक फ़ाइल डाउनलोड करना चाहेंगे।
तो आइए ऐसी दो coroutines बनाएँ जो यह कर सकें:
import asyncio
import os
import urllib.request
async def download_coroutine(url):
#"A coroutine to download the specified url"
request = urllib.request.urlopen(url)
filename = os.path.basename(url)
with open(filename, 'wb') as file_handle:
while True:
chunk = request.read(1024)
if not chunk:
break
file_handle.write(chunk)
msg = 'Finished downloading {filename}'.format(filename=filename)
return msg
async def main(urls):
"""
Creates a group of coroutines and waits for them to finish
"""
coroutines = [download_coroutine(url) for url in urls]
completed, pending = await asyncio.wait(coroutines)
for item in completed:
print(item.result())
if __name__ == '__main__':
urls = ["http://www.irs.gov/pub/irs-pdf/f1040.pdf",
"http://www.irs.gov/pub/irs-pdf/f1040a.pdf",
"http://www.irs.gov/pub/irs-pdf/f1040ez.pdf",
"http://www.irs.gov/pub/irs-pdf/f1040es.pdf",
"http://www.irs.gov/pub/irs-pdf/f1040sb.pdf"]
event_loop = asyncio.get_event_loop()
try:
event_loop.run_until_complete(main(urls))
finally:
event_loop.close()
इस कोड में, हम आवश्यक मॉड्यूल्स आयात करते हैं और फिर async सिंटैक्स का उपयोग करके अपना पहला coroutine बनाते हैं। इस coroutine का नाम download_coroutine है और यह Python के urllib का उपयोग करके उसे पास किए गए URL को डाउनलोड करता है। जब यह पूरा हो जाता है, तो यह एक संदेश लौटाता है जो इसके पूरा होने की सूचना देता है।
दूसरा coroutine हमारा मुख्य coroutine है। यह मूल रूप से एक या अधिक URLs की सूची लेता है और उन्हें कतारबद्ध करता है। हम coroutines के पूरा होने की प्रतीक्षा करने के लिए asyncio के wait फ़ंक्शन का उपयोग करते हैं। बेशक, वास्तव में coroutines शुरू करने के लिए, उन्हें event loop में जोड़ा जाना चाहिए। हम यही काम सबसे अंत में करते हैं जहाँ हम event loop प्राप्त करते हैं और फिर उसके run_until_complete मेथड को कॉल करते हैं। आप देखेंगे कि हम event loop को मुख्य coroutine पास करते हैं। यह मुख्य coroutine को चलाना शुरू करता है, जो दूसरे coroutine को कतार में लगाता है और उसे शुरू कर देता है। इसे chained coroutine कहा जाता है।
इस उदाहरण की समस्या यह है कि यह वास्तव में coroutine है ही नहीं। कारण यह है कि download_coroutine फ़ंक्शन asynchronous नहीं है। समस्या यह है कि urllib asynchronous नहीं है और आगे, मैं await या yield का भी उपयोग नहीं कर रहा हूँ। इसे करने का बेहतर तरीका aiohttp पैकेज का उपयोग करना होगा। आइए अगले भाग में उसे देखें!
aiohttp पैकेज asynchronous HTTP क्लाइंट और सर्वर बनाने के लिए बनाया गया है। आप इसे pip से इस प्रकार इंस्टॉल कर सकते हैं:
pip install aiohttp
इंस्टॉल होने के बाद, आइए अपना कोड aiohttp का उपयोग करने के लिए अपडेट करें ताकि हम फ़ाइलें डाउनलोड कर सकें:
आप देखेंगे कि हमने कुछ नई चीज़ें आयात की हैं: aiohttp और async_timeout। बाद वाला वास्तव में aiohttp की एक निर्भरता है और हमें timeout context manager बनाने देता है। आइए कोड के सबसे नीचे से शुरू करें और ऊपर की ओर बढ़ें। सबसे नीचे वाले conditional में, हम अपना asynchronous event loop शुरू करते हैं और मुख्य फ़ंक्शन को कॉल करते हैं।
मुख्य फ़ंक्शन में, हम एक ClientSession ऑब्जेक्ट बनाते हैं जिसे हम प्रत्येक url के लिए अपने डाउनलोड coroutine फ़ंक्शन को पास करते हैं, जिन्हें हम डाउनलोड करना चाहते हैं। download_coroutine में, हम async_timeout.timeout() context manager बनाते हैं जो मूल रूप से X सेकंड का टाइमर तैयार करता है। जब सेकंड पूरे हो जाते हैं, तो context manager समाप्त हो जाता है या timeout हो जाता है। इस मामले में timeout 10 सेकंड है। अगला, हम अपने session के get() मेथड को कॉल करते हैं जो हमें एक response ऑब्जेक्ट देता है। अब हम उस हिस्से पर पहुँचते हैं जो थोड़ा जादुई है। जब आप response ऑब्जेक्ट के content attribute का उपयोग करते हैं, तो यह aiohttp.StreamReader का एक इंस्टेंस लौटाता है जो हमें मनचाहे आकार के chunks में फ़ाइल डाउनलोड करने देता है। जैसे-जैसे हम फ़ाइल पढ़ते हैं, हम उसे लोकल डिस्क पर लिखते जाते हैं। अंत में हम response के release() मेथड को कॉल करते हैं, जो response प्रोसेसिंग को पूरा कर देगा।
aiohttp के प्रलेखन के अनुसार, क्योंकि response ऑब्जेक्ट एक context manager के अंदर बनाया गया था, यह तकनीकी रूप से release() को implicitly कॉल करता है। लेकिन Python में, explicit प्रायः बेहतर माना जाता है और प्रलेखन में एक नोट भी है कि हमें यह मानकर नहीं चलना चाहिए कि कनेक्शन अपने-आप बंद हो जाएगा, इसलिए मुझे लगता है कि यहाँ इसे स्पष्ट रूप से रिलीज़ करना बेहतर है।
यहाँ एक हिस्सा अब भी blocking है और वह है डिस्क पर लिखने वाला भाग। जब तक हम फ़ाइल लिख रहे हैं, हम अब भी block हो रहे हैं। aiofiles नामक एक और लाइब्रेरी है जिसका उपयोग करके हम फ़ाइल-लेखन को भी asynchronous बनाने की कोशिश कर सकते हैं, लेकिन वह अपडेट मैं पाठक पर छोड़ता हूँ।
आप asyncio event loop का उपयोग करके सामान्य फ़ंक्शनों को भी शेड्यूल कर सकते हैं। पहला मेथड जिसे हम देखेंगे, वह है call_soon। call_soon मेथड मूल रूप से आपके callback या event handler को यथाशीघ्र कॉल कर देगा। यह FIFO कतार की तरह काम करता है, इसलिए यदि कुछ callbacks को चलने में अधिक समय लगता है, तो बाकी तब तक विलंबित रहेंगे जब तक पहले वाले पूरे नहीं हो जाते।
आइए एक उदाहरण देखें:
asyncio के अधिकांश फ़ंक्शंस कीवर्ड आर्गुमेंट स्वीकार नहीं करते, इसलिए यदि हमें अपने event handler को कीवर्ड्स पास करने की आवश्यकता हो, तो हमें functools मॉड्यूल की ज़रूरत पड़ेगी। हमारा सामान्य फ़ंक्शन जब भी कॉल होगा, तब stdout पर कुछ टेक्स्ट प्रिंट करेगा। यदि आप उसके stop आर्गुमेंट को True पर सेट कर दें, तो यह event loop को भी रोक देगा।
पहली बार जब हम इसे कॉल करते हैं, हम loop नहीं रोकते। दूसरी बार जब हम इसे कॉल करते हैं, हम loop रोक देते हैं। हम loop को इसलिए रोकना चाहते हैं क्योंकि हमने उसे run_forever कहा है, जो event loop को अनंत चक्र में डाल देगा। एक बार loop रुक जाए, तो हम उसे बंद कर सकते हैं।
यदि आप यह कोड चलाते हैं, तो आपको निम्नलिखित आउटपुट दिखेगा:
starting event loop
Event handler called
Event handler called
stopping the loop
closing event loop
call_soon_threadsafe नाम का एक संबंधित फ़ंक्शन भी है। जैसा कि नाम से स्पष्ट है, यह call_soon की ही तरह काम करता है, लेकिन यह thread-safe है। यदि आप वास्तव में किसी कॉल को भविष्य में किसी समय तक विलंबित करना चाहते हैं, तो आप call_later फ़ंक्शन का उपयोग कर सकते हैं। इस स्थिति में, हम अपने call_soon सिग्नेचर को निम्नलिखित में बदल सकते हैं:
loop.call_later(1, event_handler, loop)
यह हमारे event handler को एक सेकंड की देरी से कॉल करेगा, फिर उसे कॉल करेगा और loop को उसके पहले पैरामीटर के रूप में पास करेगा। यदि आप भविष्य में किसी विशिष्ट समय का शेड्यूल बनाना चाहते हैं, तो आपको कंप्यूटर के समय की बजाय loop का समय लेना होगा। आप ऐसा इस तरह कर सकते हैं:
current_time = loop.time()
एक बार जब आपके पास यह हो, तो आप call_at फ़ंक्शन का उपयोग कर सकते हैं और उसे वह समय पास कर सकते हैं, जब आप चाहते हैं कि आपका event handler कॉल हो। मान लें कि हम अपने event handler को अभी से पाँच मिनट बाद कॉल करना चाहते हैं। आप इसे इस तरह कर सकते हैं:
loop.call_at(current_time + 300, event_handler, loop)
इस उदाहरण में, हम प्राप्त किए गए current time में 300 सेकंड या पाँच मिनट जोड़ते हैं। ऐसा करके, हम अपने event handler को पाँच मिनट के लिए विलंबित कर देते हैं! काफ़ी बढ़िया!
Tasks, Future की subclass और coroutine का wrapper होते हैं। वे आपको यह क्षमता देते हैं कि आप ट्रैक कर सकें कि वे कब अपनी प्रोसेसिंग पूरी करते हैं। क्योंकि वे Future का ही एक प्रकार हैं, अन्य coroutines किसी task का इंतजार कर सकते हैं और आप task के पूरा होने पर उसका परिणाम भी प्राप्त कर सकते हैं।
आइए एक साधारण उदाहरण देखें:
यहाँ हम एक asynchronous फ़ंक्शन बनाते हैं जो उन सेकंड्स की संख्या स्वीकार करता है जितने समय तक फ़ंक्शन चलेगा। यह एक लंबे समय तक चलने वाली प्रक्रिया का अनुकरण करता है। फिर हम अपना event loop बनाते हैं और event loop ऑब्जेक्ट के create_task फ़ंक्शन को कॉल करके एक task ऑब्जेक्ट बनाते हैं। create_task उस फ़ंक्शन को स्वीकार करता है जिसे हम task में बदलना चाहते हैं। इसके बाद हम event loop को बताते हैं कि task के पूरा होने तक चलो। बिलकुल अंत में, हम task का परिणाम प्राप्त करते हैं क्योंकि वह पूरा हो चुका होता है।
Tasks को उनकी cancel मेथड का उपयोग करके बहुत आसानी से रद्द भी किया जा सकता है। जब आप किसी task को समाप्त करना चाहें, तो बस उसे कॉल करें। यदि कोई task किसी अन्य ऑपरेशन का इंतजार करते समय रद्द हो जाता है, तो वह CancelledError उठाएगा।
इस बिंदु पर, आपको अपने आप asyncio लाइब्रेरी के साथ काम शुरू करने के लिए पर्याप्त जानकारी होनी चाहिए। asyncio लाइब्रेरी बहुत शक्तिशाली है और आपको बहुत-से बेहतरीन और दिलचस्प कार्य करने देती है। asyncio लाइब्रेरी को नेटवर्क sockets के लिए डिज़ाइन किया गया था।
asyncio से पहले asynchronous socket प्रोग्रामिंग की सुविधा देने वाली एक बढ़िया लाइब्रेरी है Twisted फ्रेमवर्क। एक और रोचक प्रोजेक्ट है Dask, जो विश्लेषणात्मक कंप्यूटिंग के लिए एक लचीली समानांतर कंप्यूटिंग लाइब्रेरी है। बस asyncio मॉड्यूल के प्रलेखन में थोड़ी और गहराई से जाएँ और अपने विचारों को प्रवाहित होने दें!
Python कोर्स
{count, plural, one {कोर्स} other {कोर्स}}
{count, plural, one {कोर्स} other {कोर्स}}
{count, plural, one {कोर्स} other {कोर्स}}