2013-04-11 3 views
49

मैं आज मेरी codebase के माध्यम से देख रही है और इस मिला था:यह लैम्ब्डा/उपज/जेनरेटर समझ कैसे काम करती है?

def optionsToArgs(options, separator='='): 
    kvs = [ 
     (
      "%(option)s%(separator)s%(value)s" % 
      {'option' : str(k), 'separator' : separator, 'value' : str(v)} 
     ) for k, v in options.items() 
    ] 
    return list(
     reversed(
      list(
        (lambda l, t: 
         (lambda f: 
          (f((yield x)) for x in l) 
         )(lambda _: t) 
        )(kvs, '-o') 
       ) 
      ) 
     ) 

यह मानकों का एक dict लेते हैं और उन्हें एक शेल कमांड के लिए पैरामीटर की एक सूची में बदल रहा है। ऐसा लगता है कि यह जनरेटर समझ के भीतर उपज का उपयोग कर रहा है, जिसे मैंने असंभव समझा होगा ...?

>>> optionsToArgs({"x":1,"y":2,"z":3}) 
['-o', 'z=3', '-o', 'x=1', '-o', 'y=2'] 

यह कैसे काम करता है?

+66

डैंग किया गया है हो सकता है। अपठनीय कोड के बारे में बात करो। – BenDundee

+2

सबसे मजेदार हिस्सा 'सूची है (' (('स्विच सही करने के लिए' सूची ('भाग' है, हालांकि – ch3ka

+5

भी सभी लैम्बडा बस '((लैम्ब्डा _: '-o') (उपज एक्स)) एक्स केवीएस में एक्स के लिए) ' –

उत्तर

47

अजगर 2.5 के बाद से, yield <value> एक अभिव्यक्ति है, न कि एक बयान है। PEP 342 देखें।

कोड घोर रूप से और अनावश्यक रूप से बदसूरत है, लेकिन यह कानूनी है। इसकी केंद्रीय चाल जनरेटर अभिव्यक्ति के अंदर f((yield x)) का उपयोग कर रही है। यह इस तरह का एक सरल उदाहरण है:

>>> def f(val): 
...  return "Hi" 
>>> x = [1, 2, 3] 
>>> list(f((yield a)) for a in x) 
[1, 'Hi', 2, 'Hi', 3, 'Hi'] 

मूल रूप से, में जनरेटर अभिव्यक्ति यह कारण बनता है स्रोत iterable में हर मूल्य के लिए दो मानों का उत्पादन करने के yield का उपयोग कर। चूंकि जनरेटर अभिव्यक्ति तारों की सूची में पुनरावृत्त होती है, प्रत्येक पुनरावृत्ति पर, yield x पहले सूची से एक स्ट्रिंग उत्पन्न करता है। जीनएक्सपी की लक्षित अभिव्यक्ति f((yield x)) है, इसलिए सूची में प्रत्येक मूल्य के लिए, जनरेटर अभिव्यक्ति का "परिणाम" f((yield x)) का मान है। लेकिन f सिर्फ इसके तर्क को अनदेखा करता है और हमेशा विकल्प स्ट्रिंग "-o" देता है। तो जनरेटर के माध्यम से हर कदम पर, यह पहले कुंजी-मूल्य स्ट्रिंग उत्पन्न करता है (उदा।, "x=1"), फिर "-o"। बाहरी list(reversed(list(...))) सिर्फ इस जनरेटर से एक सूची बनाता है और फिर इसे उलट देता है ताकि "-o" एस इसके बजाय प्रत्येक विकल्प से पहले आएगा।

हालांकि, वहाँ यह इस तरह से करने के लिए कोई कारण नहीं है। कई और अधिक पठनीय विकल्प हैं। शायद सबसे स्पष्ट बस है:

kvs = [...] # same list comprehension can be used for this part 
result = [] 
for keyval in kvs: 
    result.append("-o") 
    result.append(keyval) 
return result 

भले ही आप संक्षिप्त पसंद है, "चालाक" कोड, आप अभी भी सिर्फ

return sum([["-o", keyval] for keyval in kvs], []) 

kvs सूची समझ ही प्रयास किया पठनीयता का एक विचित्र मिश्रण है कर सकता है और unreadability। यह अधिक सरल लिखा गया है:

kvs = [str(optName) + separator + str(optValue) for optName, optValue in options.items()] 

आपको इसे अपने कोडबेस में रखने के लिए "हस्तक्षेप" की व्यवस्था करने पर विचार करना चाहिए।

+3

देख रहे हैं इतिहास में, यह होता था: 'वापसी सूची (itertools.chain (* [[' - o ', v] केवी में v के लिए])।' यह स्पष्ट नहीं है कि इसे क्यों बदला गया। – Dog

+1

@Dog केवल परिवर्तन मैं आपकी टिप्पणी में कोड से करना चाहता हूं ताकि 'itertools.chain.from_iterable' से बचें '*' का उपयोग करना (यदि सूची बड़ी है तो महंगा हो सकता है) ... – Bakuriu

19

हे भगवान। असल में, यह निर्भर करता इस ,: को

def f(_):    # I'm the lambda _: t 
    return '-o' 

def thegenerator(): # I'm (f((yield x)) for x in l) 
    for x in kvs: 
     yield f((yield x)) 

तो जब से अधिक दोहराया, thegenerator पैदावार x (kvs के एक सदस्य) और फिर f की वापसी मूल्य है, जो हमेशा -o है, सभी kvs के ऊपर एक चरण में। जो भी yield x रिटर्न और f पर जो भी हो जाता है उसे अनदेखा किया जाता है।

समकक्ष:

def thegenerator(): # I'm (f((yield x)) for x in l) 
    for x in kvs: 
     whatever = (yield x) 
     yield f(whatever) 

def thegenerator(): # I'm (f((yield x)) for x in l) 
    for x in kvs: 
     yield x 
     yield f(None) 

def thegenerator(): # I'm (f((yield x)) for x in l) 
    for x in kvs: 
     yield x 
     yield '-o' 

तरीके निश्चित रूप से बहुत सरल ऐसा करने के लिए, के बहुत सारे हैं।यहां तक ​​कि मूल डबल उपज चाल के साथ, पूरी बात

return list(((lambda _: '-o')((yield x)) for x in kvs))[::-1] 
संबंधित मुद्दे