উত্তর:
জেনারেটর আপনাকে অলস মূল্যায়ন দেয়। আপনি এগুলি তাদের উপর পুনরাবৃত্তি করে ব্যবহার করেন, হয় স্পষ্টভাবে 'for' দিয়ে বা স্পষ্ট করে এটি কোনও ফাংশনে প্রেরণ করে বা পুনরাবৃত্তিগুলি নির্মাণ করে। আপনি জেনারেটরগুলিকে একাধিক আইটেম ফিরিয়ে দেওয়া হিসাবে ভাবতে পারেন, যেমন তারা কোনও তালিকা ফিরিয়ে দেয় তবে তারা একবারে একসাথে ফিরে আসার পরিবর্তে এবং পরবর্তী আইটেমটির অনুরোধ না করা পর্যন্ত জেনারেটর ফাংশনটি বিরতি দেওয়া হয়।
জেনারেটরগুলি ফলাফলের বৃহত সেটগুলি গণনা করার জন্য ভাল (বিশেষত গণনাগুলিতে লুপগুলি জড়িত) যেখানে আপনি জানেন না যে আপনার সমস্ত ফলাফলের প্রয়োজন আছে কিনা বা যেখানে আপনি একই সাথে সমস্ত ফলাফলের জন্য মেমরি বরাদ্দ করতে চান না । অথবা এমন পরিস্থিতিতে যেখানে জেনারেটর অন্যটি ব্যবহার করে বা কিছু সংস্থান গ্রহণ করে এবং যতটা সম্ভব দেরি হয়ে গেলে এটি আরও সুবিধাজনক।
জেনারেটরের জন্য আরেকটি ব্যবহার (যা সত্যই একই) হ'ল পুনরাবৃত্তির সাথে কলব্যাকগুলি প্রতিস্থাপন করা। কিছু পরিস্থিতিতে আপনি একটি ফাংশনটি অনেক কাজ করতে চান এবং মাঝে মাঝে কলারে ফিরে রিপোর্ট করুন। Ditionতিহ্যগতভাবে আপনি এর জন্য একটি কলব্যাক ফাংশন ব্যবহার করবেন। আপনি এই কলব্যাকটি ওয়ার্ক-ফাংশনে পাস করেছেন এবং এটি পর্যায়ক্রমে এই কলব্যাকটি কল করবে। জেনারেটরের পন্থাটি হ'ল ওয়ার্ক-ফাংশন (এখন একটি জেনারেটর) কলব্যাক সম্পর্কে কিছুই জানে না এবং যখনই কোনও প্রতিবেদন করতে চায় কেবল ফলন দেয়। কলকারী পৃথক কলব্যাক লেখার পরিবর্তে ওটিকে কার্য-কার্যে প্রেরণের পরিবর্তে, সমস্ত প্রতিবেদনের কাজটি জেনারেটরের চারপাশে কিছুটা 'ফর' করে।
উদাহরণস্বরূপ, বলুন আপনি একটি 'ফাইল সিস্টেম অনুসন্ধান' প্রোগ্রাম লিখেছেন। আপনি অনুসন্ধানটি সম্পূর্ণরূপে সম্পাদন করতে পারবেন, ফলাফল সংগ্রহ করতে পারেন এবং তারপরে একবারে এটি প্রদর্শন করতে পারেন। আপনি প্রথমটি দেখানোর আগে সমস্ত ফলাফল সংগ্রহ করতে হবে এবং ফলাফলগুলি একই সাথে স্মৃতিতে থাকবে। অথবা ফলাফলগুলি খুঁজে পাওয়ার সময় আপনি ফলাফলগুলি প্রদর্শন করতে পারেন যা ব্যবহারকারীর প্রতি আরও মেমরির দক্ষ এবং অনেক বেশি বন্ধুত্বপূর্ণ হবে। পরবর্তীটি ফাইল-সিস্টেম-অনুসন্ধান ফাংশনে ফলাফল-প্রিন্টিং ফাংশনটি পাস করার মাধ্যমে করা যেতে পারে, বা এটি কেবল অনুসন্ধান ফাংশনটিকে একটি জেনারেটর তৈরি করে এবং ফলাফলটি পুনরাবৃত্তি করেই করা যেতে পারে।
যদি আপনি দ্বিতীয় দুটি পদ্ধতির উদাহরণ দেখতে চান তবে, os.path.walk () (কলব্যাক সহ পুরানো ফাইল সিস্টেম-ওয়াকিং ফাংশন) এবং ওএসওয়াক () (নতুন ফাইলসিস্টেম-ওয়াকিং জেনারেটর দেখুন see) অবশ্যই দেখুন, আপনি সত্যিই একটি তালিকার সমস্ত ফলাফল সংগ্রহ করতে চেয়েছিলেন, জেনারেটর পদ্ধতির বড় তালিকা পদ্ধতির রূপান্তর করার জন্য নগণ্য:
big_list = list(the_generator)
yield
এবং তার আগে ম্যানুয়ালি থ্রেডগুলি চালিত করতে সমস্যা না হলে এটি join
সমান্তরালে কার্যকর হয় না (এবং কোনও মানক গ্রন্থাগারের জেনারেটর এটি করে না; গোপনে থ্রেডগুলি প্রবর্তন করা ভঙ্গ করা হয়)। yield
পরবর্তী মানটির অনুরোধ না করা পর্যন্ত জেনারেটর প্রতিটি বিরতি দেয় । যদি জেনারেটর I / O কে আবৃত করে রাখে, খুব শীঘ্রই অনুরোধ করা হবে এই ধারণায় OS ওএসটি সক্রিয়ভাবে ফাইল থেকে ডেটা ক্যাশ করছে, তবে এটি ওএস, পাইথন জড়িত নয়।
জেনারেটর ব্যবহারের অন্যতম কারণ হ'ল সমাধানকে কিছু ধরণের সমাধানের জন্য সমাধানকে আরও পরিষ্কার করা।
অন্যটি হ'ল একবারে ফলাফলগুলি চিকিত্সা করা, ফলাফলগুলির বিশাল তালিকাগুলি তৈরি করা এড়িয়ে যাওয়া এড়ানো যে আপনি যেভাবেই বিচ্ছিন্ন প্রক্রিয়া করবেন।
আপনার যদি এইর মতো একটি ফাইবোনাকি-আপ-টু এন ফাংশন থাকে:
# function version
def fibon(n):
a = b = 1
result = []
for i in xrange(n):
result.append(a)
a, b = b, a + b
return result
আপনি আরও সহজেই এটি হিসাবে ফাংশন লিখতে পারেন:
# generator version
def fibon(n):
a = b = 1
for i in xrange(n):
yield a
a, b = b, a + b
ফাংশন পরিষ্কার হয়। এবং আপনি যদি ফাংশনটি এভাবে ব্যবহার করেন:
for x in fibon(1000000):
print x,
এই উদাহরণে, যদি জেনারেটর সংস্করণ ব্যবহার করা হয় তবে পুরো 1000000 আইটেম তালিকাটি তৈরি করা হবে না, একবারে কেবল একটি মান। তালিকার সংস্করণটি ব্যবহার করার সময় এমনটি হবে না, যেখানে প্রথমে একটি তালিকা তৈরি করা হবে।
list(fibon(5))
পিইপি 255 এর "অনুপ্রেরণা" বিভাগটি দেখুন ।
জেনারেটরগুলির একটি অ-স্পষ্ট ব্যবহার বিঘ্নিত ফাংশন তৈরি করছে, যা আপনাকে ইউআই আপডেট করার মতো কাজ করতে দেয় বা থ্রেড ব্যবহার না করার সময় বেশ কয়েকটি কাজ "একযোগে" চালিয়ে দেয় (আসলে ইন্টারলিভড)
আমি এই ব্যাখ্যাটি পেয়েছি যা আমার সন্দেহকে পরিষ্কার করে। কারণ এমন সম্ভাবনা রয়েছে যে ব্যক্তি জানেন Generators
না সে সম্পর্কেও জানেন নাyield
প্রত্যাবর্তন
রিটার্নের বিবৃতিটি যেখানে সমস্ত স্থানীয় ভেরিয়েবলগুলি ধ্বংস হয়ে যায় এবং ফলস্বরূপ মানটি কলারকে ফিরে (ফেরত) দেওয়া হয়। যদি একই ফাংশনটি কিছুক্ষণ পরে ডাকা হয় তবে ফাংশনটি ভেরিয়েবলগুলির একটি নতুন নতুন সেট পাবে।
উত্পাদ
কিন্তু যখন আমরা কোনও ফাংশন থেকে বেরিয়ে আসি তখন স্থানীয় ভেরিয়েবলগুলি ফেলে দেওয়া হয় না? এর থেকে বোঝা যায় যে আমরা resume the function
যেখানেই রওনা হয়েছি সেখানেই পারি। এখান থেকেই ধারণাটি generators
প্রবর্তন করা হয় এবং বাম yield
যেখানে function
বন্ধ থাকে সেখানে বিবৃতিটি আবার শুরু হয় ।
def generate_integers(N):
for i in xrange(N):
yield i
In [1]: gen = generate_integers(3)
In [2]: gen
<generator object at 0x8117f90>
In [3]: gen.next()
0
In [4]: gen.next()
1
In [5]: gen.next()
সুতরাং পাইথন মধ্যে বিবৃতি return
এবং yield
বিবৃতি মধ্যে পার্থক্য ।
ফলন বিবৃতি একটি ফাংশন একটি জেনারেটর ফাংশন করে তোলে।
সুতরাং জেনারেটর পুনরুক্তি তৈরির জন্য একটি সহজ এবং শক্তিশালী সরঞ্জাম। এগুলি নিয়মিত ফাংশনের মতো লেখা হয় তবে তারা yield
যখনই ডেটা ফেরত চায় তারা বিবৃতিটি ব্যবহার করে । পরের বার () বলা হয়ে থাকে, জেনারেটরটি যেখানেই ছেড়ে দিয়েছিল সেগুলি আবার শুরু করে (এটি সমস্ত ডেটা মানগুলিকে মনে করে এবং কোন বিবৃতি সর্বশেষ কার্যকর করা হয়েছিল)।
ধরা যাক আপনার মাইএসকিউএল টেবিলটিতে আপনার কাছে 100 মিলিয়ন ডোমেন রয়েছে এবং আপনি প্রতিটি ডোমেনের জন্য আলেক্সা র্যাঙ্কটি আপডেট করতে চান।
আপনার প্রথম যেটি দরকার তা হ'ল ডাটাবেস থেকে আপনার ডোমেনের নাম নির্বাচন করা।
ধরা যাক আপনার টেবিলের নাম domains
এবং কলামের নাম domain
।
আপনি যদি SELECT domain FROM domains
এটি ব্যবহার করেন তবে এটি 100 মিলিয়ন সারিগুলি ফিরিয়ে আনবে যা প্রচুর স্মৃতি গ্রহণ করবে। সুতরাং আপনার সার্ভার ক্রাশ হতে পারে।
সুতরাং আপনি ব্যাচগুলিতে প্রোগ্রামটি চালানোর সিদ্ধান্ত নিয়েছেন। ধরা যাক আমাদের ব্যাচের আকার 1000
আমাদের প্রথম ব্যাচে আমরা প্রথম 1000 টি সারি জিজ্ঞাসা করব, প্রতিটি ডোমেনের জন্য আলেক্সা র্যাঙ্কটি পরীক্ষা করব এবং ডাটাবেস সারিটি আপডেট করব।
আমাদের দ্বিতীয় ব্যাচে আমরা পরবর্তী 1000 সারিগুলিতে কাজ করব। আমাদের তৃতীয় ব্যাচে এটি 2001 থেকে 3000 ইত্যাদি হবে।
এখন আমাদের একটি জেনারেটর ফাংশন প্রয়োজন যা আমাদের ব্যাচগুলি উত্পন্ন করে।
আমাদের জেনারেটরের ফাংশনটি এখানে:
def ResultGenerator(cursor, batchsize=1000):
while True:
results = cursor.fetchmany(batchsize)
if not results:
break
for result in results:
yield result
যেমন আপনি দেখতে পাচ্ছেন, আমাদের ফাংশন yield
ফলাফলগুলিকে আইএনও রাখে । আপনি যদি এর return
পরিবর্তে কীওয়ার্ডটি ব্যবহার করেন yield
, তারপরে ফিরে আসার পরে পুরো ফাংশনটি শেষ হয়ে যায়।
return - returns only once
yield - returns multiple times
যদি কোনও ফাংশন কীওয়ার্ডটি ব্যবহার করে yield
তবে এটি জেনারেটর।
এখন আপনি এই মত পুনরাবৃত্তি করতে পারেন:
db = MySQLdb.connect(host="localhost", user="root", passwd="root", db="domains")
cursor = db.cursor()
cursor.execute("SELECT domain FROM domains")
for result in ResultGenerator(cursor):
doSomethingWith(result)
db.close()
বাফারিং। যখন বড় অংশগুলিতে ডেটা আনার দক্ষ হয় তবে এটি ছোট অংশগুলিতে প্রক্রিয়া করে, তখন কোনও জেনারেটর সহায়তা করতে পারে:
def bufferedFetch():
while True:
buffer = getBigChunkOfData()
# insert some code to break on 'end of data'
for i in buffer:
yield i
উপরেরগুলি আপনাকে প্রক্রিয়াকরণ থেকে সহজেই বাফারিং পৃথক করতে দেয়। গ্রাহক ফাংশন এখন কেবল বাফারিংয়ের চিন্তা না করে একের পর এক মান পেতে পারে।
আমি খুঁজে পেয়েছি যে জেনারেটরগুলি আপনার কোড সাফ করার জন্য এবং কোডকে এমপ্ল্যাপুলেট এবং মডুলারাইজ করার একটি খুব অনন্য উপায় দিয়ে আপনি খুব সহায়ক। একটি অবস্থা যেখানে আপনি ক্রমাগত নিজস্ব অভ্যন্তরীণ প্রক্রিয়াকরণ এবং ভিত্তিক মান আউট থুতু কিছু প্রয়োজন যখন কিছু চাহিদা (শুধুমাত্র একটি লুপ বা উদাহরণস্বরূপ একটি ব্লক মধ্যে) আপনার কোড যে কোন জায়গা থেকে বলা হবে, জেনারেটর আছে করার বৈশিষ্ট্য ব্যবহার করুন।
একটি বিমূর্ত উদাহরণ একটি ফাইবোনাচি নম্বর জেনারেটর হবে যা কোনও লুপের মধ্যে বাস করে না এবং যখনই কোথাও থেকে ফোন করা হয় তখনই পরবর্তী নম্বরটি ক্রমাগত ফিরে আসবে:
def fib():
first = 0
second = 1
yield first
yield second
while 1:
next = first + second
yield next
first = second
second = next
fibgen1 = fib()
fibgen2 = fib()
এখন আপনার কাছে দুটি ফিবোনাচি নম্বর জেনারেটর অবজেক্ট রয়েছে যা আপনি আপনার কোডের যে কোনও জায়গা থেকে কল করতে পারেন এবং তারা সর্বদা বৃহত্তর ফিবোনাচি নম্বরগুলিতে যথাক্রমে ফিরে আসবে:
>>> fibgen1.next(); fibgen1.next(); fibgen1.next(); fibgen1.next()
0
1
1
2
>>> fibgen2.next(); fibgen2.next()
0
1
>>> fibgen1.next(); fibgen1.next()
3
5
জেনারেটরগুলির সম্পর্কে সুন্দর জিনিস হ'ল তারা অবজেক্ট তৈরির হুপের মধ্যে না গিয়ে রাষ্ট্রকে ঘিরে ফেলে। তাদের সম্পর্কে চিন্তা করার একটি উপায় "ফাংশন" যা তাদের অভ্যন্তরীণ অবস্থার কথা মনে করে।
পাইথন জেনারেটরের কাছ থেকে আমি ফিবোনাচি উদাহরণ পেয়েছি - সেগুলি কী? এবং একটু কল্পনা করে আপনি আরও অনেকগুলি পরিস্থিতি নিয়ে আসতে পারেন যেখানে জেনারেটরগুলি for
লুপগুলি এবং অন্যান্য traditionalতিহ্যবাহী পুনরাবৃত্তি নির্মাণের দুর্দান্ত বিকল্প তৈরি করে ।
সহজ ব্যাখ্যা: একটি for
বিবৃতি বিবেচনা করুন
for item in iterable:
do_stuff()
অনেক সময়, সমস্ত আইটেমের iterable
শুরু থেকেই সেখানে থাকা প্রয়োজন হয় না, তবে ফ্লাইতে সেগুলি তৈরি করা যায় যা প্রয়োজন হয়। এটি উভয় ক্ষেত্রে অনেক বেশি দক্ষ হতে পারে
অন্যান্য সময়, আপনি সমস্ত আইটেম সময় আগে জানেন না। উদাহরণ স্বরূপ:
for command in user_input():
do_stuff_with(command)
আপনার ব্যবহারকারীর সমস্ত আদেশ আগেই জানার উপায় নেই তবে আপনি যদি জেনারেটর হস্তান্তর করে থাকেন তবে আপনি এই জাতীয় লুপটি ব্যবহার করতে পারেন:
def user_input():
while True:
wait_for_command()
cmd = get_command()
yield cmd
জেনারেটরের সাহায্যে আপনি অসীম সিকোয়েন্সগুলির মাধ্যমে পুনরাবৃত্তিও করতে পারেন, যা পাত্রে পুনরাবৃত্তি করার সময় অবশ্যই সম্ভব নয়।
আমার প্রিয় ব্যবহারগুলি হ'ল "ফিল্টার" এবং "কমান" অপারেশন।
ধরা যাক আমরা একটি ফাইল পড়ছি, এবং কেবল "##" দিয়ে শুরু হওয়া লাইনগুলি চাই।
def filter2sharps( aSequence ):
for l in aSequence:
if l.startswith("##"):
yield l
তারপরে আমরা জেনারেটর ফাংশনটি একটি সঠিক লুপে ব্যবহার করতে পারি
source= file( ... )
for line in filter2sharps( source.readlines() ):
print line
source.close()
হ্রাস উদাহরণ একই রকম। ধরা যাক আমাদের একটি ফাইল আছে যেখানে আমাদের <Location>...</Location>
লাইনগুলির ব্লকগুলি সনাক্ত করতে হবে । [এইচটিএমএল ট্যাগ নয়, লাইনগুলি ট্যাগ-জাতীয় দেখার মতো হয়]]
def reduceLocation( aSequence ):
keep= False
block= None
for line in aSequence:
if line.startswith("</Location"):
block.append( line )
yield block
block= None
keep= False
elif line.startsWith("<Location"):
block= [ line ]
keep= True
elif keep:
block.append( line )
else:
pass
if block is not None:
yield block # A partial block, icky
আবার আমরা এই জেনারেটরটিকে লুপের জন্য যথাযথভাবে ব্যবহার করতে পারি।
source = file( ... )
for b in reduceLocation( source.readlines() ):
print b
source.close()
ধারণাটি হ'ল জেনারেটর ফাংশনটি আমাদেরকে একটি সিক্যুয়েন্স ফিল্টার বা হ্রাস করতে দেয়, একই সাথে অন্য সিকোয়েন্সগুলির একটি সময়ে একটি মান দেয়।
fileobj.readlines()
জেনারেটর ব্যবহারের উদ্দেশ্যকে পরাস্ত করে মেমরির তালিকায় পুরো ফাইলটি পড়বে। যেহেতু ফাইল অবজেক্টগুলি ইতিমধ্যে পুনরাবৃত্তিযোগ্য আপনি তার for b in your_generator(fileobject):
পরিবর্তে ব্যবহার করতে পারেন । পুরো ফাইলটি পড়া এড়ানোর জন্য আপনার ফাইলটি একবারে এক লাইনে পড়তে হবে।
আপনি যদি কোনও জেনারেটর ব্যবহার করতে পারেন তার একটি ব্যবহারিক উদাহরণ হ'ল যদি আপনার কোনও ধরণের আকৃতি থাকে এবং আপনি এর কোণগুলি, প্রান্তগুলি বা যাই হোক না কেন পুনরাবৃত্তি করতে চান। আমার নিজের প্রকল্পের জন্য (উত্স কোড এখানে ) আমার একটি আয়তক্ষেত্র ছিল:
class Rect():
def __init__(self, x, y, width, height):
self.l_top = (x, y)
self.r_top = (x+width, y)
self.r_bot = (x+width, y+height)
self.l_bot = (x, y+height)
def __iter__(self):
yield self.l_top
yield self.r_top
yield self.r_bot
yield self.l_bot
এখন আমি একটি কোণে আয়তক্ষেত্র তৈরি করতে এবং লুপ করতে পারি:
myrect=Rect(50, 50, 100, 100)
for corner in myrect:
print(corner)
আপনার পরিবর্তে __iter__
একটি পদ্ধতি থাকতে পারে iter_corners
এবং এটি দিয়ে কল করতে পারে for corner in myrect.iter_corners()
। এটি __iter__
তখন থেকেই ব্যবহার করা আরও মার্জিত এবং আমরা ক্লাস উদাহরণের নামটি সরাসরি for
অভিব্যক্তিতে ব্যবহার করতে পারি ।
কিছু ভাল উত্তর এখানে, তবে আমি পাইথন ফাংশনাল প্রোগ্রামিং টিউটোরিয়ালটি সম্পূর্ণ পড়ার পরামর্শ দিচ্ছি যা জেনারেটরের আরও শক্তিশালী ব্যবহারের ক্ষেত্রে কিছু ব্যাখ্যা করতে সহায়তা করে।
যেহেতু জেনারেটরের প্রেরণের পদ্ধতি উল্লেখ করা হয়নি, তাই এখানে একটি উদাহরণ দেওয়া হল:
def test():
for i in xrange(5):
val = yield
print(val)
t = test()
# Proceed to 'yield' statement
next(t)
# Send value to yield
t.send(1)
t.send('2')
t.send([3])
এটি চলমান জেনারেটরে একটি মান প্রেরণের সম্ভাবনাটি দেখায়। নীচের ভিডিওতে জেনারেটর সম্পর্কিত আরও উন্নত কোর্স (সহ)yield
সমান্তরাল প্রক্রিয়াকরণের জন্য এক্সপ্লিটেশন, জেনারেটর , পুনরাবৃত্তির সীমা থেকে দূরে থাকা ইত্যাদি)
স্টাইলের গাদা। আপনি যে কোনও সময় আইটেমগুলির ক্রম তৈরি করতে চান, তবে সেগুলি একবারে একটি তালিকাতে 'রূপায়ন' করতে চান না। উদাহরণস্বরূপ, আপনার কাছে এমন একটি সাধারণ জেনারেটর থাকতে পারে যা প্রাথমিক সংখ্যাগুলি দেয়:
def primes():
primes_found = set()
primes_found.add(2)
yield 2
for i in itertools.count(1):
candidate = i * 2 + 1
if not all(candidate % prime for prime in primes_found):
primes_found.add(candidate)
yield candidate
তারপরে আপনি পরবর্তী প্রাইমগুলির পণ্যগুলি তৈরি করতে এটি ব্যবহার করতে পারেন:
def prime_products():
primeiter = primes()
prev = primeiter.next()
for prime in primeiter:
yield prime * prev
prev = prime
এগুলি মোটামুটি তুচ্ছ উদাহরণ, তবে আপনি দেখতে পাচ্ছেন যে এটি বৃহত্তর (সম্ভাব্য অসীম!) ডেটাসেটগুলি আগাম উত্পাদন না করে প্রক্রিয়াজাতকরণের জন্য কীভাবে কার্যকর হতে পারে, যা কেবলমাত্র আরও স্পষ্টতর ব্যবহার।
প্রাথমিক সংখ্যাগুলি এন পর্যন্ত মুদ্রণের জন্যও ভাল:
def genprime(n=10):
for num in range(3, n+1):
for factor in range(2, num):
if num%factor == 0:
break
else:
yield(num)
for prime_num in genprime(100):
print(prime_num)