पीईपी 572 (अजगर 3.8 में असाइनमेंट एक्सप्रेशन)

हेलो, हैबर। इस बार हम पीईपी 572 को देखेंगे, जो असाइनमेंट एक्सप्रेशन के बारे में बात करता है। यदि आप अभी भी ": =" ऑपरेटर पर संदेह कर रहे हैं या इसके उपयोग के नियमों को पूरी तरह से नहीं समझते हैं, तो यह लेख आपके लिए है। यहां आपको प्रश्न के कई उदाहरण और उत्तर मिलेंगे: "ऐसा क्यों है?" यह लेख जितना संभव हो सके पूरा हो गया, और यदि आपके पास थोड़ा समय है, तो मेरे द्वारा लिखे गए अनुभाग को देखें। इसकी शुरुआत में, असाइनमेंट एक्सप्रेशंस के साथ आरामदायक काम के लिए मुख्य "शोध" एकत्र किया जाता है। यदि आप त्रुटियां पाते हैं तो मुझे अग्रिम क्षमा करें (मेरे बारे में उनके बारे में लिखें, मैं इसे ठीक कर दूंगा)। चलो शुरू करते हैं:

पीईपी 572 - असाइनमेंट एक्सप्रेशंस

जोश572
शीर्षक:असाइनमेंट एक्सप्रेशंस
लेखक:क्रिस एंजेलिको <rosuav at gmail.com>, टिम पीटर्स <tim.peters at gmail.com>, गुइडो वैन रोसम <python.org पर मार्गदर्शक>
चर्चा:dy-sig python.org पर
स्थिति:स्वीकार किए जाते हैं
एक प्रकार:मानक
बनाया था:28 फरवरी 2018
पायथन संस्करण:3.8
कहानी पोस्ट करें:28-Feb-2018, 02-Mar-2018, 23-Mar-2018, 04-Apr-2018, 17-Apr-2018, 25-Apr-2018, 09-Jul-2018, 05-Aug-2019
मानक अपनाने की अनुमति:mail.python.org/pipermail/python-dev/2018-July/154601.html (लंबे समय तक वीपीएन के साथ, लेकिन यह लोड होता है)
सामग्री


टिप्पणी


यह कन्वेंशन नए नोटेशन NAME: = expr का उपयोग करके अभिव्यक्ति के अंदर असाइनमेंट की संभावना के बारे में बात करेगा।

नवाचारों के भाग के रूप में, शब्दकोश जनरेटर (शब्दकोश समझ) की गणना करने की प्रक्रिया को अद्यतन किया गया है। यह सुनिश्चित करता है कि मूल्य अभिव्यक्ति से पहले मुख्य अभिव्यक्ति का मूल्यांकन किया जाता है (यह आपको किसी चर की कुंजी को बांधने की अनुमति देता है और फिर कुंजी के अनुरूप मूल्य की गणना में बनाए गए चर का पुन: उपयोग करता है)।

इस PEP की चर्चा के दौरान, इस ऑपरेटर को अनौपचारिक रूप से वालरस ऑपरेटर के रूप में जाना जाने लगा। निर्माण का औपचारिक नाम "असाइनमेंट एक्सप्रेशन" (पीईपी के अनुसार: असाइनमेंट एक्सप्रेशंस हेडिंग) है, लेकिन इसे "नामित एक्सप्रेशंस" के रूप में संदर्भित किया जा सकता है। उदाहरण के लिए, सीपीथॉन में संदर्भ कार्यान्वयन इसी नाम का उपयोग करता है।

औचित्य


नामकरण प्रोग्रामिंग का एक महत्वपूर्ण हिस्सा है जो आपको लंबी अभिव्यक्ति के बजाय "वर्णनात्मक" नाम का उपयोग करने की अनुमति देता है, और मूल्यों का पुन: उपयोग करना भी आसान बनाता है। वर्तमान में, यह केवल निर्देशों के रूप में किया जा सकता है, जो सूची (सूची समझ), साथ ही साथ अन्य अभिव्यक्तियों को बनाते समय इस ऑपरेशन को अनुपलब्ध बनाता है।

इसके अलावा, एक बड़ी अभिव्यक्ति के नामकरण के भाग संकेत और मध्यवर्ती परिणाम प्रदर्शित करने के लिए उपकरण प्रदान करके इंटरैक्टिव डिबगिंग के साथ मदद कर सकते हैं। नेस्टेड अभिव्यक्तियों के परिणामों को कैप्चर करने की क्षमता के बिना, आपको स्रोत कोड को बदलने की आवश्यकता होगी, लेकिन असाइनमेंट एक्सप्रेशंस का उपयोग करके आपको फॉर्म "नाम: = अभिव्यक्ति" के कुछ "मार्कर" सम्मिलित करने की आवश्यकता है। यह अनावश्यक रीफ़ैक्टरिंग को समाप्त करता है, और इसलिए डिबगिंग के दौरान अनजाने कोड परिवर्तन की संभावना को कम कर देता है (हाइजेनबग्स का एक सामान्य कारण त्रुटियों है जो डीबगिंग के दौरान कोड के गुणों को बदल देता है और अप्रत्याशित रूप से उत्पादन में दिखाई दे सकता है]), और यह कोड दूसरे के लिए अधिक समझने योग्य होगा प्रोग्रामर के लिए।

असली कोड का महत्व


इस PEP के विकास के दौरान, बहुत से लोग (दोनों प्रस्तावक और आलोचक) एक तरफ खिलौना उदाहरणों पर ध्यान केंद्रित किए गए थे, और दूसरी ओर अत्यधिक जटिल उदाहरण।

खिलौनों के उदाहरणों का खतरा दो गुना है: वे अक्सर किसी को "ओह, यह विडंबनापूर्ण कहते हैं" बनाने के लिए बहुत सार होते हैं, और वे "मैं कभी नहीं लिखूंगा" शब्दों के साथ आसानी से खारिज कर दिया जाता है। अत्यधिक जटिल उदाहरणों का खतरा यह है कि वे आलोचकों के लिए एक सुविधाजनक वातावरण प्रदान करते हैं जो सुझाव देते हैं कि यह कार्यक्षमता हटा दी जाए ("यह बहुत भ्रामक है," ऐसे लोग कहते हैं)।

हालांकि, ऐसे उदाहरणों के लिए अच्छा उपयोग है: वे इच्छित शब्दार्थ को स्पष्ट करने में मदद करते हैं। इसलिए, हम उनमें से कुछ नीचे देंगे। हालांकि, आश्वस्त होने के लिए , उदाहरणों पर आधारित होना चाहिएवास्तविक कोड जो इस PEP के बारे में सोचे बिना लिखा गया था। अर्थात्, वह कोड जो वास्तव में उपयोगी अनुप्रयोग का हिस्सा है (कोई अंतर नहीं: चाहे वह बड़ा हो या छोटा)। टिम पीटर्स ने उनकी व्यक्तिगत रिपॉजिटरी को देखकर और उनके द्वारा लिखे गए कोड के उदाहरणों को चुनकर हमारी बहुत मदद की, जो कि (उनकी राय में) असाइनमेंट एक्सप्रेशंस का उपयोग करते हुए उन्हें फिर से लिखे (कट्टरता के बिना) किया गया था। उनका निष्कर्ष यह है: वर्तमान परिवर्तन उनके कोड के कुछ हिस्सों में एक मामूली लेकिन स्पष्ट सुधार लाएगा।

वास्तविक कोड का एक अन्य उदाहरण अप्रत्यक्ष अवलोकन है कि प्रोग्रामर कैसे कॉम्पैक्टनेस को महत्व देते हैं। गुइडो वैन रोसुम ने ड्रॉपबॉक्स कोडबेस की जांच की और कुछ सबूत पाए कि प्रोग्रामर कुछ छोटे अभिव्यक्तियों का उपयोग करने की तुलना में कोड की कम पंक्तियों को लिखना पसंद करते हैं।

बिंदु में मामला: जब एक प्रोग्रामर एक सबएक्प्रेशन (जिससे प्रोग्राम को धीमा कर रहा है) को दोहराता है, लेकिन कई अतिरिक्त बिंदुओं को कोड की एक अतिरिक्त लाइन से बचाता है। उदाहरण के लिए, लिखने के बजाय:

match = re.match(data)
group = match.group(1) if match else None

प्रोग्रामर इस विकल्प को पसंद करते हैं:

group = re.match(data).group(1) if re.match(data) else None

यहां एक और उदाहरण दिखाया गया है कि प्रोग्रामर कभी-कभी इंडेंटेशन के "पिछले स्तर" को बनाए रखने के लिए अधिक काम करने के लिए तैयार होते हैं:

match1 = pattern1.match(data)
match2 = pattern2.match(data)
if match1:
    result = match1.group(1)
elif match2:
    result = match2.group(2)
else:
    result = None

यह कोड पैटर्न 2 की गणना करता है, भले ही पैटर्न 1 पहले से ही मेल खाता हो (इस मामले में, दूसरी उप-शर्त कभी पूरी नहीं होगी)। इसलिए, निम्न समाधान अधिक प्रभावी है, लेकिन कम आकर्षक है:

match1 = pattern1.match(data)
if match1:
    result = match1.group(1)
else:
    match2 = pattern2.match(data)
    if match2:
        result = match2.group(2)
    else:
        result = None

सिंटेक्स और शब्दार्थ


अधिकांश मामलों में जहां पायथन मनमाने ढंग से अभिव्यक्ति का उपयोग करता है, अब आप असाइनमेंट एक्सप्रेशन का उपयोग कर सकते हैं। उनके पास NAME का रूप है: = expr, जहां expr किसी भी मान्य Python व्यंजक है, सिवाय अनपेक्षित टूपल के, और NAME पहचानकर्ता है। इस तरह की अभिव्यक्ति का मूल्य मूल के साथ मेल खाता है, लेकिन एक अतिरिक्त प्रभाव लक्ष्य वस्तु के मूल्य का काम है:

# Handle a matched regex
if (match := pattern.search(data)) is not None:
    # Do something with match

# A loop that can't be trivially rewritten using 2-arg iter()
while chunk := file.read(8192):
   process(chunk)

# Reuse a value that's expensive to compute
[y := f(x), y**2, y**3]

# Share a subexpression between a comprehension filter clause and its output
filtered_data = [y for x in data if (y := f(x)) is not None]

अपवाद स्वरूप मामले


ऐसे कई स्थान हैं जहाँ उपयोगकर्ताओं के बीच अस्पष्टता या भ्रम की स्थिति से बचने के लिए असाइनमेंट एक्सप्रेशन की अनुमति नहीं है:

  • कोष्ठकों में संलग्न नहीं किए गए असाइनमेंट एक्सप्रेशन "ऊपरी" स्तर पर निषिद्ध हैं:

    y := f(x)  # 
    (y := f(x))  # ,   

    यह नियम प्रोग्रामर के लिए असाइनमेंट ऑपरेटर और असाइनमेंट एक्सप्रेशन के बीच चयन करना आसान बना देगा - ऐसी कोई सिंटैक्टिक स्थिति नहीं होगी जिसमें दोनों विकल्प समान हों।
  • . :

    y0 = y1 := f(x)  # 
    y0 = (y1 := f(x))  # ,   

    . :

    foo(x = y := f(x))  # 
    foo(x=(y := f(x)))  # ,     

    , .
  • . :

    def foo(answer = p := 42):  # 
        ...
    def foo(answer=(p := 42)):  # Valid, though not great style
        ...

    , (. , «» ).
  • , . :

    def foo(answer: p := 42 = 5):  # 
        ...
    def foo(answer: (p := 42) = 5):  # ,  
        ...

    : , "=" ":=" .
  • -. :

    (lambda: x := 1) # 
    lambda: (x := 1) # ,  
    (x := lambda: 1) # 
    lambda line: (m := re.match(pattern, line)) and m.group(1) # Valid

    - , ":=". . , , () , .
  • f- . :

    >>> f'{(x:=10)}'  # ,  
    '10'
    >>> x = 10
    >>> f'{x:=10}'    # ,  ,  '=10'
    '        10'

    , , f-, . f- ":" . , f- . , .


एक असाइनमेंट एक्सप्रेशन एक नए दायरे का परिचय नहीं देता है। ज्यादातर मामलों में, जिस दायरे में चर बनाया जाएगा, उसे स्पष्टीकरण की आवश्यकता नहीं है: यह वर्तमान होगा। यदि चर ने पहले गैर-वैश्विक या वैश्विक कीवर्ड का उपयोग किया है, तो असाइनमेंट अभिव्यक्ति इसे ध्यान में रखेगा। केवल लैम्ब्डा (किसी फ़ंक्शन की अनाम परिभाषा होना) को इन उद्देश्यों के लिए एक अलग गुंजाइश माना जाता है।

एक विशेष मामला है: एक असाइनमेंट अभिव्यक्ति जो सूची, सेट, शब्दकोशों, या "जेनरेटर की अभिव्यक्तियों" के जनरेटर में होती है, खुद (बाद में सामूहिक रूप से "जनरेटर" (समझ) के रूप में संदर्भित) चर को उस दायरे में बांधता है जिसमें जनरेटर होता है, ग्लोब मॉडिफायर का अवलोकन करता है। या नॉनग्लोबल, अगर कोई मौजूद है।

इस विशेष मामले का औचित्य दो गुना है। सबसे पहले, यह हमें उदाहरण के लिए किसी भी () और सभी () में "सदस्य" को आसानी से पकड़ने की अनुमति देता है:

if any((comment := line).startswith('#') for line in lines):
    print("First comment:", comment)
else:
    print("There are no comments")

if all((nonblank := line).strip() == '' for line in lines):
    print("All lines are blank")
else:
    print("First non-blank line:", nonblank)

दूसरे, यह जनरेटर से एक चर को अपडेट करने के लिए एक कॉम्पैक्ट तरीका प्रदान करता है, उदाहरण के लिए:

# Compute partial sums in a list comprehension
total = 0
partial_sums = [total := total + v for v in values]
print("Total:", total)

हालाँकि, असाइनमेंट एक्सप्रेशन से वेरिएबल का नाम लूप से इट्रेट के लिए जनरेटर में पहले से इस्तेमाल किए गए नाम से मेल नहीं खा सकता है। अंतिम नाम जनरेटर के लिए स्थानीय हैं जिसमें वे दिखाई देते हैं। यह असंगत होगा यदि असाइनमेंट एक्सप्रेशन को जनरेटर के दायरे में भी संदर्भित किया जाता है।

उदाहरण के लिए, [i: = i + 1 के लिए मैं रेंज में (5)] मान्य नहीं है: लूप के लिए निर्धारित करता है कि मैं जनरेटर के लिए स्थानीय हूं, लेकिन "i: = i + 1" भाग जोर देता है कि मैं बाहरी से एक चर है क्षेत्र उसी कारण से, निम्न उदाहरण काम नहीं करेंगे:


[[(j := j) for i in range(5)] for j in range(5)] # 
[i := 0 for i, j in stuff]                       # 
[i+1 for i in (i := stuff)]                      # 

यद्यपि ऐसे मामलों के लिए लगातार शब्दार्थ निर्दिष्ट करना तकनीकी रूप से संभव है, लेकिन यह निर्धारित करना मुश्किल है कि जिस तरह से हम समझते हैं कि यह शब्दार्थ आपके वास्तविक कोड में काम करेगा। इसीलिए संदर्भ कार्यान्वयन सुनिश्चित करता है कि इस तरह के मामले विशेष हार्डवेयर कार्यान्वयन के आधार पर अपरिभाषित व्यवहार के साथ निष्पादित होने के बजाय सिंटैक्सएयर को बढ़ाते हैं। यह प्रतिबंध तब भी लागू होता है जब असाइनमेंट एक्सप्रेशन को कभी निष्पादित नहीं किया जाता है:

[False and (i := 0) for i, j in stuff]     # 
[i for i, j in stuff if True or (j := 1)]  # 

# [.  . - ""   
# ,       
# ,    ,   ]

जनरेटर बॉडी के लिए (पहला कीवर्ड "फॉर" से पहले वाला हिस्सा) और फिल्टर एक्सप्रेशन ("के बाद का हिस्सा" और किसी नेस्टेड "फॉर" के लिए)) यह प्रतिबंध विशेष रूप से वैरिएबल नामों पर लागू होता है जो एक साथ पुनरावृत्त चर के रूप में उपयोग किए जाते हैं। जैसा कि हमने पहले ही कहा है, लैंबडा अभिव्यक्तियाँ फ़ंक्शन का एक नया स्पष्ट दायरा पेश करती हैं और इसलिए इसका उपयोग अतिरिक्त प्रतिबंधों के बिना जनरेटर की अभिव्यक्तियों में किया जा सकता है। [लगभग। फिर से, ऐसे मामलों को छोड़कर: [i for i in range (2, lambda: (s: = 2) ())]]

संदर्भ कार्यान्वयन में डिज़ाइन सीमाओं के कारण (प्रतीक तालिका विश्लेषक यह नहीं पहचान सकता है कि जनरेटर के बाएं भाग से नाम का उपयोग बचे हुए भाग में किया जाता है जहाँ यह चलने योग्य अभिव्यक्ति स्थित है), इसलिए असाइनमेंट अभिव्यक्ति पूरी तरह से चलने के भाग के रूप में मनाई जाती है (प्रत्येक "के बाद वाले भाग में" और) किसी भी बाद के कीवर्ड से पहले "अगर" या "के लिए")। अर्थात्, ये सभी मामले अस्वीकार्य हैं:

[i+1 for i in (j := stuff)]                    # 
[i+1 for i in range(2) for j in (k := stuff)]  # 
[i+1 for i in [j for j in (k := stuff)]]       # 
[i+1 for i in (lambda: (j := stuff))()]        # 

एक अन्य अपवाद तब होता है जब असाइनमेंट अभिव्यक्ति का उपयोग उन जनरेटर में किया जाता है जो एक वर्ग के दायरे में हैं। यदि, उपरोक्त नियमों का उपयोग करते समय, गुंजाइश में हटाए गए वर्ग का निर्माण होना चाहिए, तो ऐसी असाइनमेंट अभिव्यक्ति अमान्य है और इसके परिणामस्वरूप सिंटैक्सएर्रोर होगा:

class Example:
    [(j := i) for i in range(5)]  # 

(अंतिम अपवाद का कारण जनरेटर द्वारा निर्मित फ़ंक्शन का अंतर्निहित दायरा है - वर्तमान में फ़ंक्शंस के लिए कोई रनटाइम तंत्र नहीं है, जो वर्ग के दायरे में स्थित एक चर का उल्लेख करने के लिए है, और हम इस तरह के तंत्र को जोड़ना नहीं चाहते हैं। यदि यह समस्या कभी हल नहीं हुई है, तो तब यह विशेष मामला (संभवतया) असाइनमेंट एक्सप्रेशंस के विनिर्देश से हटा दिया जाएगा। पर ध्यान दें कि यह समस्या तब भी होगी जब आपने क्लास के दायरे में पहले एक वैरिएबल बनाया था और इसे जनरेटर से असाइनमेंट एक्सप्रेशन के साथ बदलने की कोशिश की थी।)

देखें कैसे। जनरेटर में पाए जाने वाले असाइनमेंट एक्सप्रेशन को समकक्ष कोड में बदल दिया जाता है।

सापेक्ष प्राथमिकता: =


: = ऑपरेटर को सभी वाक्यात्मक पदों में अल्पविराम से अधिक मजबूत समूहबद्ध किया जाता है, जहाँ संभव हो, लेकिन या, और, नहीं, और सशर्त अभिव्यक्तियों (ए अगर सी और बी) सहित सभी अन्य ऑपरेटरों की तुलना में कमजोर। जैसा कि ऊपर दिए गए "असाधारण मामलों" अनुभाग से किया गया है, असाइनमेंट एक्सप्रेशंस कभी भी क्लासिक असाइनमेंट = के समान "स्तर" पर काम नहीं करते हैं। यदि संचालन का एक अलग क्रम आवश्यक है, तो कोष्ठक का उपयोग करें।

ऑपरेटर: = किसी फ़ंक्शन के स्थितीय तर्क को कॉल करते समय सीधे इस्तेमाल किया जा सकता है। हालांकि, यह सीधे तर्क में काम नहीं करेगा। कुछ उदाहरण स्पष्ट करते हैं कि तकनीकी रूप से क्या अनुमति है और क्या संभव नहीं है:

x := 0 # 

(x := 0) #  

x = y := 0 # 

x = (y := 0) #  

len(lines := f.readlines()) # 

foo(x := 3, cat='vector') # 

foo(cat=category := 'vector') # 

foo(cat=(category := 'vector')) #  

उपरोक्त "मान्य" उदाहरणों में से अधिकांश को अभ्यास में उपयोग के लिए अनुशंसित नहीं किया गया है, क्योंकि जो लोग आपके स्रोत कोड को जल्दी से स्कैन करते हैं, वे इसके अर्थ को ठीक से नहीं समझ सकते हैं। लेकिन साधारण मामलों में इसकी अनुमति है:

# Valid
if any(len(longline := line) >= 100 for line in lines):
    print("Extremely long line:", longline)

यह PEP अनुशंसा करता है कि आप बिल्कुल हमेशा रिक्त स्थान रखें: =, क्लासिक असाइनमेंट के लिए PEP 8 के लिए = की अनुशंसा के समान है। (अंतिम सिफारिश का अंतर यह है कि यह = के आसपास रिक्त स्थान को मना करता है, जिसका उपयोग फ़ंक्शन के लिए महत्वपूर्ण तर्क देने के लिए किया जाता है।)

गणना का क्रम बदलें।


अच्छी तरह से परिभाषित शब्दार्थ के लिए, इस समझौते के लिए आवश्यक है कि मूल्यांकन प्रक्रिया को स्पष्ट रूप से परिभाषित किया जाए। तकनीकी रूप से, यह कोई नई आवश्यकता नहीं है। पायथन में पहले से ही एक नियम है कि आमतौर पर उप-विभाजन का मूल्यांकन बाएं से दाएं किया जाता है। हालाँकि, असाइनमेंट एक्सप्रेशन इन "साइड इफेक्ट्स" को अधिक ध्यान देने योग्य बनाते हैं, और हम वर्तमान गणना क्रम में एक बदलाव का प्रस्ताव करते हैं:

  • शब्दकोश जनरेटर में {X: Y फ़ॉर ...}, Y का मूल्यांकन वर्तमान में X से पहले किया जाता है। हम इसे बदलने का सुझाव देते हैं ताकि X की गणना Y से पहले हो जाए ((जैसे कि एक क्लासिक तानाशाही जैसे {X: Y}, साथ ही साथ तानाशाह में भी। ((एक्स, वाई) के लिए ...) यह पहले से ही लागू किया गया है। इसलिए, शब्दकोश जनरेटर को इस तंत्र का अनुपालन करना चाहिए)


असाइनमेंट एक्सप्रेशन और असाइनमेंट निर्देशों के बीच अंतर।


सबसे महत्वपूर्ण, ": =" एक अभिव्यक्ति है , जिसका अर्थ है कि इसका उपयोग उन मामलों में किया जा सकता है जहां निर्देश मान्य नहीं हैं , जिसमें लंबोदर फ़ंक्शन और जनरेटर शामिल हैं। इसके विपरीत, असाइनमेंट एक्सप्रेशन विस्तारित कार्यक्षमता का समर्थन नहीं करते हैं जो असाइनमेंट निर्देशों में उपयोग किया जा सकता है :

  • कैस्केडिंग असाइनमेंट सीधे समर्थित नहीं है

    x = y = z = 0  # Equivalent: (z := (y := (x := 0)))
  • अलग-अलग "लक्ष्य", सरल चर नाम NAME को छोड़कर, समर्थित नहीं हैं:

    # No equivalent
    a[i] = x
    self.rest = []
  • कार्यक्षमता और प्राथमिकता "आसपास" कॉमा अलग है:

    x = 1, 2  # Sets x to (1, 2)
    (x := 1, 2)  # Sets x to 1
  • अनपैकिंग और पैकिंग वैल्यू में "शुद्ध" समतुल्यता नहीं है या बिल्कुल समर्थित नहीं है

    # Equivalent needs extra parentheses
    loc = x, y  # Use (loc := (x, y))
    info = name, phone, *rest  # Use (info := (name, phone, *rest))
    
    # No equivalent
    px, py, pz = position
    name, phone, email, *other_info = contact
  • इनलाइन प्रकार एनोटेशन समर्थित नहीं हैं:

    # Closest equivalent is "p: Optional[int]" as a separate declaration
    p: Optional[int] = None
  • संचालन का कोई छोटा रूप नहीं है:

    total += tax  # Equivalent: (total := total + tax)

कार्यान्वयन के दौरान विशिष्टता बदल जाती है


इस पीईपी के पहले लेखन और पायथन 3.8 की रिलीज़ से पहले हमारे अनुभव और अतिरिक्त विश्लेषण के आधार पर निम्नलिखित परिवर्तन किए गए थे:

  • अन्य समान अपवादों के साथ स्थिरता सुनिश्चित करने के लिए, और अंत उपयोगकर्ताओं के लिए सुविधाजनक नहीं हो सकने वाले एक नए नाम को पेश न करने के लिए, सिंटैक्सएयर के लिए मूल रूप से टारगेटस्स्कोप के प्रस्तावित उपवर्ग को हटा दिया गया है और सामान्य रूप से सिंटैक्सएयर में कम कर दिया गया है। [3]
  • CPython वर्ण तालिका को पार्स करने में सीमाओं के कारण, असाइनमेंट अभिव्यक्ति का संदर्भ कार्यान्वयन पुनरावृत्तियों के भीतर सभी उपयोगों के लिए एक SyntaxError उठाता है। इससे पहले, यह अपवाद केवल तब होता है जब बनाया जा रहा चर का नाम उसी के साथ मेल खाता है जो पहले से ही पुनरावृत्त अभिव्यक्ति में उपयोग किया जाता है। यदि पर्याप्त रूप से ठोस उदाहरण हैं, तो इसे संशोधित किया जा सकता है, लेकिन अतिरिक्त जटिलता विशुद्ध रूप से "काल्पनिक" उपयोग के मामलों के लिए अनुपयुक्त लगती है।

उदाहरण


पायथन स्टैंडर्ड लाइब्रेरी उदाहरण


site.py


env_base का उपयोग केवल एक स्थिति में किया जाता है, इसलिए असाइनमेंट को एक तार्किक ब्लॉक के "हेडर" के रूप में रखा जा सकता है।

  • वर्तमान कोड:
    env_base = os.environ.get("PYTHONUSERBASE", None)
    if env_base:
        return env_base
  • बेहतर कोड:
    if env_base := os.environ.get("PYTHONUSERBASE", None):
        return env_base

_pydecimal.py


आप नेस्टेड इफ से बच सकते हैं, जिससे एक स्तर का इंडेंटेशन निकल जाएगा।

  • वर्तमान कोड:
    if self._is_special:
        ans = self._check_nans(context=context)
        if ans:
            return ans
  • बेहतर कोड:
    if self._is_special and (ans := self._check_nans(context=context)):
        return ans

copy.py


कोड अधिक क्लासिक दिखता है, और सशर्त बयानों के कई घोंसले के शिकार से भी बचता है। (इस उदाहरण की उत्पत्ति के बारे में अधिक जानने के लिए परिशिष्ट A देखें।)

  • वर्तमान कोड:
    reductor = dispatch_table.get(cls)
    if reductor:
        rv = reductor(x)
    else:
        reductor = getattr(x, "__reduce_ex__", None)
        if reductor:
            rv = reductor(4)
        else:
            reductor = getattr(x, "__reduce__", None)
            if reductor:
                rv = reductor()
            else:
                raise Error(
                    "un(deep)copyable object of type %s" % cls)
  • बेहतर कोड:

    if reductor := dispatch_table.get(cls):
        rv = reductor(x)
    elif reductor := getattr(x, "__reduce_ex__", None):
        rv = reductor(4)
    elif reductor := getattr(x, "__reduce__", None):
        rv = reductor()
    else:
        raise Error("un(deep)copyable object of type %s" % cls)

datetime.py


tz का उपयोग केवल s + = tz के लिए किया जाता है। यदि इसका उपयोग करने का तार्किक क्षेत्र दिखाने में मदद करता है तो इसे अंदर की ओर ले जाना।

  • वर्तमान कोड:

    s = _format_time(self._hour, self._minute,
                     self._second, self._microsecond,
                     timespec)
    tz = self._tzstr()
    if tz:
        s += tz
    return s
  • बेहतर कोड:

    s = _format_time(self._hour, self._minute,
                     self._second, self._microsecond,
                     timespec)
    if tz := self._tzstr():
        s += tz
    return s

sysconfig.py


लूप में fp.readline () को "कंडीशन" के रूप में (साथ ही .match () विधि से) कॉल करने पर यदि कोड अपनी समझ को जटिल किए बिना कोड को अधिक कॉम्पैक्ट बनाता है।

  • वर्तमान कोड:

    while True:
        line = fp.readline()
        if not line:
            break
        m = define_rx.match(line)
        if m:
            n, v = m.group(1, 2)
            try:
                v = int(v)
            except ValueError:
                pass
            vars[n] = v
        else:
            m = undef_rx.match(line)
            if m:
                vars[m.group(1)] = 0
  • बेहतर कोड:

    while line := fp.readline():
        if m := define_rx.match(line):
            n, v = m.group(1, 2)
            try:
                v = int(v)
            except ValueError:
                pass
            vars[n] = v
        elif m := undef_rx.match(line):
            vars[m.group(1)] = 0

सूची जनकों को सरल बनाएं


अब सूची जनरेटर को प्रभावी ढंग से "कैप्चर" स्थिति द्वारा फ़िल्टर किया जा सकता है:

results = [(x, y, x/y) for x in input_data if (y := f(x)) > 0]

उसके बाद, चर का दूसरी अभिव्यक्ति में पुन: उपयोग किया जा सकता है:

stuff = [[y := f(x), x/y] for x in range(5)]

कृपया फिर से ध्यान दें कि दोनों मामलों में चर y उसी दायरे में है, जैसा चर परिणाम और सामान है।

शर्तों में मूल्यों पर कब्जा


असाइनमेंट एक्सप्रेशंस को प्रभावी ढंग से इफ़ेक्ट या स्टेटमेंट की शर्तों में इस्तेमाल किया जा सकता है:

# Loop-and-a-half
while (command := input("> ")) != "quit":
    print("You entered:", command)

# Capturing regular expression match objects
# See, for instance, Lib/pydoc.py, which uses a multiline spelling
# of this effect
if match := re.search(pat, text):
    print("Found:", match.group(0))
# The same syntax chains nicely into 'elif' statements, unlike the
# equivalent using assignment statements.
elif match := re.search(otherpat, text):
    print("Alternate found:", match.group(0))
elif match := re.search(third, text):
    print("Fallback found:", match.group(0))

# Reading socket data until an empty string is returned
while data := sock.recv(8192):
    print("Received data:", data)

विशेष रूप से, यह दृष्टिकोण अनंत लूप, असाइनमेंट और कंडीशन चेकिंग बनाने की आवश्यकता को समाप्त कर सकता है। यह आपको एक चक्र के बीच एक चिकनी समानांतर खींचने की भी अनुमति देता है जो एक फ़ंक्शन कॉल को अपनी स्थिति के रूप में उपयोग करता है, साथ ही एक चक्र जो न केवल स्थिति की जांच करता है, बल्कि भविष्य में फ़ंक्शन द्वारा लौटाए गए वास्तविक मूल्य का भी उपयोग करता है।

कांटा


UNIX की निम्न-स्तरीय दुनिया से एक उदाहरण: [लगभग। फोर्क () यूनिक्स जैसे ऑपरेटिंग सिस्टम पर एक सिस्टम कॉल है जो माता-पिता के सापेक्ष एक नई उप-प्रक्रिया बनाता है।]

if pid := os.fork():
    # Parent code
else:
    # Child code

अस्वीकृत विकल्प


सामान्य तौर पर, अजगर समुदाय में समान सुझाव काफी सामान्य हैं। नीचे असाइनमेंट एक्सप्रेशन के लिए कई वैकल्पिक वाक्यविन्यास हैं जो समझने के लिए बहुत विशिष्ट हैं और ऊपर के पक्ष में अस्वीकार कर दिए गए हैं।

जनरेटर के लिए गुंजाइश बदलना


इस पीईपी के पिछले संस्करण में, जनरेटर के लिए गुंजाइश नियमों में सूक्ष्म परिवर्तन उन्हें वर्ग के दायरे में अधिक उपयोगी बनाने के लिए प्रस्तावित किया गया था। हालाँकि, इन प्रस्तावों से पिछड़ी असंगति पैदा होगी और इसलिए इसे अस्वीकार कर दिया गया। इसलिए, यह पीईपी केवल असाइनमेंट एक्सप्रेशंस पर पूरी तरह से ध्यान केंद्रित करने में सक्षम था।

वैकल्पिक मंत्र


सामान्य तौर पर, प्रस्तावित असाइनमेंट अभिव्यक्तियों में एक ही शब्दार्थ होता है, लेकिन इसे अलग तरह से लिखा जाता है।

  1. NAME के ​​रूप में EXPR:

    stuff = [[f(x) as y, x/y] for x in range(5)]

    EXPR as NAME import, except with, (, ).

    ( , «with EXPR as VAR» EXPR VAR, EXPR.__enter__() VAR.)

    , ":=" :
    • , if f(x) as y , ​​ if f x blah-blah, if f(x) and y.
    • , as , , :
      • import foo as bar
      • except Exc as var
      • with ctxmgr() as var

      , as if while , as « » .
    • «»
      • NAME = EXPR
      • if NAME := EXPR

      .
  2. EXPR -> NAME

    stuff = [[f(x) -> y, x/y] for x in range(5)]

    , R Haskell, . ( , - y < — f (x) Python, - .) «as» , import, except with, . Python ( ), ":=" ( Algol-58) .
  3. «»

    stuff = [[(f(x) as .y), x/.y] for x in range(5)] # with "as"
    stuff = [[(.y := f(x)), x/.y] for x in range(5)] # with ":="

    . Python, , .
  4. where: :

    value = x**2 + 2*x where:
        x = spam(1, 4, 7, q)

    ( , «»). , «» ( with:). . PEP 3150, ( given: ).
  5. TARGET from EXPR:

    stuff = [[y from f(x), x/y] for x in range(5)]

    इस सिंटेक्स की तुलना में दूसरों के साथ संघर्ष करने की संभावना कम है (जब तक कि आप एक्स के निर्माण से एक्साइज की गिनती नहीं करते हैं), लेकिन अन्यथा उनके साथ तुलनीय हो। लक्ष्य के रूप में expr के साथ एक समानांतर के बजाय: (जो उपयोगी हो सकता है, लेकिन यह भ्रामक भी हो सकता है), इस विकल्प में किसी भी चीज़ के साथ कोई समानता नहीं है, लेकिन यह आश्चर्यजनक रूप से बेहतर याद है।


सशर्त बयानों में विशेष मामले


असाइनमेंट एक्सप्रेशंस के लिए सबसे आम उपयोग के मामलों में से एक है अगर और जब बयान। अधिक सामान्य समाधान के बजाय, इन दोनों कथनों के वाक्यविन्यास को बेहतर बनाने के लिए उपयोग करके मूल्य को कैप्चर करने के साधनों को जोड़कर तुलना की जा सकती है:

if re.search(pat, text) as match:
    print("Found:", match.group(0))

यह ठीक काम करता है, लेकिन केवल तब जब वांछित स्थिति रिटर्न वैल्यू की "शुद्धता" पर आधारित होती है। इस प्रकार, यह विधि विशिष्ट मामलों के लिए प्रभावी है (नियमित अभिव्यक्तियों की जाँच करना, सॉकेट्स पढ़ना, निष्पादन समाप्त होने पर एक खाली स्ट्रिंग लौटना), और अधिक जटिल मामलों में पूरी तरह से बेकार है (उदाहरण के लिए, जब हालत च (x) <0 है, और आप चाहते हैं च (x) का मान बचाना। इसके अलावा, यह सूची जनरेटर में कोई मतलब नहीं है।

लाभ : कोई वाक्यात्मक अस्पष्टता नहीं। नुकसान : यहां तक ​​कि अगर आप बयानों में इसका उपयोग केवल / अगर करते हैं, तो यह केवल कुछ मामलों में ही अच्छा काम करता है।

जनरेटर में विशेष मामले


असाइनमेंट एक्सप्रेशन के लिए एक और सामान्य उपयोग का मामला जनरेटर (सूची / सेट / तानाशाही और जीनएक्सप्स) है। जैसा कि ऊपर, विशिष्ट समाधानों के लिए सुझाव दिए गए थे।

  1. कहाँ, जाने, या दिया गया:

    stuff = [(y, x/y) where y = f(x) for x in range(5)]
    stuff = [(y, x/y) let y = f(x) for x in range(5)]
    stuff = [(y, x/y) given y = f(x) for x in range(5)]

    इस विधि के परिणामस्वरूप लूप और मुख्य अभिव्यक्ति के बीच एक सबएक्प्रेशन होता है। यह एक अतिरिक्त भाषा कीवर्ड भी पेश करता है, जो टकराव पैदा कर सकता है। तीन विकल्पों में से, जहां सबसे साफ और सबसे पठनीय है, लेकिन संभावित संघर्ष अभी भी मौजूद हैं (उदाहरण के लिए, SQLAlchemy और numpy उनके जहां तरीके, साथ ही tkinter.dnd.Icon मानक पुस्तकालय में हैं)।
  2. NAME = EXPR के साथ:

    stuff = [(y, x/y) with y = f(x) for x in range(5)]

    , , with. . , «» for. C, , . : « «with NAME = EXPR:» , ?»
  3. with EXPR as NAME:

    stuff = [(y, x/y) with f(x) as y for x in range(5)]

    , as, . , for. with

चुने गए तरीके के बावजूद, लूप के माध्यम से जनरेटर और उनके तैनात संस्करणों के बीच एक तेज शब्दार्थ अंतर को पेश किया जाएगा। चर बनाने के चरण को संसाधित किए बिना एक जनरेटर में चक्र को लपेटना असंभव हो जाएगा। एकमात्र कीवर्ड जो इस कार्य के लिए पुन: पेश किया जा सकता है, वह शब्द हैलेकिन यह इसे कोड के विभिन्न भागों में अलग-अलग शब्दार्थ प्रदान करेगा, जिसका अर्थ है कि आपको एक नया कीवर्ड बनाने की आवश्यकता है, लेकिन इसमें बहुत अधिक लागत शामिल है।

कम ऑपरेटर प्राथमिकता


The: = ऑपरेटर की दो तार्किक प्राथमिकताएँ होती हैं। या इसमें यथासंभव कम प्राथमिकता होनी चाहिए (असाइनमेंट ऑपरेटर के बराबर)। या यह तुलना ऑपरेटरों से अधिक पूर्वता लेना चाहिए। तुलना ऑपरेटरों और अंकगणितीय संचालन (सटीक होने के लिए: बिटवाइज या से थोड़ा कम) के बीच अपनी प्राथमिकता को रखने से आप ज्यादातर मामलों में कोष्ठक के बिना करने की अनुमति देंगे जब आप इसका उपयोग करते हैं और करते हैं, क्योंकि यह अधिक संभावना है कि आप पहले कुछ का मूल्य रखना चाहते हैं। इस पर तुलना कैसे की जाएगी:

pos = -1
while pos := buffer.find(search_term, pos + 1) >= 0:
    ...

जैसे ही पाते हैं () रिटर्न -1, लूप समाप्त हो जाता है। यदि: = ऑपरेंड्स को स्वतंत्र रूप से = के रूप में बाँधता है, तो खोज का परिणाम () सबसे पहले तुलना ऑपरेटर में "कैप्चर" किया जाएगा और आमतौर पर ट्रू या गलत लौटाएगा, जो कम उपयोगी है।

यद्यपि यह व्यवहार कई स्थितियों में व्यवहार में सुविधाजनक होगा, यह समझाना अधिक कठिन होगा। और इसलिए हम कह सकते हैं कि "ऑपरेटर: = सामान्य असाइनमेंट ऑपरेटर के समान व्यवहार करता है।" अर्थात्, के लिए प्राथमिकता: = ऑपरेटर के लिए जितना संभव हो उतना करीब चुना गया था = (इसके अलावा: = कोमा की तुलना में प्राथमिकता अधिक है)।

आप दाईं तरफ अल्पविराम देते हैं


कुछ आलोचकों का तर्क है कि असाइनमेंट एक्सप्रेशन को कोष्ठक के जोड़ के बिना ट्यूपल्स को पहचानना चाहिए ताकि दोनों प्रविष्टियाँ बराबर हों:

(point := (x, y))
(point := x, y)

(मानक के वर्तमान संस्करण में, अंतिम रिकॉर्ड अभिव्यक्ति के बराबर होगा ((बिंदु: = x), y)।)

लेकिन यह तर्कसंगत है कि इस परिदृश्य में, फ़ंक्शन कॉल में असाइनमेंट अभिव्यक्ति का उपयोग करते समय, यह कॉमा से भी कम होगा। निम्नलिखित भ्रामक समानता होगी:

foo (x: = 1, y)
foo (x: = (1, y))

और हम केवल कम भ्रामक रास्ता निकालते हैं: = ऑपरेटर को अल्पविराम से कम प्राथमिकता देते हैं।

हमेशा कोष्ठक की आवश्यकता होती है


यह हमेशा असाइनमेंट एक्सप्रेशन को ब्रैकेट करने के लिए प्रस्तावित किया गया है। यह हमें कई अस्पष्टताओं से बचाएगा। वास्तव में, वांछित मूल्य निकालने के लिए कोष्ठकों की अक्सर आवश्यकता होगी। लेकिन निम्नलिखित मामलों में, कोष्ठक की उपस्थिति स्पष्ट रूप से हमें बहुत अच्छी लगी:

# Top level in if
if match := pattern.match(line):
    return match.group(1)

# Short call
len(lines := f.readlines())

बार-बार आपत्ति


क्यों न सिर्फ असाइनमेंट स्टेटमेंट को भावों में बदल दें?


C और इसी तरह की भाषाएं = ऑपरेटर को एक अभिव्यक्ति के रूप में परिभाषित करती हैं , न कि एक निर्देश के रूप में, जैसा कि पायथन करता है। यह कई स्थितियों में असाइनमेंट की अनुमति देता है, जिसमें वे स्थान शामिल हैं जहां चर की तुलना की जाती है। अगर (x == y) और अगर (x = y) के बीच वाक्यविन्यास समानताएं उनके तेज भिन्न शब्दार्थों का खंडन करती हैं। इस प्रकार, यह पीईपी ऑपरेटर का परिचय देता है: = उनके मतभेदों को स्पष्ट करने के लिए।

असाइनमेंट निर्देश मौजूद होने पर असाइनमेंट एक्सप्रेशन से परेशान क्यों ?


इन दो रूपों में अलग-अलग लचीलापन है। ऑपरेटर: = का उपयोग एक बड़ी अभिव्यक्ति के अंदर किया जा सकता है, और = ऑपरेटर में इसका उपयोग "+ =" प्रकार के "मिनी-ऑपरेटरों के परिवार" द्वारा किया जा सकता है। इसके अलावा = आप विशेषताओं और अनुक्रमित द्वारा मूल्यों को निर्दिष्ट करने की अनुमति देता है।

स्थानीय स्कोप का उपयोग क्यों न करें और नाम स्थान के प्रदूषण को रोकें?


इस मानक के पिछले संस्करणों में असाइनमेंट एक्सप्रेशंस के लिए एक वास्तविक स्थानीय गुंजाइश (एक बयान तक सीमित), नाम रिसाव और नाम स्थान प्रदूषण को रोका गया था। इस तथ्य के बावजूद कि कुछ स्थितियों में इसने एक निश्चित लाभ दिया, कई अन्य लोगों में यह कार्य को जटिल बनाता है, और मौजूदा दृष्टिकोण के लाभों से लाभ उचित नहीं हैं। यह भाषा की सरलता के हित में किया जाता है। अब आपको इस चर की आवश्यकता नहीं है? एक समाधान है: डेल कीवर्ड का उपयोग करके वेरिएबल को हटाएं या उसके नाम पर एक निचला अंडरस्कोर जोड़ें।

(लेखक इस दिशा में PEP मानक को आगे बढ़ाने के लिए अपने सुझाव के लिए Guido van Rossum और Christophe Groth को धन्यवाद देना चाहेंगे। [२]]

शैली की सिफारिशें


चूंकि असाइनमेंट एक्सप्रेशन का उपयोग कभी-कभी असाइनमेंट ऑपरेटर के बराबर किया जा सकता है, इसलिए यह सवाल उठता है कि क्या अभी भी पसंद किया जाता है? .. अन्य शैली सम्मेलनों (जैसे पीईपी 8) के अनुसार, दो सिफारिशें हैं:

  1. यदि आप दोनों असाइनमेंट विकल्पों का उपयोग कर सकते हैं, तो ऑपरेटरों को वरीयता दें। वे सबसे स्पष्ट रूप से आपके इरादे व्यक्त करते हैं।
  2. यदि असाइनमेंट एक्सप्रेशन के उपयोग से निष्पादन क्रम में अस्पष्टता आ जाती है, तो क्लासिकल ऑपरेटर का उपयोग करके कोड को फिर से लिखें।

धन्यवाद


इस मानक के लेखक इस पीईपी में उनके महत्वपूर्ण योगदान के लिए निक कॉघलान और स्टीवन डी'अप्रानो को धन्यवाद देना चाहते हैं, साथ ही पायथन कोर मेंटरशिप सदस्यों को इसे लागू करने में उनकी मदद के लिए।

परिशिष्ट A: टिम पीटर्स निष्कर्ष


यहाँ एक छोटा निबंध है जो टिम पीटर्स ने इस विषय पर लिखा है।

मैं "भ्रमित" कोड की तरह नहीं हूं, और एक पंक्ति में वैचारिक रूप से असंबंधित तर्क रखने की तरह भी नहीं है। इसलिए, उदाहरण के लिए, इसके बजाय:

i = j = count = nerrors = 0

मैं लिखना पसंद करता हूँ:

i = j = 0
count = 0
nerrors = 0

इसलिए, मुझे लगता है कि मुझे कई जगह मिलेंगी जहाँ मैं असाइनमेंट एक्सप्रेशन का उपयोग करना चाहता हूँ। मैं उन अभिव्यक्तियों के उपयोग के बारे में भी बात नहीं करना चाहता जो पहले से ही आधी स्क्रीन तक फैली हुई हैं। अन्य मामलों में, ऐसा व्यवहार:

mylast = mylast[1]
yield mylast[0]

इससे काफी बेहतर:

yield (mylast := mylast[1])[0]

इन दो संहिताओं में पूरी तरह से अलग अवधारणाएं हैं और उन्हें मिश्रण करना पागल होगा। अन्य मामलों में, तार्किक अभिव्यक्तियों का संयोजन कोड की समझ को जटिल करता है। उदाहरण के लिए, पुनर्लेखन:

while True:
    old = total
    total += term
    if old == total:
        return total
    term *= mx2 / (i*(i+1))
    i += 2

छोटे रूप में, हमने "तर्क" खो दिया है। आपको यह समझने की आवश्यकता है कि यह कोड कैसे काम करता है। मेरा दिमाग ऐसा नहीं करना चाहता:

while total != (total := total + term):
    term *= mx2 / (i*(i+1))
    i += 2
return total

लेकिन ऐसे मामले दुर्लभ हैं। परिणाम को बनाए रखने का कार्य बहुत आम है, और "विरल घने से बेहतर है" इसका मतलब यह नहीं है कि "लगभग खाली विरल से बेहतर है" [लगभग]। ज़ेन पायथन के संदर्भ में]। उदाहरण के लिए, मेरे पास कई कार्य हैं जो यह बताने के लिए कि मुझे कुछ भी उपयोगी नहीं है, "लेकिन मेरे पास कुछ भी नहीं है, लेकिन चूंकि यह अक्सर होता है, इसलिए मैं आपको अपवादों से परेशान नहीं करना चाहता।" वास्तव में, इस तंत्र का उपयोग नियमित अभिव्यक्तियों में भी किया जाता है जो मैच नहीं होने पर वापस लौट जाते हैं। इसलिए, इस उदाहरण में, बहुत सारे कोड:

result = solution(xs, n)
if result:
    # use result

मुझे निम्नलिखित विकल्प अधिक समझ में आता है, और निश्चित रूप से अधिक पठनीय:

if result := solution(xs, n):
    # use result

पहले तो मैंने इसे बहुत अधिक महत्व नहीं दिया, लेकिन इतना छोटा निर्माण इतनी बार दिखाई दिया कि यह जल्द ही मुझे परेशान करने लगा कि मैं इसका उपयोग नहीं कर सकता। इससे मैं आश्चर्यचकित हुआ! [लगभग। स्पष्ट रूप से यह पायथन 3.8 आधिकारिक तौर पर जारी होने से पहले लिखा गया था।]

ऐसे अन्य मामले हैं जहां असाइनमेंट अभिव्यक्ति वास्तव में "शूट" है। फिर से मेरे कोड में अफवाह फैलाने के बजाय, किरिल बालुनोव ने मानक एएनडीआर से कॉपी () फ़ंक्शन का एक अच्छा उदाहरण दिया:

reductor = dispatch_table.get(cls)
if reductor:
    rv = reductor(x)
else:
    reductor = getattr(x, "__reduce_ex__", None)
    if reductor:
        rv = reductor(4)
    else:
        reductor = getattr(x, "__reduce__", None)
        if reductor:
            rv = reductor()
        else:
            raise Error("un(shallow)copyable object of type %s" % cls)

लगातार बढ़ता आक्रोश भ्रामक है: आखिरकार, तर्क सपाट है: पहला सफल परीक्षण "जीत":

if reductor := dispatch_table.get(cls):
    rv = reductor(x)
elif reductor := getattr(x, "__reduce_ex__", None):
    rv = reductor(4)
elif reductor := getattr(x, "__reduce__", None):
    rv = reductor()
else:
    raise Error("un(shallow)copyable object of type %s" % cls)

असाइनमेंट अभिव्यक्तियों का सरल उपयोग कोड के दृश्य संरचना को तर्क के "विमान" पर जोर देने की अनुमति देता है। लेकिन लगातार बढ़ता आक्रोश इसे अंतर्निहित करता है।

यहां मेरे कोड से एक और छोटा उदाहरण है, जिसने मुझे बहुत खुश किया क्योंकि इसने मुझे आंतरिक रूप से संबंधित तर्क को एक पंक्ति में रखने और कष्टप्रद "कृत्रिम" इंडेंटेशन स्तर को हटाने की अनुमति दी। यह बिल्कुल वैसा ही है जैसा मैं अगर बयान से चाहता हूं और यह पढ़ना आसान बनाता है। निम्नलिखित कोड:

diff = x - x_base
if diff:
    g = gcd(diff, n)
    if g > 1:
        return g

में बदल गया:

if (diff := x - x_base) and (g := gcd(diff, n)) > 1:
    return g

इसलिए, अधिकांश लाइनों में जहां चर असाइनमेंट होता है, मैं असाइनमेंट एक्सप्रेशन का उपयोग नहीं करता। लेकिन यह डिजाइन इतनी लगातार है कि अभी भी कई जगह हैं जहां मैं इस अवसर को ले जाऊंगा। हाल के मामलों में, मैं थोड़ा जीता, क्योंकि वे अक्सर दिखाई देते थे। शेष उप-भाग में, इससे मध्यम या बड़े सुधार हुए। इस प्रकार, मैं असाइनमेंट एक्सप्रेशंस का उपयोग कई बार ट्रिपल से अधिक करेगा यदि, लेकिन संवर्धित असाइनमेंट की तुलना में बहुत कम [लगभग]। संक्षिप्त विकल्प: * =, / =, + =, आदि]।

संख्यात्मक उदाहरण


मेरे पास एक और उदाहरण है जिसने मुझे पहले मारा था।

यदि सभी चर सकारात्मक पूर्णांक हैं, और चर एक x की nth रूट से अधिक है, तो यह एल्गोरिथ्म x की nth रूट की "कम" गोलाई (और लगभग प्रति बिट सटीक बिट की संख्या को दोगुना) करता है:

while a > (d := x // a**(n-1)):
    a = ((n-1)*a + d) // n
return a

यह स्पष्ट नहीं है कि क्यों, लेकिन एल्गोरिथ्म का ऐसा संस्करण एक सशर्त शाखा ब्रेक (लूप और एक आधा) के साथ अनंत लूप की तुलना में कम स्पष्ट है। गणितीय कथन ("अंकगणित माध्य - ज्यामितीय माध्य असमानता") पर निर्भर किए बिना इस कार्यान्वयन की शुद्धता को साबित करना भी मुश्किल है और नस्टेड राउंडिंग फ़ंक्शन नीचे की ओर कैसे व्यवहार करते हैं, इसके बारे में कुछ गैर-तुच्छ चीजों को नहीं जानना। लेकिन यहां समस्या पहले से ही गणित में है, और प्रोग्रामिंग में नहीं।

और अगर आप यह सब जानते हैं, तो असाइनमेंट एक्सप्रेशंस का उपयोग करने का विकल्प एक सरल वाक्य की तरह बहुत आसानी से पढ़ा जाता है: "वर्तमान" अनुमान "की जांच करें और यदि यह बहुत बड़ा है, तो इसे कम करें" और स्थिति आपको लूप स्थिति से मध्यवर्ती मान को तुरंत बचाने की अनुमति देती है। मेरी राय में, क्लासिक रूप समझना कठिन है:

while True:
    d = x // a**(n-1)
    if a <= d:
        break
    a = ((n-1)*a + d) // n
return a

परिशिष्ट बी: जनरेटर के लिए एक कठिन कोड दुभाषिया


यह परिशिष्ट उन नियमों को स्पष्ट (यद्यपि निर्दिष्ट नहीं) करने का प्रयास करता है जिनके द्वारा जनरेटर अभिव्यक्तियों में एक चर बनाया जाना चाहिए। कई उदाहरणों के लिए, हम स्रोत कोड दिखाते हैं जहां जनरेटर को कुछ "मचान" के संयोजन में एक समान फ़ंक्शन द्वारा बदल दिया जाता है।

चूंकि [x for ...] सूची के बराबर है (x for ...), उदाहरण अपनी व्यापकता नहीं खोते हैं। और चूंकि इन उदाहरणों का उद्देश्य केवल सामान्य नियमों को स्पष्ट करना है, इसलिए वे यथार्थवादी होने का दावा नहीं करते हैं।

नोट: जनरेटर अब नेस्टेड जनरेटर फ़ंक्शन (इस परिशिष्ट में दिए गए लोगों के समान) के निर्माण के माध्यम से कार्यान्वित किए जाते हैं। उदाहरण नए हिस्से को दिखाते हैं, जो असाइनमेंट एक्सप्रेशंस के दायरे के साथ काम करने के लिए उपयुक्त कार्यक्षमता जोड़ता है (जैसे कि स्कोप को सबसे बाहरी जनरेटर वाले ब्लॉक में असाइन किया गया था)। "प्रकार के अनुमान" को सरल बनाने के लिए, ये उदाहरणात्मक उदाहरण इस बात पर ध्यान नहीं देते हैं कि असाइनमेंट एक्सप्रेशन वैकल्पिक हैं (लेकिन वे जनरेटर के अंदर बनाए गए चर के दायरे को ध्यान में रखते हैं)।

आइए पहले याद रखें कि असाइनमेंट एक्सप्रेशन के बिना जनरेटर के लिए "हुड के नीचे" कोड क्या उत्पन्न होता है:

  • स्रोत कोड (EXPR सबसे अधिक बार VAR चर का उपयोग करता है):

    def f():
        a = [EXPR for VAR in ITERABLE]
  • परिवर्तित कोड (नाम संघर्ष की चिंता न करें):

    def f():
        def genexpr(iterator):
            for VAR in iterator:
                yield EXPR
        a = list(genexpr(iter(ITERABLE)))


आइए एक सरल असाइनमेंट अभिव्यक्ति जोड़ें।

  • स्रोत:

    def f():
        a = [TARGET := EXPR for VAR in ITERABLE]
    
  • परिवर्तित कोड:

    def f():
        if False:
            TARGET = None  # Dead code to ensure TARGET is a local variable
        def genexpr(iterator):
            nonlocal TARGET
            for VAR in iterator:
                TARGET = EXPR
                yield TARGET
        a = list(genexpr(iter(ITERABLE)))

अब चलिए f () फ़ंक्शन की घोषणा के लिए वैश्विक TARGET स्टेटमेंट जोड़ते हैं।

  • स्रोत:

    def f():
        global TARGET
        a = [TARGET := EXPR for VAR in ITERABLE]
    
  • परिवर्तित कोड:

    def f():
        global TARGET
        def genexpr(iterator):
            global TARGET
            for VAR in iterator:
                TARGET = EXPR
                yield TARGET
        a = list(genexpr(iter(ITERABLE)))

या इसके विपरीत, आइए f () फ़ंक्शन की घोषणा में गैर-तालिक लक्ष्य जोड़ें।

  • स्रोत:

    def g():
        TARGET = ...
        def f():
            nonlocal TARGET
            a = [TARGET := EXPR for VAR in ITERABLE]
    
  • परिवर्तित कोड:

    def g():
        TARGET = ...
        def f():
            nonlocal TARGET
            def genexpr(iterator):
                nonlocal TARGET
                for VAR in iterator:
                    TARGET = EXPR
                    yield TARGET
            a = list(genexpr(iter(ITERABLE)))

अंत में, चलो दो जनरेटर में डालते हैं।

  • स्रोत:

    def f():
        a = [[TARGET := i for i in range(3)] for j in range(2)]
        # I.e., a = [[0, 1, 2], [0, 1, 2]]
        print(TARGET)  # prints 2
    
  • परिवर्तित कोड:

    def f():
        if False:
            TARGET = None
        def outer_genexpr(outer_iterator):
            nonlocal TARGET
            def inner_generator(inner_iterator):
                nonlocal TARGET
                for i in inner_iterator:
                    TARGET = i
                    yield i
            for j in outer_iterator:
                yield list(inner_generator(range(3)))
        a = list(outer_genexpr(range(2)))
        print(TARGET)

परिशिष्ट C: स्कोप शब्दार्थ में कोई परिवर्तन नहीं


ध्यान दें कि पायथन में स्कोप शब्दार्थ नहीं बदला है। स्थानीय कार्यों का दायरा अभी भी संकलन समय पर निर्धारित किया गया है और रनटाइम (बंद) में अनिश्चित समय सीमा है। उदाहरण:

a = 42
def f():
    # `a` is local to `f`, but remains unbound
    # until the caller executes this genexp:
    yield ((a := i) for i in range(3))
    yield lambda: a + 100
    print("done")
    try:
        print(f"`a` is bound to {a}")
        assert False
    except UnboundLocalError:
        print("`a` is not yet bound")

फिर:

>>> results = list(f()) # [genexp, lambda]
done
`a` is not yet bound
# The execution frame for f no longer exists in CPython,
# but f's locals live so long as they can still be referenced.
>>> list(map(type, results))
[<class 'generator'>, <class 'function'>]
>>> list(results[0])
[0, 1, 2]
>>> results[1]()
102
>>> a
42

संदर्भ


  1. अवधारणा कार्यान्वयन का प्रमाण
  2. असाइनमेंट एक्सप्रेशंस के शब्दार्थ की चर्चा (वीपीएन तंग है लेकिन भरी हुई है)
  3. PEP 572 में TargetScopeError की चर्चा (पिछले एक के समान लोड की गई)

कॉपीराइट


यह दस्तावेज़ सार्वजनिक रूप से उपलब्ध कराया गया है।

स्रोत: github.com/python/peps/blob/master/pep-0572.rst

मेरा पक्ष


शुरू करने के लिए, आइए संक्षेप करते हैं:
  • ताकि लोग कई "शास्त्रीय" स्थानों में शब्दार्थिक द्वंद्व को दूर करने की कोशिश न करें , जहां कोई "=" और ": =" दोनों का उपयोग कर सकता है, इसलिए प्रतिबंध हैं, इसलिए ऑपरेटर :: = अक्सर कोष्ठक में संलग्न होना चाहिए। मूल उपयोग का वर्णन करने वाले अनुभाग में इन मामलों की समीक्षा की जाएगी
  • असाइनमेंट एक्सप्रेशन की प्राथमिकता कॉमा की तुलना में थोड़ी अधिक है। इसके कारण, असाइनमेंट के दौरान ट्यूपल्स नहीं बनते हैं। किसी फ़ंक्शन के लिए तर्क पास करते समय: = = ऑपरेटर का उपयोग करना भी संभव बनाता है।
  • , , , . . lambda , «» .
  • : ,
  • , .
  • / .
  • , .

अंत में, मैं कहना चाहता हूं कि मुझे नया ऑपरेटर पसंद आया। यह आपको शर्तों में चापलूसी कोड लिखने की अनुमति देता है, "फ़िल्टर" सूचियाँ, और (अंत में) "उसी" को हटा दें, यदि अकेला हो तो। यदि लोग अपने इच्छित उद्देश्य के लिए असाइनमेंट एक्सप्रेशन का उपयोग करते हैं, तो यह एक बहुत ही सुविधाजनक उपकरण होगा जो कोड की पठनीयता और सुंदरता को बढ़ाएगा (हालांकि, यह किसी भी कार्यात्मक भाषा के बारे में कहा जा सकता है ....)

All Articles