জেনারেটর এক্সপ্রেশন বনাম তালিকা সংক্ষেপণ


411

আপনার কখন জেনারেটর এক্সপ্রেশন ব্যবহার করা উচিত এবং কখন আপনি পাইথনে তালিকা বোঝার ব্যবহার করবেন?

# Generator expression
(x*2 for x in range(256))

# List comprehension
[x*2 for x in range(256)]

27
[exp for x in iter]শুধু চিনি হতে পারে list((exp for x in iter))? নাকি মৃত্যুদণ্ড কার্যকর করার পার্থক্য রয়েছে?
b0fh

1
মনে হয় আমার একটি প্রাসঙ্গিক প্রশ্ন ছিল, তাই ফলন ব্যবহার করার সময় আমরা কোনও ফাংশন থেকে কেবল জেনারেটর এক্সপ্রেশনটি ব্যবহার করতে পারি বা জেনারেটর অবজেক্ট ফিরিয়ে দিতে আমাদের কোনও ফাংশনের জন্য ফলন ব্যবহার করতে হবে?

28
@ b0fh আপনার মন্তব্যে খুব দেরি করে দেওয়া উত্তর: পাইথন 2 এ একটি সামান্য পার্থক্য রয়েছে, লুপ ভেরিয়েবল একটি তালিকা বোধগম্যতার বাইরে বেরিয়ে আসবে, অন্যদিকে জেনারেটর এক্সপ্রেশন ফাঁস হবে না। তুলনা X = [x**2 for x in range(5)]; print xসঙ্গে Y = list(y**2 for y in range(5)); print y, দ্বিতীয় একটি ত্রুটি দেব। পাইথন 3-এ, একটি তালিকা উপলব্ধিটি হ'ল জেনারেটর এক্সপ্রেশনটির জন্য সিনট্যাকটিক চিনি list()যা আপনার প্রত্যাশা অনুযায়ী খাওয়ানো হয়েছে , তাই লুপের ভেরিয়েবল আর ফাঁস হবে না
বাস সুইঙ্ককেলস

12
আমি পিইপি 0289 পড়ার পরামর্শ দিই । দ্বারা সংকলিত আপ "এই PEP প্রবর্তন জেনারেটরের একটি উচ্চ কার্যকারিতা যেমন এক্সপ্রেশন তালিকা comprehensions এবং জেনারেটর স্মৃতির দক্ষ সাধারণীকরণ" । এগুলি কখন ব্যবহার করতে হবে তারও দরকারী উদাহরণ রয়েছে।
icc97

5
@ আইসিসি ৯7 আমি পার্টিতে আট বছর দেরি করেছি, এবং পিইপি লিঙ্কটি নিখুঁত ছিল। এটি সন্ধান করা সহজ করার জন্য ধন্যবাদ!
eenblam

উত্তর:


283

জন এর উত্তর ভাল (আপনি একাধিক বার পুনরাবৃত্তি করতে চান যখন তালিকা বোধগম্য ভাল)। তবে, আপনি যদি তালিকার কোনও পদ্ধতি ব্যবহার করতে চান তবে আপনার একটি তালিকা ব্যবহার করা উচিত এটিও লক্ষণীয়। উদাহরণস্বরূপ, নিম্নলিখিত কোডটি কাজ করবে না:

def gen():
    return (something for something in get_some_stuff())

print gen()[:2]     # generators don't support indexing or slicing
print [5,6] + gen() # generators can't be added to lists

মূলত, জেনারেটর এক্সপ্রেশনটি ব্যবহার করুন যদি আপনি যা করছেন সমস্ত একবার পুনরাবৃত্তি হয়। আপনি যদি উত্পন্ন ফলাফলগুলি সংরক্ষণ এবং ব্যবহার করতে চান তবে আপনি সম্ভবত তালিকা বোধের সাথে আরও ভাল off

যেহেতু পারফরম্যান্স একে অপরের থেকে একটি বেছে নেওয়ার সর্বাধিক সাধারণ কারণ, আমার পরামর্শটি এটি সম্পর্কে উদ্বিগ্ন না হয়ে কেবল একটি বাছাই করা; যদি আপনি দেখতে পান যে আপনার প্রোগ্রামটি খুব ধীরে চলছে, তবে কেবল তখনই আপনার ফিরে আসা উচিত এবং আপনার কোড টিউন করার বিষয়ে চিন্তা করা উচিত।


70
কখনও কখনও আপনি আছে উদাহরণস্বরূপ, আপনার সমবায় সিডিউলিং ফলন ব্যবহার করে coroutines লেখার তাহলে - জেনারেটর ব্যবহার করতে। তবে আপনি যদি তা করে থাকেন তবে আপনি সম্ভবত এই প্রশ্নটি জিজ্ঞাসা করছেন না;)
প্রাক্তন

12
আমি জানি এটি পুরানো, তবে আমি মনে করি যে জেনারেটরগুলি (এবং যে কোনও পুনরাবৃত্তযোগ্য) বর্ধিত তালিকাগুলিতে যুক্ত করা যেতে পারে: a = [1, 2, 3] b = [4, 5, 6] a.extend(b)- এ এখন [1, 2, 3, 4, 5, 6] হবে। (আপনি কি মন্তব্যগুলিতে নতুন লাইন যুক্ত করতে পারবেন ??)
জারভিস্টেভে

12
@ জারভিস্টেভে উদাহরণস্বরূপ আপনি যে শব্দগুলি বলছেন তা বোঝায়। এখানে একটি সূক্ষ্ম বিন্দু আছে। জেনারেটরগুলির সাথে তালিকাগুলি বাড়ানো যেতে পারে তবে তারপরে এটিকে জেনারেটর বানানোর কোনও উদ্দেশ্য ছিল না। জেনারেটর তালিকা সহ প্রসারিত করা যাবে না, এবং জেনারেটর যথেষ্ট পুনরাবৃত্ত হয় না। a = (x for x in range(0,10)), b = [1,2,3]এই ক্ষেত্রে. a.extend(b)একটি ব্যতিক্রম নিক্ষেপ b.extend(a)এগুলির সমস্ত মূল্যায়ন করবে, সেক্ষেত্রে এটিকে প্রথমে জেনারেটর বানানোর কোনও অর্থ নেই।
স্লেটার ভিক্টোরফ

4
@ স্লেটারটাইরানস আপনি 100% সঠিক, এবং আমি আপনাকে যথার্থতার জন্য উত্সাহিত করেছি। তবুও, আমি মনে করি তাঁর মন্তব্যটি ওপি-র প্রশ্নের কার্যকর উত্তর নয় কারণ এটি যারা তাদের এখানে খুঁজে পেতে সহায়তা করবে কারণ তারা একটি অনুসন্ধান ইঞ্জিনে 'জেনারেটরকে লিস্টের সাথে সংযুক্ত করার' মতো টাইপ করেছিলেন।
আরবিপি

1
একাধিকবার পুনরাবৃত্তি করার পরে কোনও জেনারেটর একবারে পুনরাবৃত্তি করার কারণটি (যেমন মেমরির অভাব সম্পর্কে আমার উদ্বেগকে "একবারে একবারে" আনা "মান সম্পর্কে আমার উদ্বেগকে ওভার্রাইড করে ) সম্ভবত প্রয়োগ হয় না? আমি বলবো এটি একটি তালিকা আরও কার্যকর করতে পারে তবে স্মৃতির উদ্বেগকে ছাড়িয়ে যাওয়ার পক্ষে এটি যথেষ্ট কি না অন্য কিছু।
রব গ্রান্ট

181

জেনারেটরের এক্সপ্রেশন বা তালিকার বোঝাপড়ার উপর নজর রাখা একই কাজ করবে। যাইহোক, তালিকা উপলব্ধিটি প্রথমে মেমরিতে পুরো তালিকা তৈরি করবে যখন জেনারেটর এক্সপ্রেশন ফ্লাইতে আইটেমগুলি তৈরি করবে, তাই আপনি এটি খুব বড় (এবং অসীম!) সিকোয়েন্সগুলির জন্য ব্যবহার করতে সক্ষম হন।


39
অসীমের জন্য +1। আপনি পারফরম্যান্স সম্পর্কে কতটা যত্ন নিচ্ছেন তা নির্বিশেষে আপনি কোনও তালিকা দিয়ে এটি করতে পারবেন না।
পল ড্রাগন

আপনি কি বোঝার পদ্ধতিটি ব্যবহার করে অসীম জেনারেটর তৈরি করতে পারেন?
আনানফায়

5
@ অন্নান কেবলমাত্র যদি আপনার কাছে ইতিমধ্যে অন্য অসীম জেনারেটরের অ্যাক্সেস থাকে। উদাহরণস্বরূপ, itertools.count(n)পূর্ণসংখ্যাগুলির একটি অসীম অনুক্রম, এন থেকে শুরু করে, তাই শুরু (2 ** item for item in itertools.count(n))করার ক্ষমতার একটি অসীম অনুক্রম 2হবে 2 ** n
কেভিন

2
একটি জেনারেটর তাদের পুনরাবৃত্তির পরে মেমরি থেকে আইটেমগুলি মুছে দেয়। সুতরাং এটির দ্রুত যদি আপনার কাছে বড় ডেটা থাকে তবে আপনি কেবল এটি প্রদর্শন করতে চান, উদাহরণস্বরূপ। এটি কোনও স্মৃতি হোগ নয়। জেনারেটর আইটেম সঙ্গে 'প্রয়োজন হিসাবে প্রক্রিয়াজাত করা হয়'। আপনি যদি তালিকায় ঝুলতে চান বা আবার এটির পুনরাবৃত্তি করতে চান (সুতরাং আইটেমগুলি সঞ্চয় করুন) তবে তালিকার বোঝাপড়াটি ব্যবহার করুন।
j2emanue

102

যখন ফলাফলটি একাধিকবার পুনরাবৃত্তি করা দরকার বা যেখানে গতিটি সর্বোচ্চ is জেনারেটর এক্সপ্রেশন ব্যবহার করুন যেখানে ব্যাপ্তি বড় বা অসীম।

দেখুন জেনারেটর এক্সপ্রেশন এবং তালিকা comprehensions আরও তথ্যের জন্য।


2
এটি সম্ভবত একটি সামান্য অফ-টপিক হবে, তবে দুর্ভাগ্যক্রমে "আন-গুগলেবল" ... এই প্রসঙ্গে "পরামিতি" এর অর্থ কী? আমি নেটিভ ইংলিশ স্পিকার নই ... :)
গিলারমো এরেস

6
@ গুইলারমোআরেস এটি সর্বজনীন অর্থের জন্য "গুগলিং" এর প্রত্যক্ষ ফলাফল: অন্য যে কোনও কিছুর চেয়ে গুরুত্বপূর্ণ; সুপ্রিম।
Sнаđошƒаӽ

1
সুতরাং listsচেয়ে speedier হয় generatorএক্সপ্রেশন? ডিএফ এর উত্তর পড়া থেকে, এটি জুড়ে এসেছিল যে এটি অন্যদিকে।
হাসান বৈগ

1
এটি সম্ভবত বলা ভাল যে তালিকা বোধগম্যতা যখন দ্রুততর হয় ছোট হয়, তবে স্কেল বাড়ার সাথে সাথে ফ্লাইয়ের মানগুলি গণনা করা আরও মূল্যবান হয়ে ওঠে - কেবলমাত্র তাদের ব্যবহারের জন্য সময় হিসাবে। এটি একটি জেনারেটর এক্সপ্রেশন কি করে।
কাইল

59

গুরুত্বপূর্ণ বিষয়টি হল তালিকান বোধগম্যতা একটি নতুন তালিকা তৈরি করে। জেনারেটর একটি পুনরাবৃত্তিযোগ্য অবজেক্ট তৈরি করে যা বিটগুলি গ্রাস করার সাথে সাথে অন-ফ্লাইতে উত্স উপাদানটি "ফিল্টার" করবে।

আপনার কল্পনা করুন যে আপনার কাছে একটি "2 টিবি" লগ ফাইল রয়েছে যার নাম "विशालফিল. টেক্সট" রয়েছে এবং আপনি "ENTRY" শব্দটি দিয়ে শুরু হওয়া সমস্ত লাইনের জন্য সামগ্রী এবং দৈর্ঘ্য চান।

সুতরাং আপনি একটি তালিকা বোধগম্যতা লিখে শুরু করার চেষ্টা করুন:

logfile = open("hugefile.txt","r")
entry_lines = [(line,len(line)) for line in logfile if line.startswith("ENTRY")]

এটি পুরো ফাইলটিকে স্লাপ করে, প্রতিটি লাইনকে প্রক্রিয়া করে এবং আপনার অ্যারেতে মিলিত লাইনগুলি সঞ্চয় করে। এই অ্যারেতে 2TB অবধি সামগ্রী থাকতে পারে। এটি প্রচুর র‌্যাম, এবং সম্ভবত আপনার উদ্দেশ্যে ব্যবহারিক নয়।

সুতরাং পরিবর্তে আমরা আমাদের সামগ্রীতে একটি "ফিল্টার" প্রয়োগ করতে একটি জেনারেটর ব্যবহার করতে পারি। ফলাফলের উপর পুনরাবৃত্তি শুরু না করা পর্যন্ত কোনও ডেটা আসলেই পড়ে না।

logfile = open("hugefile.txt","r")
entry_lines = ((line,len(line)) for line in logfile if line.startswith("ENTRY"))

এমনকি আমাদের ফাইল থেকে একটি লাইনও পড়েনি। আসলে, বলুন আমরা আমাদের ফলাফলটি আরও আরও ফিল্টার করতে চাই:

long_entries = ((line,length) for (line,length) in entry_lines if length > 80)

এখনও কিছু পড়েনি, তবে আমরা এখন দুটি জেনারেটর নির্দিষ্ট করেছি যা আমাদের ইচ্ছামতো আমাদের ডেটাতে কাজ করবে।

আমাদের ফিল্টার করা লাইনগুলি অন্য কোনও ফাইলে লিখতে দেয়:

outfile = open("filtered.txt","a")
for entry,length in long_entries:
    outfile.write(entry)

এখন আমরা ইনপুট ফাইলটি পড়ি। আমাদের forলুপটি অতিরিক্ত লাইনগুলির অনুরোধ অব্যাহত রাখার সাথে সাথে, long_entriesজেনারেটর জেনারেটরের কাছ থেকে লাইনগুলি দাবি করে entry_lines, কেবল তাদের অক্ষরে ফিরে আসে যাদের দৈর্ঘ্য 80 টি অক্ষরের চেয়ে বেশি। এবং ঘুরে, entry_linesজেনারেটর logfileপুনরুক্তিকারী থেকে লাইনগুলি (ইঙ্গিত হিসাবে ফিল্টারযুক্ত) অনুরোধ করে , যা পরিবর্তে ফাইলটি পড়ে।

সুতরাং সম্পূর্ণ জনবহুল তালিকার আকারে আপনার আউটপুট ফাংশনে ডেটা "পুশিং" না করে আপনি আউটপুট ফাংশনটি কেবল যখন প্রয়োজন তখন ডেটা "টান" দেওয়ার একটি উপায় দিচ্ছেন। এটি আমাদের ক্ষেত্রে অনেক বেশি দক্ষ, তবে যথেষ্ট নমনীয় নয়। জেনারেটর একটি উপায়, একটি পাস; আমরা যে লগ ফাইলটি পড়েছি তা থেকে ডেটা তাত্ক্ষণিকভাবে বাতিল হয়ে যায়, তাই আমরা কোনও আগের লাইনে ফিরে যেতে পারি না। অন্যদিকে, তথ্য শেষ হয়ে গেলে আমাদের চারপাশে রাখার বিষয়ে চিন্তা করতে হবে না।


46

জেনারেটর এক্সপ্রেশনটির সুবিধা হ'ল এটি কম মেমরি ব্যবহার করে যেহেতু এটি একবারে পুরো তালিকা তৈরি করে না। জেনারেটর এক্সপ্রেশনগুলি সর্বোত্তমভাবে ব্যবহৃত হয় যখন তালিকাটি মধ্যস্থতাকারী হয় যেমন ফলাফলের সংক্ষিপ্তসার বা ফলাফলের বাইরে ডিক তৈরি করা।

উদাহরণ স্বরূপ:

sum(x*2 for x in xrange(256))

dict( (k, some_func(k)) for k in some_list_of_keys )

সেখানে সুবিধাটি হ'ল তালিকাটি পুরোপুরি তৈরি হয় নি, এবং এইভাবে অল্প স্মৃতি ব্যবহৃত হয় (এবং এটি আরও দ্রুত হওয়া উচিত)

কাঙ্ক্ষিত চূড়ান্ত পণ্য তালিকা হলে আপনার তালিকার বোধগম্যতা ব্যবহার করা উচিত। আপনি জেনারেটর এক্সপ্রেশন ব্যবহার করে কোনও স্মৃতি সংরক্ষণ করতে যাচ্ছেন না, যেহেতু আপনি উত্পন্ন তালিকাটি চান। বাছাই বা বিপরীতগুলির মতো তালিকার যে কোনও কার্য ব্যবহার করতে সক্ষম হওয়ার সুবিধা আপনি পান।

উদাহরণ স্বরূপ:

reversed( [x*2 for x in xrange(256)] )

9
আপনার পক্ষে ঠিক ভাষায় একটি ইঙ্গিত দেওয়া আছে যে জেনারেটর এক্সপ্রেশনগুলি সেভাবে ব্যবহার করা। বন্ধনী হারান! sum(x*2 for x in xrange(256))
u0b34a0f6ae

8
sortedএবং যে reversedকোনও পুনরাবৃত্ত, জেনারেটর এক্সপ্রেশন অন্তর্ভুক্ত উপর সূক্ষ্ম কাজ।
75

1
আপনি যদি ২.7 বা তার বেশি ব্যবহার করতে পারেন তবে সেই ডিক () উদাহরণটি ডিক বোঝাপড়া হিসাবে আরও ভাল লাগবে (এর জন্য পিইপি তখন জেনারেটর এক্সপ্রেশন পিইপি এর চেয়ে পুরানো, তবে অবতরণে বেশি সময় নিয়েছে)
জারজেন এ। এরার্ড

14

কোনও পরিবর্তনীয় বস্তু থেকে একটি জেনারেটর তৈরি করার সময় (তালিকার মতো) জেনে রাখুন জেনারেটর তৈরির সময় জেনারেটর ব্যবহারের সময় জেনারেটরটি তালিকার স্থিতিতে মূল্যায়ন করা হবে:

>>> mylist = ["a", "b", "c"]
>>> gen = (elem + "1" for elem in mylist)
>>> mylist.clear()
>>> for x in gen: print (x)
# nothing

যদি আপনার তালিকার কোনও সংশোধন হওয়ার সম্ভাবনা থাকে (বা সেই তালিকার অভ্যন্তরে কোনও পরিবর্তনীয় বস্তু) তবে জেনারেটর তৈরির সময় আপনার রাষ্ট্রটি দরকার আপনার পরিবর্তে একটি তালিকা বোঝার প্রয়োজন।


1
এবং এটি গ্রহণযোগ্য উত্তর হওয়া উচিত। যদি আপনার ডেটা উপলব্ধ মেমরির চেয়ে বড় হয় তবে আপনার সর্বদা জেনারেটর ব্যবহার করা উচিত যদিও মেমরির তালিকায় ওভার লুপ করা দ্রুত হতে পারে (তবে এটি করার মতো পর্যাপ্ত মেমরি আপনার নেই)।
মারেক মারকজাক


4

আমি হাদুপ মিনসিমেট মডিউলটি ব্যবহার করছি । আমি মনে করি এটির একটি নোট নিতে এটি একটি দুর্দান্ত উদাহরণ:

import mincemeat

def mapfn(k,v):
    for w in v:
        yield 'sum',w
        #yield 'count',1


def reducefn(k,v): 
    r1=sum(v)
    r2=len(v)
    print r2
    m=r1/r2
    std=0
    for i in range(r2):
       std+=pow(abs(v[i]-m),2)  
    res=pow((std/r2),0.5)
    return r1,r2,res

এখানে জেনারেটর একটি পাঠ্য ফাইলের বাইরে সংখ্যা (15 গিগাবাইটের মতো বড়) পায় এবং হ্যাডোপের মানচিত্র-হ্রাস ব্যবহার করে সেই সংখ্যাগুলিতে সহজ গণিত প্রয়োগ করে। যদি আমি ফলন ফাংশনটি না ব্যবহার করে, তবে পরিবর্তে একটি তালিকা বোঝার জন্য, এটির পরিমাণগুলি এবং গড় গণনা করতে অনেক বেশি সময় নিতে পারত (স্থানের জটিলতার কথা উল্লেখ না করে)।

জেনারেটরগুলির সমস্ত সুবিধা ব্যবহারের জন্য হাদুপ একটি দুর্দান্ত উদাহরণ।

আমাদের সাইট ব্যবহার করে, আপনি স্বীকার করেছেন যে আপনি আমাদের কুকি নীতি এবং গোপনীয়তা নীতিটি পড়েছেন এবং বুঝতে পেরেছেন ।
Licensed under cc by-sa 3.0 with attribution required.