अजगर: जनरेटर अभिव्यक्ति बनाम उपज


90

अजगर में, वहाँ एक के माध्यम से एक जनरेटर वस्तु बनाने के बीच कोई अंतर है जनरेटर अभिव्यक्ति का उपयोग कर बनाम उपज बयान?

उपज का उपयोग :

def Generator(x, y):
    for i in xrange(x):
        for j in xrange(y):
            yield(i, j)

जनरेटर अभिव्यक्ति का उपयोग करना :

def Generator(x, y):
    return ((i, j) for i in xrange(x) for j in xrange(y))

दोनों फ़ंक्शन जनरेटर ऑब्जेक्ट्स को लौटाते हैं, जो टुपल्स का उत्पादन करते हैं, जैसे (0,0), (0,1) आदि।

एक या दूसरे के किसी भी लाभ? विचार?


सभी का धन्यवाद! इन उत्तरों में बहुत सारी बेहतरीन जानकारी और आगे के संदर्भ हैं!


2
आपको जो सबसे अधिक पठनीय लगता है, उसे चुनें।
user238424

जवाबों:


74

दोनों में केवल मामूली अंतर हैं। आप अपने लिए disइस प्रकार की चीज़ों की जांच करने के लिए मॉड्यूल का उपयोग कर सकते हैं ।

संपादित करें: मेरा पहला संस्करण इंटरैक्टिव प्रॉम्प्ट में मॉड्यूल-गुंजाइश पर बनाई गई जनरेटर अभिव्यक्ति को विघटित करता है। यह एक फ़ंक्शन के अंदर उपयोग किए गए ओपी के संस्करण से थोड़ा अलग है। मैंने प्रश्न में वास्तविक मामले से मेल खाने के लिए इसे संशोधित किया है।

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

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

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

>>> def Generator(x, y):
...     for i in xrange(x):
...         for j in xrange(y):
...             yield(i, j)
...
>>> dis.dis(Generator)
  2           0 SETUP_LOOP              54 (to 57)
              3 LOAD_GLOBAL              0 (xrange)
              6 LOAD_FAST                0 (x)
              9 CALL_FUNCTION            1
             12 GET_ITER
        >>   13 FOR_ITER                40 (to 56)
             16 STORE_FAST               2 (i)

  3          19 SETUP_LOOP              31 (to 53)
             22 LOAD_GLOBAL              0 (xrange)
             25 LOAD_FAST                1 (y)
             28 CALL_FUNCTION            1
             31 GET_ITER
        >>   32 FOR_ITER                17 (to 52)
             35 STORE_FAST               3 (j)

  4          38 LOAD_FAST                2 (i)
             41 LOAD_FAST                3 (j)
             44 BUILD_TUPLE              2
             47 YIELD_VALUE
             48 POP_TOP
             49 JUMP_ABSOLUTE           32
        >>   52 POP_BLOCK
        >>   53 JUMP_ABSOLUTE           13
        >>   56 POP_BLOCK
        >>   57 LOAD_CONST               0 (None)
             60 RETURN_VALUE
>>> def Generator_expr(x, y):
...    return ((i, j) for i in xrange(x) for j in xrange(y))
...
>>> dis.dis(Generator_expr.func_code.co_consts[1])
  2           0 SETUP_LOOP              47 (to 50)
              3 LOAD_FAST                0 (.0)
        >>    6 FOR_ITER                40 (to 49)
              9 STORE_FAST               1 (i)
             12 SETUP_LOOP              31 (to 46)
             15 LOAD_GLOBAL              0 (xrange)
             18 LOAD_DEREF               0 (y)
             21 CALL_FUNCTION            1
             24 GET_ITER
        >>   25 FOR_ITER                17 (to 45)
             28 STORE_FAST               2 (j)
             31 LOAD_FAST                1 (i)
             34 LOAD_FAST                2 (j)
             37 BUILD_TUPLE              2
             40 YIELD_VALUE
             41 POP_TOP
             42 JUMP_ABSOLUTE           25
        >>   45 POP_BLOCK
        >>   46 JUMP_ABSOLUTE            6
        >>   49 POP_BLOCK
        >>   50 LOAD_CONST               0 (None)
             53 RETURN_VALUE

स्वीकृत - डिस का उपयोग करके अंतर की विस्तृत व्याख्या के लिए। धन्यवाद!
cschol

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

36

इस उदाहरण में, वास्तव में नहीं। लेकिन yieldअधिक जटिल निर्माणों के लिए उपयोग किया जा सकता है - उदाहरण के लिए यह कॉलर से मूल्यों को भी स्वीकार कर सकता है और परिणामस्वरूप प्रवाह को संशोधित कर सकता है। अधिक विवरण के लिए पीईपी 342 पढ़ें (यह जानने लायक एक दिलचस्प तकनीक है)।

वैसे भी, सबसे अच्छी सलाह का उपयोग करें जो आपकी आवश्यकताओं के लिए स्पष्ट है

PS यहाँ डेव बेज़ले से एक सरल कोरआउट का उदाहरण दिया गया है :

def grep(pattern):
    print "Looking for %s" % pattern
    while True:
        line = (yield)
        if pattern in line:
            print line,

# Example use
if __name__ == '__main__':
    g = grep("python")
    g.next()
    g.send("Yeah, but no, but yeah, but no")
    g.send("A series of tubes")
    g.send("python generators rock!")

8
डेविड बेज़ले को जोड़ने के लिए +1। कोरटाइन्स पर उनकी प्रस्तुति सबसे ज्यादा दिमाग लगाने वाली चीज है जिसे मैंने लंबे समय में पढ़ा है। जनरेटरों पर उनकी प्रस्तुति के रूप में उपयोगी नहीं, शायद, लेकिन फिर भी अद्भुत।
रॉबर्ट रोसनी

18

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

>>> def fibgen():
...    a = b = 1
...    while True:
...        yield a
...        a, b = b, a+b

>>> list(itertools.takewhile((lambda x: x<100), fibgen()))
[1, 1, 2, 3, 5, 8, 13, 21, 34, 55, 89]

5
+1 जो सुपर कूल है ... यह नहीं कह सकता कि मैंने कभी भी बिना किसी पुनरावृत्ति के ऐसे छोटे और मीठे फ़ाइबर कार्यान्वयन को देखा है।
जूडोविल

भ्रामक सरल कोड स्निपेट - मुझे लगता है कि फिबोनाची इसे देखकर खुश होगा !!
यूजर-एस्टेरिक्स

10

उपयोग में, एक जनरेटर वस्तु बनाम एक जनरेटर फ़ंक्शन के बीच अंतर पर ध्यान दें।

एक जनरेटर ऑब्जेक्ट का उपयोग-एक बार ही किया जाता है, एक जनरेटर फ़ंक्शन के विपरीत, जिसे हर बार जब आप इसे फिर से कॉल करते हैं, तो इसका पुन: उपयोग किया जा सकता है, क्योंकि यह एक ताज़ा जनरेटर ऑब्जेक्ट देता है।

जेनरेटर एक्सप्रेशन आमतौर पर "रॉ" का उपयोग करते हैं, उन्हें किसी फ़ंक्शन में लपेटे बिना, और वे जेनरेटर ऑब्जेक्ट वापस करते हैं।

उदाहरण के लिए:

def range_10_gen_func():
    x = 0
    while x < 10:
        yield x
        x = x + 1

print(list(range_10_gen_func()))
print(list(range_10_gen_func()))
print(list(range_10_gen_func()))

कौन से आउटपुट:

[0, 1, 2, 3, 4, 5, 6, 7, 8, 9]
[0, 1, 2, 3, 4, 5, 6, 7, 8, 9]
[0, 1, 2, 3, 4, 5, 6, 7, 8, 9]

थोड़े अलग उपयोग के साथ तुलना करें:

range_10_gen = range_10_gen_func()
print(list(range_10_gen))
print(list(range_10_gen))
print(list(range_10_gen))

कौन से आउटपुट:

[0, 1, 2, 3, 4, 5, 6, 7, 8, 9]
[]
[]

और एक जनरेटर अभिव्यक्ति के साथ तुलना करें:

range_10_gen_expr = (x for x in range(10))
print(list(range_10_gen_expr))
print(list(range_10_gen_expr))
print(list(range_10_gen_expr))

जो भी आउटपुट:

[0, 1, 2, 3, 4, 5, 6, 7, 8, 9]
[]
[]

8

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

def Generator(x):
  for i in xrange(x):
    yield(i)
  yield(None)

5

जब पुनरावृत्तियों के बारे में सोचते हैं, तो itertoolsमॉड्यूल:

... तेज, मेमोरी कुशल उपकरणों का एक मुख्य सेट मानकीकृत करता है जो स्वयं या संयोजन में उपयोगी होते हैं। साथ में, वे एक "पुनरावृति बीजगणित" बनाते हैं, जो विशेष रूप से शुद्ध पायथन में कुशलतापूर्वक और कुशलता से निर्माण करना संभव बनाता है।

प्रदर्शन के लिए, विचार करें itertools.product(*iterables[, repeat])

इनपुट पुनरावृत्तियों के कार्टेशियन उत्पाद।

जनरेटर अभिव्यक्ति में नेस्टेड-लूप के बराबर। उदाहरण के लिए, product(A, B)जैसा है वैसा ही लौटाता है ((x,y) for x in A for y in B)

>>> import itertools
>>> def gen(x,y):
...     return itertools.product(xrange(x),xrange(y))
... 
>>> [t for t in gen(3,2)]
[(0, 0), (0, 1), (1, 0), (1, 1), (2, 0), (2, 1)]
>>> 

4

हां, वहां एक अंतर है।

जनरेटर अभिव्यक्ति के लिए (x for var in expr), iter(expr)जब अभिव्यक्ति का निर्माण किया जाता है तो इसे कहा जाता है ।

उपयोग करते समय defऔर yieldएक जनरेटर बनाने के लिए, जैसे कि:

def my_generator():
    for var in expr:
        yield x

g = my_generator()

iter(expr)अभी तक नहीं बुलाया गया है। इसे केवल तभी कहा जाएगा जब इसे पुनरावृत्त किया जाएगा g(और इसे बिल्कुल भी नहीं कहा जा सकता है)।

एक उदाहरण के रूप में इस पुनरावृत्ति लेना:

from __future__ import print_function


class CountDown(object):
    def __init__(self, n):
        self.n = n

    def __iter__(self):
        print("ITER")
        return self

    def __next__(self):
        if self.n == 0:
            raise StopIteration()
        self.n -= 1
        return self.n

    next = __next__  # for python2

यह कोड:

g1 = (i ** 2 for i in CountDown(3))  # immediately prints "ITER"
print("Go!")
for x in g1:
    print(x)

जबकि:

def my_generator():
    for i in CountDown(3):
        yield i ** 2


g2 = my_generator()
print("Go!")
for x in g2:  # "ITER" is only printed here
    print(x)

चूंकि अधिकांश पुनरावृत्तियों में बहुत अधिक सामान नहीं होता है __iter__, इसलिए इस व्यवहार को याद करना आसान है। एक वास्तविक दुनिया उदाहरण होगा Django के QuerySet, जिसमें डेटा प्राप्त होता है__iter__ और इसके बाद data = (f(x) for x in qs)बहुत समय लग सकता def g(): for x in qs: yield f(x)हैdata=g() तुरंत वापस आ जाएगा।

अधिक जानकारी और औपचारिक परिभाषा के लिए पीईपी 289 - जेनरेटर एक्सप्रेशंस देखें


0

एक अंतर है जो कुछ संदर्भों में महत्वपूर्ण हो सकता है जिन्हें अभी तक इंगित नहीं किया गया है। का उपयोग करना yieldआपको रोकने के returnअलावा कुछ और के लिए उपयोग करने से रोकता है StopIteration (और coroutines संबंधित सामान)

इसका मतलब है कि यह कोड बीमार है (और एक दुभाषिया को खिलाने से आपको मिल जाएगा AttributeError):

class Tea:

    """With a cloud of milk, please"""

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

def mary_poppins_purse(tea_time=False):
    """I would like to make one thing clear: I never explain anything."""
    if tea_time:
        return Tea(355)
    else:
        for item in ['lamp', 'mirror', 'coat rack', 'tape measure', 'ficus']:
            yield item

print(mary_poppins_purse(True).temperature)

दूसरी ओर, यह कोड एक आकर्षण की तरह काम करता है:

class Tea:

    """With a cloud of milk, please"""

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

def mary_poppins_purse(tea_time=False):
    """I would like to make one thing clear: I never explain anything."""
    if tea_time:
        return Tea(355)
    else:
        return (item for item in ['lamp', 'mirror', 'coat rack',
                                  'tape measure', 'ficus'])

print(mary_poppins_purse(True).temperature)
हमारी साइट का प्रयोग करके, आप स्वीकार करते हैं कि आपने हमारी Cookie Policy और निजता नीति को पढ़ और समझा लिया है।
Licensed under cc by-sa 3.0 with attribution required.