सूची ब्योरा बनाम नक्शा


732

क्या map()सूची समझ या इसके विपरीत का उपयोग करने को प्राथमिकता देना है ? क्या उनमें से कोई भी आमतौर पर अधिक कुशल है या आम तौर पर अन्य की तुलना में अधिक पायथोनिक माना जाता है?


8
ध्यान दें कि यदि आप सूची समझ के बजाय मानचित्र का उपयोग करते हैं, तो PyLint चेतावनी देता है, संदेश W0141 देखें ।
लुम्ब्रिक

2
@lumbric, मुझे यकीन नहीं है लेकिन यह केवल तभी होता है जब लैम्बडा का उपयोग मानचित्र में किया जाता है।
0xc0de

जवाबों:


660

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

वास्तव में एक ही फ़ंक्शन का उपयोग करते समय मानचित्र की छोटी गति लाभ का एक उदाहरण:

$ python -mtimeit -s'xs=range(10)' 'map(hex, xs)'
100000 loops, best of 3: 4.86 usec per loop
$ python -mtimeit -s'xs=range(10)' '[hex(x) for x in xs]'
100000 loops, best of 3: 5.58 usec per loop

प्रदर्शन की तुलना पूरी तरह से विपरीत हो जाती है, जब मानचित्र को लंबोदर की आवश्यकता होती है, इसका एक उदाहरण:

$ python -mtimeit -s'xs=range(10)' 'map(lambda x: x+2, xs)'
100000 loops, best of 3: 4.24 usec per loop
$ python -mtimeit -s'xs=range(10)' '[x+2 for x in xs]'
100000 loops, best of 3: 2.32 usec per loop

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

46
एलेक्स की अनंत शैली के बिंदुओं पर कबाश करने के लिए नहीं, लेकिन कभी-कभी मानचित्र मुझे पढ़ना आसान लगता है: डेटा = मानचित्र (str, some_list_of_objects)। कुछ अन्य ... oper.attrgetter, operator.itemgetter, आदि
ग्रीग लिंड

57
map(operator.attrgetter('foo'), objs)से पढ़ने में आसान [o.foo for o in objs]!
एलेक्स मार्टेली

52
@ एलेक्स: मैं अनावश्यक नामों को पेश नहीं करना पसंद करता हूं, जैसे oयहां, और आपके उदाहरण बताते हैं कि क्यों।
बार्टन

29
मुझे लगता है कि @GreggLind के पास एक str()उदाहरण है, हालांकि उनका उदाहरण है।
एरिक ओ लेबिगॉट

474

मामले

  • सामान्य मामला : लगभग हमेशा, आप अजगर में एक सूची समझ का उपयोग करना चाहेंगे क्योंकि यह अधिक स्पष्ट होगा कि आप अपने कोड को पढ़ रहे नौसिखिए प्रोग्रामर को क्या कर रहे हैं। (यह अन्य भाषाओं पर लागू नहीं होता है, जहां अन्य मुहावरे लागू हो सकते हैं।) यह और भी स्पष्ट होगा कि आप अजगर प्रोग्रामर के लिए क्या कर रहे हैं, क्योंकि सूची बोध के बाद से पुनरावृत्ति के लिए अजगर में डी-फैक्टो मानक हैं; वे अपेक्षित हैं
  • कम-सामान्य मामला : हालांकि यदि आपके पास पहले से ही कोई फ़ंक्शन परिभाषित है , तो इसका उपयोग करना अक्सर उचित होता है map, हालांकि इसे 'अनीफैथोनिक' माना जाता है। उदाहरण के लिए, map(sum, myLists)की तुलना में अधिक सुरुचिपूर्ण / कविता है [sum(x) for x in myLists]। आपको डमी वैरिएबल (जैसे sum(x) for x...या sum(_) for _...या sum(readableName) for readableName...) जो आपको दो बार टाइप करना है, केवल इट्रेट करने के लिए नहीं होने की लालित्य प्राप्त करता है । एक ही तर्क मॉड्यूल के लिए filterऔर reduceकुछ भी रखता है itertools: यदि आपके पास पहले से ही कोई फ़ंक्शन है, तो आप आगे बढ़ सकते हैं और कुछ कार्यात्मक प्रोग्रामिंग कर सकते हैं। यह कुछ स्थितियों में पठनीयता हासिल करता है, और इसे दूसरों में खो देता है (जैसे नौसिखिए प्रोग्रामर, कई तर्क) ... लेकिन आपके कोड की पठनीयता वैसे भी आपकी टिप्पणियों पर निर्भर करती है।
  • लगभग कभी नहीं : आप mapकार्यात्मक प्रोग्रामिंग करते समय एक शुद्ध सार फ़ंक्शन के रूप में फ़ंक्शन का उपयोग करना चाह सकते हैं , जहां आप मैपिंग कर रहे हैं map, या करी कर रहे हैं map, या अन्यथा mapफ़ंक्शन के बारे में बात करने से लाभ उठा सकते हैं । उदाहरण के लिए हास्केल में, एक फ़ंक्शनल इंटरफ़ेस जिसे fmapकिसी भी डेटा संरचना पर मैपिंग को सामान्यीकृत किया जाता है। यह अजगर में बहुत ही असामान्य है क्योंकि अजगर व्याकरण आपको पुनरावृत्ति के बारे में बात करने के लिए जनरेटर-शैली का उपयोग करने के लिए मजबूर करता है; आप इसे आसानी से सामान्य नहीं कर सकते। (यह कभी-कभी अच्छा होता है और कभी-कभी बुरा होता है।) आप शायद दुर्लभ अजगर उदाहरणों के साथ आ सकते हैं, जहां map(f, *lists)करना उचित है। निकटतम उदाहरण मैं साथ आ सकता हूं sumEach = partial(map,sum), जो कि एक-लाइनर है जो बहुत ही समान है:

def sumEach(myLists):
    return [sum(_) for _ in myLists]
  • सिर्फ for-लूप का उपयोग करना : आप निश्चित रूप से सिर्फ फॉर-लूप का उपयोग कर सकते हैं। हालांकि एक कार्यात्मक-प्रोग्रामिंग दृष्टिकोण से उतना सुंदर नहीं है, कभी-कभी गैर-स्थानीय चर, अनिवार्य प्रोग्रामिंग भाषाओं जैसे अजगर में कोड को स्पष्ट कर देते हैं, क्योंकि लोग उस तरह से कोड पढ़ने के लिए उपयोग किए जाते हैं। फ़ोर-लूप भी हैं, आम तौर पर, सबसे अधिक कुशल जब आप किसी भी जटिल ऑपरेशन को कर रहे होते हैं जो सूची-बोध और मानचित्र जैसी सूची का निर्माण नहीं कर रहे होते हैं (जैसे संक्षेप में, या एक पेड़ बनाने के लिए, आदि) - कम से कम स्मृति के संदर्भ में कुशल (समय के संदर्भ में जरूरी नहीं, जहां मैं सबसे खराब कारक की उम्मीद करूं, कुछ दुर्लभ रोग-कचरा-संग्रह हिचकी को रोकना)।

"Pythonism"

मैं "पाइथोनिक" शब्द को नापसंद करता हूं क्योंकि मुझे नहीं लगता कि पाइथोनिक मेरी आंखों में हमेशा सुरुचिपूर्ण है। फिर भी, mapऔर filter(इसी तरह के उपयोगी itertoolsमॉड्यूल की तरह) कार्यों को शैली के संदर्भ में संभवतः अनैफोनिक माना जाता है।

आलस्य

दक्षता के संदर्भ में, अधिकांश कार्यात्मक प्रोग्रामिंग निर्माणों की तरह, MAP CAN BE LAZY , और वास्तव में अजगर में आलसी है। इसका मतलब है कि आप ऐसा कर सकते हैं ( python3 में ) और आपका कंप्यूटर मेमोरी से बाहर नहीं चलेगा और आपके सभी सहेजे न गए डेटा को खो देगा:

>>> map(str, range(10**100))
<map object at 0x2201d50>

एक सूची समझने की कोशिश करें:

>>> [str(n) for n in range(10**100)]
# DO NOT TRY THIS AT HOME OR YOU WILL BE SAD #

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

>>> (str(n) for n in range(10**100))
<generator object <genexpr> at 0xacbdef>

आप मूल रूप से [...]वाक्य रचनाकार को सूची निर्माणकर्ता की तरह जनरेटर अभिव्यक्ति में पास करने के बारे में सोच सकते हैं , जैसे list(x for x in range(5))

संक्षिप्त संक्षिप्त उदाहरण

from operator import neg
print({x:x**2 for x in map(neg,range(5))})

print({x:x**2 for x in [-y for y in range(5)]})

print({x:x**2 for x in (-y for y in range(5))})

सूची की समझ गैर-आलसी है, इसलिए अधिक मेमोरी की आवश्यकता हो सकती है (जब तक कि आप जनरेटर की समझ का उपयोग न करें)। वर्ग कोष्ठक [...]अक्सर चीजों को स्पष्ट करते हैं, खासकर जब कोष्ठक की गड़बड़ी में। दूसरी ओर, कभी-कभी आप अंत में टाइपिंग की तरह क्रिया करते हैं [x for x in...। जब तक आप अपने पुनरावृत्तियों को छोटा रखते हैं, तब तक सूची बोध सामान्य रूप से स्पष्ट हो जाता है यदि आप अपने कोड को इंडेंट नहीं करते हैं। लेकिन आप हमेशा अपने कोड को इंडेंट कर सकते हैं।

print(
    {x:x**2 for x in (-y for y in range(5))}
)

या चीजों को तोड़ना:

rangeNeg5 = (-y for y in range(5))
print(
    {x:x**2 for x in rangeNeg5}
)

पायथन 3 के लिए दक्षता तुलना

map अब आलसी है:

% python3 -mtimeit -s 'xs=range(1000)' 'f=lambda x:x' 'z=map(f,xs)'
1000000 loops, best of 3: 0.336 usec per loop            ^^^^^^^^^

इसलिए यदि आप अपने सभी डेटा का उपयोग नहीं कर रहे हैं, या समय से पहले नहीं जानते हैं कि आपको कितने डेटा की आवश्यकता है, तो mappython3 में (और python2 या python3 में जनरेटर के भाव) अंतिम मूल्यों की आवश्यकता होने तक उनके मूल्यों की गणना करने से बचें। आमतौर पर यह आमतौर पर किसी भी उपरि का उपयोग करने से आगे निकल जाएगा map। नकारात्मक पक्ष यह है कि यह सबसे कार्यात्मक भाषाओं के विपरीत अजगर में बहुत सीमित है: आपको यह लाभ केवल तभी मिलता है जब आप अपने डेटा को बाएं से दाएं "क्रम में" तक पहुंचते हैं, क्योंकि अजगर जनरेटर के अभिव्यक्तियों का केवल आदेश का मूल्यांकन किया जा सकता है x[0], x[1], x[2], ...

हालाँकि हम कहते हैं कि हमारे पास एक पूर्व-निर्मित कार्य है fजिसे हम करना चाहते हैं map, और हम mapतुरंत के साथ मूल्यांकन को मजबूर करके आलस्य को अनदेखा करते हैं list(...)। हम कुछ बहुत ही रोचक परिणाम प्राप्त करते हैं:

% python3 -mtimeit -s 'xs=range(1000)' 'f=lambda x:x' 'z=list(map(f,xs))'                                                                                                                                                
10000 loops, best of 3: 165/124/135 usec per loop        ^^^^^^^^^^^^^^^
                    for list(<map object>)

% python3 -mtimeit -s 'xs=range(1000)' 'f=lambda x:x' 'z=[f(x) for x in xs]'                                                                                                                                      
10000 loops, best of 3: 181/118/123 usec per loop        ^^^^^^^^^^^^^^^^^^
                    for list(<generator>), probably optimized

% python3 -mtimeit -s 'xs=range(1000)' 'f=lambda x:x' 'z=list(f(x) for x in xs)'                                                                                                                                    
1000 loops, best of 3: 215/150/150 usec per loop         ^^^^^^^^^^^^^^^^^^^^^^
                    for list(<generator>)

परिणाम एएए / बीबीबी / सीसीसी के रूप में हैं, जहां ए को एक सर्पा-2010 इंटेल वर्कस्टेशन पर पायथन 3 के साथ प्रदर्शन किया गया था। और, और बी और सी को एक ए -2013 एएमडी वर्कस्टेशन के साथ अजगर 3.2.1 के साथ प्रदर्शन किया गया था। बेहद अलग हार्डवेयर के साथ। परिणाम यह प्रतीत होता है कि प्रदर्शन में मानचित्र और सूची की समझ तुलनीय है, जो अन्य यादृच्छिक कारकों से सबसे अधिक प्रभावित होता है। केवल एक चीज जो हम बता सकते हैं, वह यह है कि अजीब तरह से, जबकि हम उम्मीद करते हैं कि सूची [...]अभिव्यक्तियाँ जनरेटर के अभिव्यक्तियों की तुलना में बेहतर प्रदर्शन करती हैं (...), mapयह भी अधिक कुशल है कि जनरेटर अभिव्यक्ति (फिर से यह मानकर कि सभी मूल्यों का मूल्यांकन / उपयोग किया जाता है)।

यह महसूस करना महत्वपूर्ण है कि ये परीक्षण एक बहुत ही सरल कार्य (पहचान समारोह) मानते हैं; हालांकि यह ठीक है क्योंकि यदि फ़ंक्शन जटिल था, तो कार्यक्रम में अन्य कारकों की तुलना में प्रदर्शन ओवरहेड नगण्य होगा। (यह अभी भी अन्य सरल चीजों के साथ परीक्षण करने के लिए दिलचस्प हो सकता है f=lambda x:x+x)

यदि आप अजगर विधानसभा को पढ़ने में कुशल हैं, तो आप disमॉड्यूल का उपयोग करके देख सकते हैं कि क्या वास्तव में पर्दे के पीछे चल रहा है:

>>> listComp = compile('[f(x) for x in xs]', 'listComp', 'eval')
>>> dis.dis(listComp)
  1           0 LOAD_CONST               0 (<code object <listcomp> at 0x2511a48, file "listComp", line 1>) 
              3 MAKE_FUNCTION            0 
              6 LOAD_NAME                0 (xs) 
              9 GET_ITER             
             10 CALL_FUNCTION            1 
             13 RETURN_VALUE         
>>> listComp.co_consts
(<code object <listcomp> at 0x2511a48, file "listComp", line 1>,)
>>> dis.dis(listComp.co_consts[0])
  1           0 BUILD_LIST               0 
              3 LOAD_FAST                0 (.0) 
        >>    6 FOR_ITER                18 (to 27) 
              9 STORE_FAST               1 (x) 
             12 LOAD_GLOBAL              0 (f) 
             15 LOAD_FAST                1 (x) 
             18 CALL_FUNCTION            1 
             21 LIST_APPEND              2 
             24 JUMP_ABSOLUTE            6 
        >>   27 RETURN_VALUE

 

>>> listComp2 = compile('list(f(x) for x in xs)', 'listComp2', 'eval')
>>> dis.dis(listComp2)
  1           0 LOAD_NAME                0 (list) 
              3 LOAD_CONST               0 (<code object <genexpr> at 0x255bc68, file "listComp2", line 1>) 
              6 MAKE_FUNCTION            0 
              9 LOAD_NAME                1 (xs) 
             12 GET_ITER             
             13 CALL_FUNCTION            1 
             16 CALL_FUNCTION            1 
             19 RETURN_VALUE         
>>> listComp2.co_consts
(<code object <genexpr> at 0x255bc68, file "listComp2", line 1>,)
>>> dis.dis(listComp2.co_consts[0])
  1           0 LOAD_FAST                0 (.0) 
        >>    3 FOR_ITER                17 (to 23) 
              6 STORE_FAST               1 (x) 
              9 LOAD_GLOBAL              0 (f) 
             12 LOAD_FAST                1 (x) 
             15 CALL_FUNCTION            1 
             18 YIELD_VALUE          
             19 POP_TOP              
             20 JUMP_ABSOLUTE            3 
        >>   23 LOAD_CONST               0 (None) 
             26 RETURN_VALUE

 

>>> evalledMap = compile('list(map(f,xs))', 'evalledMap', 'eval')
>>> dis.dis(evalledMap)
  1           0 LOAD_NAME                0 (list) 
              3 LOAD_NAME                1 (map) 
              6 LOAD_NAME                2 (f) 
              9 LOAD_NAME                3 (xs) 
             12 CALL_FUNCTION            2 
             15 CALL_FUNCTION            1 
             18 RETURN_VALUE 

ऐसा लगता है कि [...]सिंटैक्स का उपयोग करना बेहतर है list(...)। अफसोस की बात है कि mapक्लास डिसाइड करने के लिए थोड़ा अपारदर्शी है, लेकिन हम अपनी गति परीक्षण के कारण बना सकते हैं।


5
"बहुत उपयोगी इटर्टूलस मॉड्यूल [है] शायद शैली के संदर्भ में अनहैथोनिक माना जाता है"। हम्म। मुझे "पाइथोनिक" शब्द पसंद नहीं है, इसलिए कुछ अर्थों में मुझे परवाह नहीं है कि इसका क्या मतलब है, लेकिन मुझे नहीं लगता कि यह उन लोगों के लिए उचित है जो इसका उपयोग करते हैं, यह कहना कि "पायथोनिकनेस" बिल्डिंग्स के अनुसार mapऔर filterमानक पुस्तकालय के साथ itertoolsस्वाभाविक रूप से खराब शैली है। जब तक जीवीआर वास्तव में यह नहीं कहता कि वे या तो एक भयानक गलती थे या केवल प्रदर्शन के लिए, एकमात्र प्राकृतिक निष्कर्ष यदि "पाइथोनेसिस" कहता है कि इसे बेवकूफी के रूप में भूलना है ;-)
स्टीव जेसोप

4
@SteveJessop: वास्तव में, गुइडो ने सोचा कि गिरना map/ filterपायथन 3 के लिए एक महान विचार था , और केवल अन्य पायथनो द्वारा विद्रोह ने उन्हें अंतर्निहित नेमस्पेस (जबकि reduceस्थानांतरित कर दिया गया था functools) में रखा। मैं व्यक्तिगत रूप से असहमत हूं ( mapऔर filterपूर्वनिर्धारित, विशेष रूप से निर्मित-कार्यों के साथ ठीक हैं, यदि lambdaआवश्यक हो तो कभी भी उनका उपयोग न करें ), लेकिन जीवीआर ने मूल रूप से उन्हें वर्षों तक पाइथोनिक नहीं कहा है।
शैडो रेंजर

@ शादो रेंजर: सच है, लेकिन क्या जीवीआर को हटाने की योजना थी itertools? इस जवाब से मैं जो अंश उद्धृत करता हूं, वह मुख्य दावा है कि मुझे परेशान करता है। मैं नहीं पता है उनके आदर्श दुनिया में चाहे, mapऔर filterकरने के लिए कदम होगा itertools(या functools) या पूरी तरह से जाना है, लेकिन जो भी मामला है, एक बार एक का कहना है कि itertoolsअपनी संपूर्णता में unPythonic है, तो मैं सच में नहीं पता है कि "pythonic" है मतलब है, लेकिन मुझे नहीं लगता कि यह "जीवीआर लोगों को उपयोग करने की सिफारिश करता है" के समान कुछ भी हो सकता है।
स्टीव जेसप

2
@SteveJessop: मैं केवल संबोधित कर रहा था map/ filter, नहीं itertools। कार्यात्मक प्रोग्रामिंग पूरी तरह से pythonic है ( itertools, functoolsऔर operatorसब को ध्यान में कार्यात्मक प्रोग्रामिंग के साथ विशेष रूप से डिजाइन किए गए थे, और मैं अजगर सब समय में कार्यात्मक मुहावरे का उपयोग करें), और itertoolsहै विशेष रूप से सुविधाओं है कि एक दर्द अपने आप को लागू करने के लिए किया जाएगा प्रदान करता है, यह mapऔर filterजनरेटर भाव के साथ किया जा रहा अनावश्यक जिससे गुइडो को उनसे नफरत हो गई। itertoolsहमेशा ठीक रहा है।
शैडो रेंजर

1
अगर कोई रास्ता होता तो मैं इस जवाब को पसंदीदा बना सकता था। अच्छी तरह से समझाया गया।
नेल्सनगॉन

95

अजगर 2: आप का उपयोग करना चाहिए mapऔर filterसूची comprehensions के बजाय।

एक उद्देश्य यह है कि आपको उन्हें "पाइथोनिक" नहीं होने के बावजूद भी क्यों पसंद करना चाहिए:
उन्हें तर्क के रूप में कार्यों / लंबोधों की आवश्यकता होती है, जो एक नए दायरे का परिचय देते हैं

मैंने एक से अधिक बार इससे काट लिया है:

for x, y in somePoints:
    # (several lines of code here)
    squared = [x ** 2 for x in numbers]
    # Oops, x was silently overwritten!

लेकिन अगर इसके बजाय मैंने कहा था:

for x, y in somePoints:
    # (several lines of code here)
    squared = map(lambda x: x ** 2, numbers)

तब सब ठीक हो जाता।

आप कह सकते हैं कि मैं समान दायरे में समान चर नाम का उपयोग करने के लिए मूर्खतापूर्ण था।

मैं नहीं था। मूल रूप से कोड ठीक था - दोनों xएक ही दायरे में नहीं थे।
यह आंतरिक ब्लॉक को कोड के एक अलग खंड में स्थानांतरित करने के बाद ही समस्या आई थी (पढ़ें: रखरखाव के दौरान समस्या, विकास नहीं), और मुझे इसकी उम्मीद नहीं थी।

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

निष्कर्ष:

का उपयोग करें mapऔर filter। वे सूक्ष्म-से-कठिन निदान गुंजाइश-संबंधी बग को रोकते हैं।

पक्षीय लेख:

उपयोग करने पर विचार के लिए मत भूलना imapऔर ifilter(में itertools) अगर वे अपनी स्थिति के लिए उपयुक्त हैं!


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

13
@wim: यह केवल Python 2 के बारे में था, हालाँकि यह Python 3 पर लागू होता है यदि आप पीछे की ओर रहना चाहते हैं। मुझे इसके बारे में पता था और मैं थोड़ी देर के लिए पायथन का उपयोग कर रहा था (हाँ, बस कुछ महीनों से अधिक), और फिर भी यह मेरे साथ हुआ। मैंने दूसरों को देखा है जो मुझसे ज्यादा चालाक हैं उसी जाल में पड़ जाते हैं। यदि आप इतने उज्ज्वल और / या अनुभवी हैं कि यह आपके लिए कोई समस्या नहीं है तो मैं आपके लिए खुश हूं, मुझे नहीं लगता कि अधिकांश लोग आपके जैसे हैं। यदि वे थे, तो पायथन 3 में इसे ठीक करने के लिए इस तरह का आग्रह नहीं किया जाएगा
user541686

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

8
@wim: हुह? अजगर 2 अभी भी कई स्थानों पर उपयोग किया जाता है, यह तथ्य कि पायथन 3 मौजूद है, वह नहीं बदलता है। और जब आप कहते हैं "यह किसी के लिए एक सूक्ष्म बग नहीं है जिसने पायथन का कुछ महीनों से अधिक उपयोग किया है" तो वाक्य का शाब्दिक अर्थ है "यह केवल अनुभवहीन डेवलपर्स की चिंता करता है" (स्पष्ट रूप से आप नहीं)। और रिकॉर्ड के लिए, आपने स्पष्ट रूप से जवाब नहीं पढ़ा क्योंकि मैंने बोल्ड में कहा था कि मैं आगे बढ़ रहा था , कॉपी नहीं कर रहा था । कॉपी-पेस्ट कीड़े भाषाओं में बहुत समान हैं। इस तरह की बग अपनी डांट की वजह से अजगर के लिए अधिक अद्वितीय है; यह सबटॉलर है और आसानी से भूल जाते हैं।
user541686

3
यह अभी भी map/ और / या पर स्विच करने का तार्किक कारण नहीं है filter। यदि कुछ भी हो, तो आपकी समस्या से बचने के लिए सबसे सीधा और तार्किक अनुवाद map(lambda x: x ** 2, numbers)एक जेनरेटर अभिव्यक्ति के बजाय list(x ** 2 for x in numbers)नहीं है, जो लीक नहीं करता है, जैसा कि जेरोमेज ने पहले ही बताया है। मेहरदाद देखो, व्यक्तिगत रूप से इतना पतन मत करो, मैं यहाँ तुम्हारे तर्क से पूरी तरह असहमत हूँ।
विम

46

वास्तव में, mapऔर सूची की समझ पायथन 3 भाषा में काफी अलग तरह से व्यवहार करती है। निम्नलिखित पायथन 3 कार्यक्रम पर एक नज़र डालें:

def square(x):
    return x*x
squares = map(square, [1, 2, 3])
print(list(squares))
print(list(squares))

आप इसे दो बार "[1, 4, 9]" लाइन प्रिंट करने की उम्मीद कर सकते हैं, लेकिन इसके बजाय "[1, 4, 9]" प्रिंट करता है और उसके बाद "[]"। पहली बार जब आप squaresइसे देखते हैं तो यह तीन तत्वों के अनुक्रम के रूप में व्यवहार करता है, लेकिन दूसरी बार एक खाली के रूप में।

अजगर 2 भाषा में mapएक पुरानी पुरानी सूची देता है, जैसे सूची की समझ दोनों भाषाओं में होती है। क्रुक्स यह है कि mapपायथन 3 (और imapपायथन 2 में) का रिटर्न मान एक सूची नहीं है - यह एक पुनरावृत्त है!

जब आप किसी सूची पर पुनरावृति करते हैं, तो इसके विपरीत एक पुनरावृति पर पुनरावृति करने पर तत्वों की खपत होती है। यही कारण है कि squaresअंतिम print(list(squares))पंक्ति में खाली दिखता है ।

संक्षेप में:

  • जब आप पुनरावृत्तियों से निपटते हैं, तो आपको याद रखना होगा कि वे स्टेटफुल हैं और जब आप उन्हें ट्राउट करते हैं, तो वे म्यूट हो जाते हैं।
  • सूचियाँ अधिक अनुमानित हैं क्योंकि वे केवल तब बदलती हैं जब आप उन्हें स्पष्ट रूप से बदलते हैं; वो हैं कंटेनर हैं
  • और एक बोनस: संख्या, तार, और ट्यूपल्स और भी अधिक अनुमानित हैं क्योंकि वे बिल्कुल भी नहीं बदल सकते हैं; वे मूल्य हैं

यह संभवतः सूची समझ के लिए सबसे अच्छा तर्क है। अजगर का नक्शा कार्यात्मक नक्शा नहीं है, लेकिन कार्यात्मक कार्यान्वयन के लाल-सिर वाले स्टेपचाइल्ड है। बहुत दुख की बात है, क्योंकि मैं वास्तव में समझ को नापसंद करता हूं।
अर्ध

@semiomant मैं कहूंगा कि आलसी नक्शा (जैसे python3) उत्सुक मानचित्र (python2 की तरह) की तुलना में अधिक 'कार्यात्मक' है। उदाहरण के लिए, हास्केल में नक्शा आलसी है (ठीक है, हास्केल में सब कुछ आलसी है ...)। वैसे भी, नक्शों का पीछा करने के लिए आलसी नक्शा बेहतर होता है - यदि आपके पास नक़्शे पर लागू होने वाला नक़्शा है, तो आपके पास python2 में मध्यवर्ती मानचित्र कॉल में से प्रत्येक के लिए एक सूची है, जबकि python3 में आपके पास सिर्फ एक परिणामी सूची है, इसलिए इसकी अधिक मेमोरी कुशल है ।
MnZrK

मुझे लगता है कि मैं जो चाहता हूं वह mapएक डेटा संरचना का उत्पादन करने के लिए है, न कि एक पुनरावृत्त। लेकिन शायद आलसी पुनरावृत्तियों आलसी डेटा संरचनाओं की तुलना में आसान है। सोच के लिए भोजन। धन्यवाद @MnZrK
अर्ध-24

आप कहना चाहते हैं कि मानचित्र एक पुनरावृत्ति देता है, एक पुनरावृत्ति नहीं।
user541686

16

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

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


9
हाँ, आह, लेकिन गुइडो को पाइथन 3 में लैम्ब्डा को पूरी तरह से हटाने के मूल इरादे के खिलाफ उसके खिलाफ पैरवी करने का एक बैराज मिला, इसलिए वह मेरे कट्टर समर्थन के बावजूद इस पर वापस चला गया - आह, ठीक है, लैम्ब्डा का अनुमान है कि बहुत सारे SIMPLE मामलों में, केवल एकमात्र समस्या यह है कि जब यह SIMPLE की सीमा से अधिक हो जाता है या एक नाम को सौंपा जाता है (बाद वाले मामले में यह एक मूर्खतापूर्ण शौक की नकल है! -)।
एलेक्स मार्टेली

1
आप जिस साक्षात्कार के बारे में सोच रहे हैं, वह यह है: amk.ca/python/writing/gvr-interview , जहां गुइडो कहते हैं, "कभी-कभी मैं योगदान स्वीकार करने में बहुत तेज रहा हूं, और बाद में एहसास हुआ कि यह एक गलती थी। एक उदाहरण होगा। कुछ कार्यात्मक प्रोग्रामिंग सुविधाएँ, जैसे लैम्बडा फ़ंक्शंस। लैम्ब्डा एक कीवर्ड है जो आपको एक छोटा अनाम फ़ंक्शन बनाने की सुविधा देता है; बिल्ट-इन फ़ंक्शंस जैसे नक्शा, फ़िल्टर, और एक अनुक्रम प्रकार से अधिक फ़ंक्शन को कम करने के लिए, जैसे कि एक सूची। "
जे टेलर

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

> गाइडो। जो सबूतों का एक और टुकड़ा है कि गुइडो उसके दिमाग से बाहर है। बेशक lambdaइतना लंगड़ा कर दिया गया है (कोई बयान नहीं ..) कि वे का उपयोग करना मुश्किल है और वैसे भी सीमित हैं।
जावदबा

16

यहाँ एक संभव मामला है:

map(lambda op1,op2: op1*op2, list1, list2)

बनाम:

[op1*op2 for op1,op2 in zip(list1,list2)]

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


"op1 से op1 * op2, zip में op2 (सूची 1, सूची 2)]" | s / form / for / और out zip के साथ एक समान सूची: (कम पठनीय) [list1 [i] * list2 [i] i के लिए i in रेंज (len (list1))]
कमजोर करें

2
अपने दूसरे कोड उद्धरण, @andz में "से" के लिए "नहीं" होना चाहिए, और @ कमजोर टिप्पणी में भी। मुझे लगा कि मैं समझ को समझने के लिए एक नया वाक्य-विन्यास दृष्टिकोण खोज चुका हूँ ... डारन।
भौतिक विज्ञान

4
एक बहुत देर से टिप्पणी जोड़ने के लिए, आप कर सकते हैं zipका उपयोग करके आलसीitertools.izip
tacaswell

5
मुझे लगता है कि मैं अभी भी पसंद करता हूं map(operator.mul, list1, list2)। यह इन बहुत ही सरल बाईं ओर अभिव्यक्तियों पर है कि समझदारी अनाड़ी हो जाती है।
यन वर्नियर

1
मुझे इस बात का एहसास नहीं था कि नक्शा अपने कार्य के लिए इनपुट के रूप में कई पुनरावृत्तियों को ले सकता है और इस प्रकार एक ज़िप से बच सकता है।
bli

16

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



1
पायथन का मल्टीप्रोसेसिंग मॉड्यूल यह करता है: docs.python.org/2/library/multiprocessing.html
रॉबर्ट एल।

9

इसलिए पायथन 3 के बाद से, map()एक पुनरावृत्त व्यक्ति है, आपको यह ध्यान रखने की आवश्यकता है कि आपको क्या चाहिए: एक पुनरावृत्त या listवस्तु।

जैसा कि @AlexMartelli ने पहले ही उल्लेख किया है , map()यदि आप lambdaफ़ंक्शन का उपयोग नहीं करते हैं तो सूची समझ से अधिक तेज़ है ।

मैं आपको कुछ समय की तुलना प्रस्तुत करूंगा।

पायथन 3.5.2 और सीपीथॉन
मैंने बृहस्पति नोटबुक का उपयोग किया है और विशेष रूप से %timeitअंतर्निहित जादू कमांड
मापक : s == 1000 एमएस == 1000 * 1000 = = = 1000 * 1000 * 1000 एनएस

सेट अप:

x_list = [(i, i+1, i+2, i*2, i-9) for i in range(1000)]
i_list = list(range(1000))

निर्मित समारोह:

%timeit map(sum, x_list)  # creating iterator object
# Output: The slowest run took 9.91 times longer than the fastest. 
# This could mean that an intermediate result is being cached.
# 1000000 loops, best of 3: 277 ns per loop

%timeit list(map(sum, x_list))  # creating list with map
# Output: 1000 loops, best of 3: 214 µs per loop

%timeit [sum(x) for x in x_list]  # creating list with list comprehension
# Output: 1000 loops, best of 3: 290 µs per loop

lambda समारोह:

%timeit map(lambda i: i+1, i_list)
# Output: The slowest run took 8.64 times longer than the fastest. 
# This could mean that an intermediate result is being cached.
# 1000000 loops, best of 3: 325 ns per loop

%timeit list(map(lambda i: i+1, i_list))
# Output: 1000 loops, best of 3: 183 µs per loop

%timeit [i+1 for i in i_list]
# Output: 10000 loops, best of 3: 84.2 µs per loop

जनरेटर अभिव्यक्ति के रूप में ऐसी चीज भी है, PEP-0289 देखें । इसलिए मैंने सोचा कि तुलना करने के लिए इसे जोड़ना उपयोगी होगा

%timeit (sum(i) for i in x_list)
# Output: The slowest run took 6.66 times longer than the fastest. 
# This could mean that an intermediate result is being cached.
# 1000000 loops, best of 3: 495 ns per loop

%timeit list((sum(x) for x in x_list))
# Output: 1000 loops, best of 3: 319 µs per loop

%timeit (i+1 for i in i_list)
# Output: The slowest run took 6.83 times longer than the fastest. 
# This could mean that an intermediate result is being cached.
# 1000000 loops, best of 3: 506 ns per loop

%timeit list((i+1 for i in i_list))
# Output: 10000 loops, best of 3: 125 µs per loop

आपको listवस्तु की आवश्यकता है:

सूची बोध का उपयोग करें यदि यह कस्टम फ़ंक्शन है, list(map())तो अंतर्निहित फ़ंक्शन का उपयोग करें

आपको listऑब्जेक्ट की आवश्यकता नहीं है , आपको बस पुनरावृत्ति की आवश्यकता है:

हमेशा उपयोग करें map()!


1

मैंने एक त्वरित परीक्षण किया, जो किसी वस्तु की विधि को लागू करने के लिए तीन तरीकों की तुलना करता है। इस मामले में, समय का अंतर, नगण्य है और सवाल में फ़ंक्शन का मामला है (देखें @ एलेक्स मार्टेली की प्रतिक्रिया )। यहाँ, मैंने निम्नलिखित विधियों को देखा:

# map_lambda
list(map(lambda x: x.add(), vals))

# map_operator
from operator import methodcaller
list(map(methodcaller("add"), vals))

# map_comprehension
[x.add() for x in vals]

मैंने सूची आकार में वृद्धि के लिए valsदोनों पूर्णांकों (पायथन int) और फ्लोटिंग पॉइंट नंबरों (पायथन float) के सूचियों (संग्रह में संग्रहीत ) को देखा । निम्नलिखित डमी वर्ग DummyNumमाना जाता है:

class DummyNum(object):
    """Dummy class"""
    __slots__ = 'n',

    def __init__(self, n):
        self.n = n

    def add(self):
        self.n += 5

विशेष रूप से, addविधि। __slots__विशेषता पायथन में एक साधारण अनुकूलन, कुल स्मृति वर्ग (विशेषताएँ) द्वारा आवश्यक परिभाषित करने के लिए स्मृति आकार को कम करने है। यहाँ परिणामी भूखंड हैं।

पायथन ऑब्जेक्ट विधियों के मानचित्रण का प्रदर्शन

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

प्लॉट और डेटा उत्पन्न करने के लिए उपयोग किए जाने वाले स्रोत के लिए इस पास्टबिन पर जाएं ।


1
जैसा कि पहले से ही अन्य जवाबों में बताया गया है, mapतभी तेजी से काम किया जाता है जब फ़ंक्शन को ठीक उसी तरह से बुलाया जाता है (यानी [*map(f, vals)]बनाम [f(x) for x in vals])। तो list(map(methodcaller("add"), vals))से तेज है [methodcaller("add")(x) for x in vals]mapलूपिंग समकक्ष एक अलग कॉलिंग विधि का उपयोग करता है जब कुछ ओवरहेड से बच सकते हैं (जैसे या लैम्ब्डा अभिव्यक्ति ओवरहेड x.add()से बचा जाता है methodcaller) का उपयोग तेजी से नहीं हो सकता है । इस विशिष्ट परीक्षण के मामले के लिए, [*map(DummyNum.add, vals)]तेज होगा (क्योंकि DummyNum.add(x)और x.add()मूल रूप से एक ही प्रदर्शन है)।
GZ0

1
वैसे, स्पष्ट list()कॉल्स सूची की समझ से थोड़ी धीमी हैं। निष्पक्ष तुलना के लिए आपको लिखना होगा [*map(...)]
GZ0

@ GZ0 महान प्रतिक्रिया के लिए धन्यवाद! सभी समझ में आता है, और मैं इस बात से अनजान था कि list()कॉल ओवरहेड हो गए। उत्तरों के माध्यम से पढ़ने में अधिक समय देना चाहिए। मैं इन परीक्षणों को एक निष्पक्ष तुलना के लिए फिर से चलाऊंगा, हालांकि मतभेद नगण्य हो सकते हैं।
क्रेयमिकेल

0

मेरा मानना ​​है कि सबसे पायथोनिक तरीका एक सूची समझ का उपयोग करना है mapऔर इसके बजाय filter। कारण यह है कि सूची की समझ mapऔर की तुलना में स्पष्ट हैं filter

In [1]: odd_cubes = [x ** 3 for x in range(10) if x % 2 == 1] # using a list comprehension

In [2]: odd_cubes_alt = list(map(lambda x: x ** 3, filter(lambda x: x % 2 == 1, range(10)))) # using map and filter

In [3]: odd_cubes == odd_cubes_alt
Out[3]: True

जैसा कि आप देखते हैं, एक समझ के लिए अतिरिक्त lambdaअभिव्यक्तियों की आवश्यकता नहीं होती है map। इसके अलावा, एक समझ भी आसानी से फ़िल्टरिंग की अनुमति देती है, जबकि फ़िल्टरिंग की अनुमति देने की mapआवश्यकता होती filterहै।


0

मैंने @ एलेक्स-मार्टेली द्वारा कोड की कोशिश की, लेकिन कुछ विसंगतियां पाई गईं

python -mtimeit -s "xs=range(123456)" "map(hex, xs)"
1000000 loops, best of 5: 218 nsec per loop
python -mtimeit -s "xs=range(123456)" "[hex(x) for x in xs]"
10 loops, best of 5: 19.4 msec per loop

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


3
यह एक बहुत पुराना प्रश्न है, और आप जिस उत्तर का उल्लेख कर रहे हैं, वह पायथन 2 के संदर्भ में बहुत संभावना से लिखा गया था, जहां mapएक सूची मिलती है। पायथन 3 में, mapआलसी का मूल्यांकन किया जाता है, इसलिए केवल कॉलिंग mapकिसी भी नई सूची तत्वों की गणना नहीं करती है, इसलिए आपको बहुत कम समय मिलता है।
काया ३

मुझे लगता है कि आप पायथन 3.x का उपयोग कर रहे हैं जब मैंने यह सवाल पूछा था कि पायथन 3 केवल हाल ही में जारी किया गया था और पायथन 2.x बहुत मानक था।
तीमुथियुसविमान
हमारी साइट का प्रयोग करके, आप स्वीकार करते हैं कि आपने हमारी Cookie Policy और निजता नीति को पढ़ और समझा लिया है।
Licensed under cc by-sa 3.0 with attribution required.