कोर्स
Python 3.8 ने भाषा में एक नया ऑपरेटर पेश किया। इसका आधिकारिक नाम असाइनमेंट एक्सप्रेशन है, और इसमें := ऑपरेटर का उपयोग होता है।
हालाँकि, आमतौर पर इसे walrus ऑपरेटर कहा जाता है क्योंकि यह walrus की मूँछों जैसा दिखता है!
आम तौर पर शांत रहने वाली Python दुनिया में यह ऑपरेटर जोड़ने पर काफी बहस हुई थी, और इसका उपयोग अभी भी अपेक्षाकृत सीमित है। फिर भी, असाइनमेंट एक्सप्रेशन अब Python का हिस्सा है और कुछ स्थितियों में उपयोगी हो सकता है।
यह ट्यूटोरियल बताएगा कि Python का walrus ऑपरेटर कैसे काम करता है और किन परिस्थितियों में इसका उपयोग किया जा सकता है।
Python का Walrus ऑपरेटर क्या है
जो असाइनमेंट एक्सप्रेशन := ऑपरेटर का उपयोग करता है, वह अधिक सामान्य असाइनमेंट स्टेटमेंट जैसा दिखता है, जिसमें = ऑपरेटर का उपयोग होता है। हालाँकि, दोनों के बीच एक महत्वपूर्ण अंतर है।
एक स्टेटमेंट कोड की वह इकाई है जो कोई कार्रवाई करती है। एक एक्सप्रेशन ऐसा स्टेटमेंट है जिसे किसी मान तक मूल्यांकित किया जा सकता है। इसलिए, एक एक्सप्रेशन कोई मान लौटाता है, जबकि जो स्टेटमेंट एक्सप्रेशन नहीं हैं वे कोई मान नहीं लौटाते।
Python का REPL (Read, Evaluate, Print, and Loop) किसी एक्सप्रेशन का मान उसके मूल्यांकन होते ही दिखा देता है। हालाँकि, जब ऐसा स्टेटमेंट चलाया जाता है जो एक्सप्रेशन नहीं है, तो कोई मान नहीं दिखाया जाता:
>>> import random # Not an expression
>>> 10 * 2 # An expression
20
import स्टेटमेंट किसी मान तक मूल्यांकित नहीं होता। जबकि 10 * 2 एक एक्सप्रेशन है जो किसी मान तक मूल्यांकित होता है।
असाइनमेंट स्टेटमेंट, जो = ऑपरेटर का उपयोग करता है, किसी ऑब्जेक्ट को वैरिएबल या किसी अन्य रेफ़रेंस को असाइन करता है, लेकिन यह मान लौटाता नहीं है। असाइनमेंट एक्सप्रेशन, यानी walrus ऑपरेटर, असाइनमेंट स्टेटमेंट जैसा ही काम करता है, पर साथ ही ऑब्जेक्ट को लौटाता भी है:
>>> value = 10 # Assignment statement
>>> value
10
>>> (value := 20) # Assignment expression
20
ध्यान दें कि असाइनमेंट स्टेटमेंट मान नहीं लौटाता, जबकि असाइनमेंट एक्सप्रेशन लौटाता है। इसलिए, एक असाइनमेंट एक्सप्रेशन कई कार्रवाइयों को साथ जोड़ देता है:
:=ऑपरेटर के दाएँ तरफ़ वाले एक्सप्रेशन का मूल्यांकन करता है।- इस मान को किसी वैरिएबल नाम को असाइन करता है।
- यह मान लौटाता है।
जहाँ असाइनमेंट स्टेटमेंट की जगह असाइनमेंट एक्सप्रेशन का उपयोग किया जा रहा हो, वहाँ अस्पष्टता कम करने के लिए कोष्ठक आवश्यक सिंटैक्स हैं। walrus ऑपरेटर के सिंटैक्स नियमों पर आगे इस ट्यूटोरियल में और जानकारी है।
एक असाइनमेंट एक्सप्रेशन का उपयोग Python में जहाँ भी एक्सप्रेशन इस्तेमाल हो सकते हैं, वहाँ किया जा सकता है। आइए एक उदाहरण देखें:
print(f"The number is {(value := 50)}")
print(value)
The number is 50
50
पहली पंक्ति की f-string के भीतर वैरिएबल नाम value परिभाषित किया गया है। साथ ही, वही पूर्णांक सीधे f-string की कर्ली ब्रेसेज़ में लौटाया भी जाता है। f-strings की कर्ली ब्रेसेज़ में एक एक्सप्रेशन होना ज़रूरी है, इसलिए वहाँ असाइनमेंट स्टेटमेंट का उपयोग नहीं किया जा सकता।
Python के Walrus ऑपरेटर के उपयोग के मामले
walrus ऑपरेटर के दो सामान्य उपयोग हैं: कोड को सरल बनाना और उसे अनुकूलित करना।
कोड को सरल बनाना
walrus ऑपरेटर का एक सामान्य उपयोग वह कोड सरल करना है जहाँ किसी वैरिएबल को उपयोग से पहले परिभाषित करना पड़ता है, जैसे कुछ सशर्त स्टेटमेंट्स में। इस while लूप पर विचार करें:
import random
value = random.randint(1, 20)
while value < 18:
print(value)
value = random.randint(1, 20)
7
5
1
11
12
1
9
यह कोड तब तक while ब्लॉक चलाता है जब तक उत्पन्न यादृच्छिक मान 18 से छोटा है। वैरिएबल value को while स्टेटमेंट में उपयोग करने के लिए लूप से पहले परिभाषित करना पड़ता है। while ब्लॉक के अंत में value को एक नया यादृच्छिक नंबर असाइन किया जाता है।
असाइनमेंट एक्सप्रेशन := का उपयोग करके कोड की पंक्तियाँ कम की जा सकती हैं और random.randint() को प्रोग्राम के एक ही स्थान पर कॉल किया जा सकता है:
import random
while (value := random.randint(1, 20)) < 18:
print(value)
1
9
2
4
9
15
11
हर बार जब while लूप एक नया इटरेशन शुरू करता है, तो एक नया यादृच्छिक नंबर जेनरेट होकर value को असाइन होता है।
कोड का अनुकूलन
अब लिस्ट कॉम्प्रिहेंशन्स वाला एक और उदाहरण देखें। यह कोड उन स्ट्रिंग्स की सूची को फ़िल्टर करता है जिनमें तिथियाँ हैं और केवल किसी दिए गए वर्ष के भीतर की तिथियों वाले datetime.datetime ऑब्जेक्ट्स की सूची बनाता है:
from datetime import datetime
def format_date(date_str):
return datetime.strptime(date_str, "%Y-%m-%d")
dates = ["2024-01-01", "2022-12-31", "2024-06-15", "2023-08-23", "2024-11-30"]
formatted_dates = [
format_date(date)
for date in dates
if format_date(date).year == 2024
]
print(formatted_dates)
[datetime.datetime(2024, 1, 1, 0, 0), datetime.datetime(2024, 6, 15, 0, 0), datetime.datetime(2024, 11, 30, 0, 0)]
फ़ंक्शन format_date() yyyy-mm-dd प्रारूप में दी गई तिथि वाली स्ट्रिंग लेता है और एक datetime.datetime ऑब्जेक्ट लौटाता है। datetime.datetime ऑब्जेक्ट के गुणों में से एक .year है, जिसमें तिथि का वर्ष घटक होता है।
अंतिम सूची में केवल 2024 की तीन तिथियाँ शामिल हैं, और वे स्ट्रिंग्स के बजाय datetime.datetime ऑब्जेक्ट्स हैं। लिस्ट कॉम्प्रिहेंशन में if क्लॉज़ है, जो format_date() को कॉल करता है। हालाँकि, format_date() को लिस्ट कॉम्प्रिहेंशन की पहली अभिव्यक्ति में भी कॉल किया जाता है, जो सूची में जोड़ने के लिए मान बनाती है। एक ही आर्ग्युमेंट के साथ फ़ंक्शन को दो बार कॉल करना अक्षम है, खासकर यदि वही फ़ंक्शन प्रोग्राम के प्रदर्शन में बाधा बन रहा हो।
एक विकल्प यह है कि फ़ंक्शन को पहले कॉल कर लिया जाए और उसे किसी वैरिएबल में असाइन कर लिया जाए, फिर उसका उपयोग किया जाए। लेकिन, असाइनमेंट स्टेटमेंट के साथ ऐसा करते हुए लिस्ट कॉम्प्रिहेंशन का उपयोग संभव नहीं है। इस समाधान के लिए सामान्य for लूप चाहिए:
from datetime import datetime
def format_date(date_str):
return datetime.strptime(date_str, "%Y-%m-%d")
dates = ["2024-01-01", "2022-12-31", "2024-06-15", "2023-08-23", "2024-11-30"]
formatted_dates = []
for date in dates:
formatted_date = format_date(date)
if formatted_date.year == 2024:
formatted_dates.append(formatted_date)
print(formatted_dates)
[datetime.datetime(2024, 1, 1, 0, 0), datetime.datetime(2024, 6, 15, 0, 0), datetime.datetime(2024, 11, 30, 0, 0)]
walrus ऑपरेटर लिस्ट कॉम्प्रिहेंशन के साथ अनुकूल एक वैकल्पिक तरीका देता है, क्योंकि फ़ंक्शन द्वारा लौटाई गई फ़ॉर्मैटेड तिथि को उसी अभिव्यक्ति में किसी वैरिएबल नाम को असाइन भी किया जा सकता है और लौटाया भी जा सकता है:
from datetime import datetime
def format_date(date_str):
return datetime.strptime(date_str, "%Y-%m-%d")
dates = ["2024-01-01", "2022-12-31", "2024-06-15", "2023-08-23", "2024-11-30"]
formatted_dates = [
formatted_date
for date in dates
if (formatted_date := format_date(date)).year == 2024
]
print(formatted_dates)
[datetime.datetime(2024, 1, 1, 0, 0), datetime.datetime(2024, 6, 15, 0, 0), datetime.datetime(2024, 11, 30, 0, 0)]
लिस्ट कॉम्प्रिहेंशन में असाइनमेंट एक्सप्रेशन वह अभिव्यक्ति है जो कोष्ठकों में घिरी है, (formatted_date := format_date(date))। यह अभिव्यक्ति format_date() फ़ंक्शन को कॉल करती है, उसका मान वैरिएबल formatted_date को असाइन करती है और वही मान लौटाती है। क्योंकि असाइनमेंट एक्सप्रेशन का मूल्यांकन एक datetime.datetime ऑब्जेक्ट तक होता है, कोड .year गुण तक पहुँचता है, जिसकी तुलना समानता ऑपरेटर से 2024 से होती है।
लिस्ट कॉम्प्रिहेंशन में if क्लॉज़ उस अभिव्यक्ति से पहले चलती है जो सूची के लिए मान बनाती है। इसलिए, if क्लॉज़ में walrus ऑपरेटर का उपयोग किया जाता है, और इस असाइनमेंट एक्सप्रेशन में परिभाषित वैरिएबल को लिस्ट कॉम्प्रिहेंशन की पहली अभिव्यक्ति के रूप में उपयोग किया जाता है।
Python के Walrus ऑपरेटर पर विवाद
असाइनमेंट एक्सप्रेशन सभी Python प्रोग्रामर्स के प्रिय नहीं हैं। कुछ लोग इसे कोड को सरल बनाने का तरीका मानते हैं, जबकि अन्य महसूस करते हैं कि यह कोड की पठनीयता घटाता है और वे लंबा विकल्प पसंद करते हैं। पठनीयता विषयगत हो सकती है, इसलिए इस पर राय अलग-अलग है कि walrus ऑपरेटर कोड को अधिक या कम पठनीय बनाता है।
पिछले खंड के अंतिम उदाहरण में आवश्यक समझौते दिखते हैं। walrus ऑपरेटर के बिना, इस उदाहरण के कोड में पहले एक खाली सूची आरंभ करनी पड़ती है, फिर for लूप में मान उसमें जोड़े जाते हैं। यह समाधान लिस्ट कॉम्प्रिहेंशन की तुलना में लंबा और कम कुशल है, जबकि लिस्ट कॉम्प्रिहेंशन इसी काम के लिए अनुकूलित हैं।
हालाँकि, कुछ प्रोग्रामर्स जो walrus ऑपरेटर की बजाय अधिक कोड लिखना पसंद करते हैं—खासकर जब प्रदर्शन चिंता का विषय न हो—उनके लिए सामान्य for लूप समाधान अधिक पठनीय हो सकता है।
कई कोडिंग स्टाइल गाइड्स अभी भी असाइनमेंट एक्सप्रेशन से बचने या इसे बहुत कम उपयोग करने की सलाह देते हैं।
Python के Walrus ऑपरेटर: सिंटैक्स नियम
walrus ऑपरेटर को पहली बार आज़माते समय उसके सिंटैक्स नियमों के कारण सिंटैक्स एरर्स आना आम है।
असाइनमेंट स्टेटमेंट = और असाइनमेंट एक्सप्रेशन := में भ्रम से बचने के लिए, ऐसी कोई स्थिति नहीं है जहाँ दोनों विकल्प सिंटैक्स के लिहाज़ से मान्य हों। इसी कारण, अक्सर असाइनमेंट एक्सप्रेशन को कोष्ठकों में रखना आवश्यक होता है:
(value := 20) # Valid
value := 20 # Invalid (SyntaxError)
इस स्थिति में असाइनमेंट एक्सप्रेशन को कोष्ठकों में रखना अनिवार्य है, जबकि असाइनमेंट स्टेटमेंट को कोष्ठकों में नहीं रखा जा सकता—यानी यह उसके उलट है।
हालाँकि, हर बार असाइनमेंट एक्सप्रेशन में कोष्ठक आवश्यक नहीं होते:
import random
if value := random.randint(0, 3):
print(f"{value} is greater than 0")
1 is greater than 0
if स्टेटमेंट में असाइनमेंट एक्सप्रेशन 0 से 3 के बीच एक यादृच्छिक संख्या बनाता है और उसे वैरिएबल value को असाइन करता है। पूर्णांक 1, 2, और 3 truthy होते हैं, और जब ये मान मिलते हैं तो if ब्लॉक का कोड चलता है। लेकिन जब random.randint() 0 लौटाता है, जो falsy है, तब if ब्लॉक निष्पादित नहीं होता।
if कीवर्ड के बाद असाइनमेंट स्टेटमेंट मान्य नहीं है। इसलिए यहाँ कोई भ्रम नहीं है, और कोष्ठक आवश्यक नहीं हैं।
यह उदाहरण असाइनमेंट एक्सप्रेशन के बारे में एक और अहम बिंदु भी दिखाता है। असाइन किए गए वैरिएबल का स्कोप सामान्य LEGB नियमों का पालन करता है। इसलिए, इस उदाहरण में वैरिएबल value ग्लोबल स्कोप में हर जगह उपलब्ध है:
import random
if value := random.randint(0, 3):
print(f"{value} is greater than 0")
print(value)
2 is greater than 0
2
यदि किसी फ़ंक्शन डेफ़िनिशन के भीतर असाइनमेंट एक्सप्रेशन का उपयोग किया जाता है, तो वैरिएबल केवल उसी फ़ंक्शन के भीतर लोकल रूप से उपलब्ध होगा। यह व्यवहार असाइनमेंट स्टेटमेंट से बनाए गए वैरिएबल्स जैसा ही है।
इस उदाहरण में walrus ऑपरेटर का उपयोग करते समय एक और संभावित समस्या दिखाने के लिए परिवर्तन किया जा सकता है। आइए if स्टेटमेंट में एक तुलना ऑपरेटर शामिल करें:
import random
if value := random.randint(0, 3) < 2:
print(f"{value} is less than 2")
True is less than 2
आउटपुट इस कोड की समस्या की ओर इशारा करता है क्योंकि value एक पूर्णांक के बजाय Boolean True है। walrus ऑपरेटर की प्राथमिकता सभी ऑपरेटर्स में सबसे कम होती है, और केवल कॉमा की प्राथमिकता इससे भी कम है। इसलिए पहले कम-से (<) ऑपरेटर का मूल्यांकन होता है, और उसका आउटपुट value को असाइन हो जाता है। इस स्थिति से निपटने के लिए कोष्ठक आवश्यक हैं:
import random
if (value := random.randint(0, 3)) < 2:
print(f"{value} is less than 2")
0 is less than 2
अब कोष्ठकों की प्राथमिकता सबसे अधिक होने के कारण असाइनमेंट एक्सप्रेशन का पहले मूल्यांकन होता है। random.randint() द्वारा लौटाई गई संख्या value को असाइन होती है, और कम-से ऑपरेटर के पहले ओपेरैंड के रूप में उपयोग होती है।
असाइनमेंट स्टेटमेंट के विपरीत, walrus ऑपरेटर केवल वैरिएबल नामों को ही मान असाइन कर सकता है; यह सबस्क्रिप्ट्स या एट्रिब्यूट्स को मान असाइन करने में उपयोग नहीं हो सकता:
(name := "James") # Valid
points = {"James": 10, "Mary": 20}
(points["Sarah"] := 30) # Invalid
class Article:
pass
article = Article()
(article.title := "The Walrus Operator") # Invalid
walrus ऑपरेटर का उपयोग करके न तो सबस्क्रिप्ट के माध्यम से और न ही किसी एट्रिब्यूट को मान असाइन करना संभव है।
निष्कर्ष
असाइनमेंट एक्सप्रेशन, जो := सिंटैक्स का उपयोग करता है और जिसे अक्सर walrus ऑपरेटर कहा जाता है, Python में अपेक्षाकृत नया जोड़ है। असाइनमेंट स्टेटमेंट, जो = ऑपरेटर का उपयोग करता है, किसी वैरिएबल नाम को मान असाइन करने का अधिक सामान्य तरीका है।
लेकिन walrus ऑपरेटर इस असाइनमेंट को आगे बढ़ाते हुए वह मान भी लौटाता है जो असाइन हुआ है, जिससे यह असाइनमेंट एक एक्सप्रेशन बन जाता है। इसलिए, आप असाइनमेंट एक्सप्रेशन का उपयोग वहाँ कर सकते हैं जहाँ Python में अन्य एक्सप्रेशन्स का उपयोग किया जा सकता है।
फिर भी, Python समुदाय walrus ऑपरेटर की उपयोगिता और पठनीयता को लेकर बँटा हुआ है। इसी कारण, असाइनमेंट एक्सप्रेशन का उपयोग Python में अन्य नई सुविधाओं जितना प्रचलित नहीं है। इसे उपयोग करना जानना अच्छा है, पर इसका अति-उपयोग न करें!
मैंने यूनिवर्सिटी ऑफ माल्टा में स्नातक स्तर पर भौतिकी और गणित का अध्ययन किया। उसके बाद, मैं लंदन चला गया और इम्पीरियल कॉलेज से भौतिकी में पीएचडी प्राप्त की। मैंने मानव रेटिना की इमेजिंग के लिए नवीन ऑप्टिकल तकनीकों पर काम किया। अब, मैं Python के बारे में लिखने, Python पर संवाद करने और Python सिखाने पर ध्यान केंद्रित करता हूँ।
