পাইথন: জেনারেটর এক্সপ্রেশন বনাম ফলন


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) ইত্যাদি

এক বা অন্য কোন সুবিধা? ভাবনা?


সবাইকে ধন্যবাদ! এই উত্তরগুলিতে প্রচুর দুর্দান্ত তথ্য এবং আরও রেফারেন্স রয়েছে!


4
আপনি সবচেয়ে পঠনযোগ্য এটি চয়ন করুন।
ব্যবহারকারী 238424

উত্তর:


74

দুজনের মধ্যে সামান্য পার্থক্য রয়েছে। আপনি disনিজের জন্য এই ধরণের জিনিস পরীক্ষা করতে মডিউলটি ব্যবহার করতে পারেন ।

সম্পাদনা করুন: আমার প্রথম সংস্করণটি ইন্টারেক্টিভ প্রম্পটে মডিউল-স্কোপ-এ তৈরি জেনারেটর এক্সপ্রেশনটি সংহত করে। এটি কোনও ফাংশনের অভ্যন্তরে ব্যবহৃত ওপির সংস্করণ থেকে কিছুটা আলাদা। প্রশ্নের আসল কেসের সাথে মেলে আমি এটিকে পরিবর্তন করেছি।

আপনি নীচের দেখতে পারেন, "ফলন" জেনারেটর (প্রথম ক্ষেত্রে) সেটআপ তিনটি অতিরিক্ত নির্দেশাবলী আছে, কিন্তু প্রথম থেকে FOR_ITERতারা শুধুমাত্র এক সম্মান পার্থক্য: "ফলন" পদ্ধতির একটি ব্যবহার LOAD_FASTএকটি স্থানে LOAD_DEREFলুপ ভিতরে। এর তুলনায় LOAD_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ভাল হয় would একটি তাত্ত্বিক বিশ্লেষণ কেবল এতদূর যায়।
পিটার হ্যানসেন

36

এই উদাহরণে, আসলেই নয়। তবে yieldআরও জটিল নির্মাণের জন্য ব্যবহার করা যেতে পারে - উদাহরণস্বরূপ এটি কলারের কাছ থেকে মানগুলিও গ্রহণ করতে পারে এবং ফলস্বরূপ প্রবাহকে সংশোধন করতে পারে। আরও তথ্যের জন্য পিইপি 342 পড়ুন (এটি জানার মতো আকর্ষণীয় কৌশল)।

যাইহোক, সর্বোত্তম পরামর্শ হ'ল আপনার প্রয়োজনের জন্য যা পরিষ্কার হয় তা ব্যবহার করুন

পিএস এখানে ডেভ বিজলির একটি সাধারণ কর্টিন উদাহরণ :

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]

4
+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__করে তাই এই আচরণটি মিস করা সহজ। আসল বিশ্বের উদাহরণ হ'ল জ্যাঙ্গোর QuerySet, যা ডেটা এনে দেয়__iter__ এবং data = (f(x) for x in qs)অনেক সময় নিতে পারে, def g(): for x in qs: yield f(x)এরপরে data=g()তাৎক্ষণিকভাবে ফিরে আসবে।

আরও তথ্য এবং আনুষ্ঠানিক সংজ্ঞা জন্য পিইপি 289 - জেনারেটর এক্সপ্রেশন দেখুন


0

কিছু পার্থক্য রয়েছে যা এখনও চিহ্নিত করা হয়নি যে একটি পার্থক্য আছে। ব্যবহার yieldআপনাকে স্পষ্টভাবে স্টিপ ইন্টেরেশন (এবং করোটিন সম্পর্কিত স্টাফ) উত্থাপনেরreturn চেয়ে অন্য কোনও কিছুর জন্য ব্যবহার থেকে বিরত রাখে

এর অর্থ এই কোডটি দুর্গঠিত (এবং এটি একজন দোভাষীর কাছে খাওয়ানো আপনাকে একটি প্রদান করবে 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)
আমাদের সাইট ব্যবহার করে, আপনি স্বীকার করেছেন যে আপনি আমাদের কুকি নীতি এবং গোপনীয়তা নীতিটি পড়েছেন এবং বুঝতে পেরেছেন ।
Licensed under cc by-sa 3.0 with attribution required.