फ़ंक्शन तर्क के रूप में जनरेटर


81

क्या कोई समझा सकता है कि एक फ़ंक्शन के एकमात्र एकमात्र तर्क के रूप में एक जनरेटर को पारित करना विशेष नियम क्यों लगता है?

अगर हमारे पास है:

def f(*args):
    print "Success!"
    print args
  1. यह उम्मीद के मुताबिक काम करता है।

    >>> f(1, *[2])
    Success!
    (1, 2)
    
  2. यह अपेक्षा के अनुरूप काम नहीं करता है।

    >>> f(*[2], 1)
      File "<stdin>", line 1
    SyntaxError: only named arguments may follow *expression
    
  3. यह उम्मीद के मुताबिक काम करता है

    >>> f(1 for x in [1], *[2])
    Success! 
    (generator object <genexpr> at 0x7effe06bdcd0>, 2)
    
  4. यह काम करता है, लेकिन मुझे समझ नहीं आता कि क्यों। यह 2 के रूप में उसी तरह विफल नहीं होना चाहिए)

    >>> f(*[2], 1 for x in [1])
    Success!
    (generator object <genexpr> at 0x7effe06bdcd0>, 2)
    

1
एक सटीक डुप्लिकेट नहीं है, लेकिन काफी समान है: stackoverflow.com/questions/12720450/… । टीएल; डीआर ऐसा लगता है जैसे यह एक कार्यान्वयन विवरण है - यह बस उसी तरह काम करता है।
J0HN

2
नोट: केस 2 को अजगर 3.5+ ( पीईपी 448 के कारण ) में काम करना चाहिए
बकुरीउ

1
पायथन 3.5 बाहर है, और अब यह बताता है कि मामला 3 (वास्तव में भी मामला 4) तय किया गया है। पायथन 3.5 में नया क्या है
एंट्टी हवाला

जवाबों:


76

दोनों 3. और 4. सभी पायथन संस्करणों पर वाक्यविन्यास त्रुटियां होनी चाहिए। हालाँकि आपको एक बग मिल गया है जो पायथन संस्करणों को 2.5 - 3.4 को प्रभावित करता है, और जिसे बाद में पायथन इश्यू ट्रैकर में पोस्ट किया गया । बग की वजह से, एक अपरिवर्तित जनरेटर अभिव्यक्ति को एक फ़ंक्शन के तर्क के रूप में स्वीकार किया गया था यदि यह केवल *argsऔर / या के साथ था **kwargs। जबकि पाइथन 2.6+ ने दोनों मामलों को अनुमति दी 3. और 4., पायथन 2.5 ने केवल केस 3 की अनुमति दी - - फिर भी दोनों दस्तावेज व्याकरण के खिलाफ थे :

call    ::=     primary "(" [argument_list [","]
                            | expression genexpr_for] ")"

प्रलेखन का कहना है कि एक फ़ंक्शन कॉल में primary(अभिव्यक्ति जो कॉल करने योग्य का मूल्यांकन करता है) शामिल है, उसके बाद, कोष्ठक में, या तो एक तर्क सूची या बस एक अनिर्धारित जनरेटर अभिव्यक्ति; और तर्क सूची के भीतर, सभी जनरेटर अभिव्यक्तियाँ कोष्ठक में होनी चाहिए।


यह बग (हालांकि ऐसा लगता है कि यह ज्ञात नहीं था), पायथन 3.5 प्रीरेलिस में तय किया गया था। पायथन में 3.5 कोष्ठक हमेशा एक जनरेटर अभिव्यक्ति के आसपास आवश्यक होते हैं, जब तक कि यह फ़ंक्शन का एकमात्र तर्क नहीं है:

Python 3.5.0a4+ (default:a3f2b171b765, May 19 2015, 16:14:41) 
[GCC 4.9.2] on linux
Type "help", "copyright", "credits" or "license" for more information.
>>> f(1 for i in [42], *a)
  File "<stdin>", line 1
SyntaxError: Generator expression must be parenthesized if not sole argument

यह अब Python 3.5 में व्हाट्स न्यू में प्रलेखित है , इस बग को चिह्नित करने के लिए DeTeReR का धन्यवाद।


बग का विश्लेषण

पायथन 2.6 में एक परिवर्तन किया गया था जिसके बाद खोजशब्द तर्क के उपयोग की अनुमति दी गई *args :

किसी फ़ंक्शन कॉल के लिए * args तर्क के बाद कीवर्ड तर्क प्रदान करना कानूनी हो गया है।

>>> def f(*args, **kw):
...     print args, kw
...
>>> f(1,2,3, *(4,5,6), keyword=13)
(1, 2, 3, 4, 5, 6) {'keyword': 13}

पहले यह एक सिंटैक्स त्रुटि होती। (अमौरी फोर्जोट डी आर्क द्वारा योगदान; मुद्दा 3473।)


हालाँकि, पायथन 2.6 व्याकरण कीवर्ड तर्क, स्थिति तर्क, या नंगे जनरेटर के भावों के बीच कोई अंतर नहीं करता है - वे सभी प्रकार argumentके पार्सर हैं।

पायथन नियमों के अनुसार, यदि यह फ़ंक्शन का एकमात्र तर्क नहीं है, तो एक जनरेटर अभिव्यक्ति को छोटा किया जाना चाहिए। इसमें मान्य है Python/ast.c:

for (i = 0; i < NCH(n); i++) {
    node *ch = CHILD(n, i);
    if (TYPE(ch) == argument) {
        if (NCH(ch) == 1)
            nargs++;
        else if (TYPE(CHILD(ch, 1)) == gen_for)
            ngens++;
        else
            nkeywords++;
    }
}
if (ngens > 1 || (ngens && (nargs || nkeywords))) {
    ast_error(n, "Generator expression must be parenthesized "
              "if not sole argument");
    return NULL;
}

हालांकि यह फ़ंक्शन बिल्कुल भी विचार नहीं करता है *args- यह विशेष रूप से केवल सामान्य स्थिति संबंधी तर्क और कीवर्ड तर्क के लिए दिखता है।

उसी फ़ंक्शन में नीचे, कीवर्ड arg के बाद गैर-कीवर्ड arg के लिए एक त्रुटि संदेश उत्पन्न होता है :

if (TYPE(ch) == argument) {
    expr_ty e;
    if (NCH(ch) == 1) {
        if (nkeywords) {
            ast_error(CHILD(ch, 0),
                      "non-keyword arg after keyword arg");
            return NULL;
        }
        ...

लेकिन यह फिर से उन दलीलों पर लागू होता है जो कथन द्वारा प्रमाण के रूप में अनपेक्षित रूप से जनरेटर के भाव नहीं हैं :else if

else if (TYPE(CHILD(ch, 1)) == gen_for) {
    e = ast_for_genexp(c, ch);
    if (!e)
        return NULL;
    asdl_seq_SET(args, nargs++, e);
}

इस प्रकार एक अपरिवर्तित जनरेटर अभिव्यक्ति को पास करने की अनुमति दी गई थी।


अब पायथन 3.5 *argsमें एक फ़ंक्शन कॉल में कहीं भी उपयोग किया जा सकता है , इसलिए इसके लिए समायोजित करने के लिए व्याकरण को बदल दिया गया था:

arglist: argument (',' argument)*  [',']

तथा

argument: ( test [comp_for] |
            test '=' test |
            '**' test |
            '*' test )

और forपाश बदल गया था करने के लिए

for (i = 0; i < NCH(n); i++) {
    node *ch = CHILD(n, i);
    if (TYPE(ch) == argument) {
        if (NCH(ch) == 1)
            nargs++;
        else if (TYPE(CHILD(ch, 1)) == comp_for)
            ngens++;
        else if (TYPE(CHILD(ch, 0)) == STAR)
            nargs++;
        else
            /* TYPE(CHILD(ch, 0)) == DOUBLESTAR or keyword argument */
            nkeywords++;
    }
}

इस प्रकार बग को ठीक करना।

हालांकि अनजाने में बदलाव वैध वैध निर्माण है

func(i for i in [42], *args)

तथा

func(i for i in [42], **kwargs)

जहां एक अपरिवर्तित जनरेटर पहले से काम करता है *argsया **kwargsअब काम करना बंद कर देता है।


इस बग का पता लगाने के लिए, मैंने विभिन्न पायथन संस्करणों की कोशिश की। 2.5 में आपको मिलेगा SyntaxError:

Python 2.5.5 (r255:77872, Nov 28 2010, 16:43:48) 
[GCC 4.4.5] on linux2
Type "help", "copyright", "credits" or "license" for more information.
>>> f(*[1], 2 for x in [2])
  File "<stdin>", line 1
    f(*[1], 2 for x in [2])

और यह पायथन 3.5 के कुछ पूर्वगामी से पहले तय किया गया था:

Python 3.5.0a4+ (default:a3f2b171b765, May 19 2015, 16:14:41) 
[GCC 4.9.2] on linux
Type "help", "copyright", "credits" or "license" for more information.
>>> f(*[1], 2 for x in [2])
  File "<stdin>", line 1
SyntaxError: Generator expression must be parenthesized if not sole argument

हालाँकि, पैरेंटाइज़्ड जनरेटर एक्सप्रेशन, यह पायथन 3.5 में काम करता है, लेकिन यह पायथन 3.4 में काम नहीं करता है:

f(*[1], (2 for x in [2]))

और यह सुराग है। पायथन 3.5 में *splattingसामान्यीकृत है; आप इसे फंक्शन कॉल में कहीं भी उपयोग कर सकते हैं:

>>> print(*range(5), 42)
0 1 2 3 4 42

तो वास्तविक बग ( *starकोष्ठकों के बिना काम करने वाला जनरेटर ) वास्तव में अजगर 3.5 में तय किया गया था , और बग में पाया जा सकता है कि पायथन 3.4 और 3.5 के बीच क्या बदल गया है


1
यह 3.5 में तय नहीं है - बस जनरेटर के चारों ओर परेंस लगाए और व्यवहार समान है।
विराटपुर

1
@viraptor अच्छा बिंदु, 3.4 में कोष्ठक अभिव्यक्ति एक त्रुटि देता है
एंटी हापला

हुह? 3.4.3 पर चल रहा है: f(*[1], 1 for x in [1])=>(<generator object <genexpr> at 0x7fa56c889288>, 1)
viraptor

@viraptor f(*[1], (1 for x in [1]))पायथन 3.4 पर वाक्यविन्यास त्रुटि है। यह पायथन 3.5 में मान्य है।
अंती हापाला

अगर मैं कर सकता था, तो मैं इस उत्तर को हल्का करूंगा, प्रासंगिक सी स्रोत को शामिल करने के लिए धन्यवाद!
निक स्वीटिंग
हमारी साइट का प्रयोग करके, आप स्वीकार करते हैं कि आपने हमारी Cookie Policy और निजता नीति को पढ़ और समझा लिया है।
Licensed under cc by-sa 3.0 with attribution required.