জেনারেটর কল করে এমন ফাংশন থেকে ফেরত বা ফলন?


30

আমি একটি জেনারেটর আছে generatorএবং এটি একটি সুবিধা পদ্ধতি - generate_all

def generator(some_list):
  for i in some_list:
    yield do_something(i)

def generate_all():
  some_list = get_the_list()
  return generator(some_list) # <-- Is this supposed to be return or yield?

উচিত generate_all returnবা yield? আমি উভয় পদ্ধতির ব্যবহারকারীরা এটি একই ব্যবহার করতে চাই, অর্থাত্‍

for x in generate_all()

সমান হতে হবে

some_list = get_the_list()
for x in generate(some_list)

2
হয় ব্যবহার করার একটি কারণ আছে। এই উদাহরণস্বরূপ, রিটার্ন আরও দক্ষ
ম্যাড পদার্থবিদ

1
এটি আমার একটি অনুরূপ প্রশ্ন আমি একবার যাকে জাহির মনে করিয়ে দেয়: "iterable থেকে ফলন" বনাম "রাউটার (iterable) আসতে" । জেনারেটর সম্পর্কে বিশেষত না হলেও এটি জেনারেটরগুলির মতো একই এবং পুনরাবৃত্ত যন্ত্রগুলির পুনরুত্থানের ক্ষেত্রে পুনরুত্পাদনকারীগুলির মতো একইরকম। এছাড়াও উত্তর দ্বারা প্রস্তাবিত বাইটকোডের সাথে তুলনা করার কৌশলটি এখানে কার্যকর হতে পারে।
পিটারই

উত্তর:


12

জেনারেটরগুলি অলস-মূল্যায়ন করছে তাই returnবা yieldআপনার কোডটি ডিবাগ করার সময় বা কোনও ব্যতিক্রম ছুঁড়ে ফেলা হলে অন্যরকম আচরণ করবে।

আপনার সাথে returnযে কোনও ব্যতিক্রম ঘটে সে generatorসম্পর্কে কিছুই জানতে পারবেন না generate_all, কারণ generatorসত্যিকার অর্থে মৃত্যুদন্ড কার্যকর করা হলে আপনি ইতিমধ্যে generate_allফাংশনটি রেখে গেছেন । সঙ্গে yieldসেখানে এটা করতে হবে generate_allট্রেসব্যাক হবে।

def generator(some_list):
    for i in some_list:
        raise Exception('exception happened :-)')
        yield i

def generate_all():
    some_list = [1,2,3]
    return generator(some_list)

for item in generate_all():
    ...
Exception                                 Traceback (most recent call last)
<ipython-input-3-b19085eab3e1> in <module>
      8     return generator(some_list)
      9 
---> 10 for item in generate_all():
     11     ...

<ipython-input-3-b19085eab3e1> in generator(some_list)
      1 def generator(some_list):
      2     for i in some_list:
----> 3         raise Exception('exception happened :-)')
      4         yield i
      5 

Exception: exception happened :-)

এবং যদি এটি ব্যবহার করে yield from:

def generate_all():
    some_list = [1,2,3]
    yield from generator(some_list)

for item in generate_all():
    ...
Exception                                 Traceback (most recent call last)
<ipython-input-4-be322887df35> in <module>
      8     yield from generator(some_list)
      9 
---> 10 for item in generate_all():
     11     ...

<ipython-input-4-be322887df35> in generate_all()
      6 def generate_all():
      7     some_list = [1,2,3]
----> 8     yield from generator(some_list)
      9 
     10 for item in generate_all():

<ipython-input-4-be322887df35> in generator(some_list)
      1 def generator(some_list):
      2     for i in some_list:
----> 3         raise Exception('exception happened :-)')
      4         yield i
      5 

Exception: exception happened :-)

তবে এটি পারফরম্যান্সের ব্যয়ে আসে। অতিরিক্ত জেনারেটর স্তরের কিছু ওভারহেড থাকে। সুতরাং returnসাধারণত yield from ...(বা for item in ...: yield item) থেকে কিছুটা দ্রুত হবে । বেশিরভাগ ক্ষেত্রে এটি খুব বেশি গুরুত্ব দেয় না, কারণ আপনি জেনারেটরে যা কিছু করেন তা সাধারণত রান-টাইমকে প্রাধান্য দেয় যাতে অতিরিক্ত স্তরটি লক্ষণীয় না হয়।

তবে yieldকিছু অতিরিক্ত সুবিধা রয়েছে: আপনি একক পুনরাবৃত্তির মধ্যে সীমাবদ্ধ নন, আপনি সহজেই অতিরিক্ত আইটেমও উত্পাদন করতে পারবেন:

def generator(some_list):
    for i in some_list:
        yield i

def generate_all():
    some_list = [1,2,3]
    yield 'start'
    yield from generator(some_list)
    yield 'end'

for item in generate_all():
    print(item)
start
1
2
3
end

আপনার ক্ষেত্রে ক্রিয়াকলাপগুলি বেশ সহজ এবং এটির জন্য একাধিক ফাংশন তৈরি করা এমনকি প্রয়োজনীয় কিনা আমি জানি না, mapতার পরিবর্তে একটি বিল্ট-ইন বা জেনারেটর এক্সপ্রেশনটি সহজেই ব্যবহার করতে পারে :

map(do_something, get_the_list())          # map
(do_something(i) for i in get_the_list())  # generator expression

উভয় ব্যবহারের জন্য অভিন্ন হওয়া উচিত (ব্যতিক্রম ঘটলে কিছু পার্থক্য বাদে)। এবং যদি তাদের আরও বর্ণনামূলক নামের প্রয়োজন হয় তবে আপনি তাদের এখনও একটি ফাংশনে মুড়িয়ে রাখতে পারেন।

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

তবে আমি ধরে নিলাম আপনার আসল কোডটি আরও জটিল তাই এটি প্রযোজ্য নাও হতে পারে তবে আমি ভেবেছিলাম বিকল্পগুলির উল্লেখ না করে এটি একটি সম্পূর্ণ উত্তর হবে না।


17

আপনি সম্ভবত জেনারেটরের প্রতিনিধি (PEP380) খুঁজছেন

সাধারণ পুনরাবৃত্তির yield from iterableজন্য মূলত কেবল একটি সংক্ষিপ্ত রূপ formfor item in iterable: yield item

def generator(iterable):
  for i in iterable:
    yield do_something(i)

def generate_all():
  yield from generator(get_the_list())

এটি বেশ সংক্ষিপ্ত এবং এর অন্যান্য অনেক সুবিধা রয়েছে যেমন স্বেচ্ছাসেবী / বিভিন্ন পুনরাবৃত্ত শৃঙ্খলাবদ্ধ করতে সক্ষম!


ওহ আপনি নামকরণ মানে list? এটি একটি খারাপ উদাহরণ, প্রশ্নটিতে আসল কোডটি আটকানো নয়, আমার সম্ভবত এটি সম্পাদনা করা উচিত।
হায়ানকভ

হ্যাঁ - কখনও ভয় করবেন না, আমি উদাহরণের কোডের জন্য বেশ দোষী যা প্রথম জিজ্ঞাসাও চলবে না ..
ti7

2
প্রথম এক ও-লাইনারও হতে পারে :) :) yield from map(do_something, iterable)বা এমনকিyield from (do_something(x) for x in iterable)
ম্যাড পদার্থবিদ

2
"এটি সর্বত্র নীচে কোডের উদাহরণ!"
ti7

3
আপনি কেবল নতুন জেনারেটর ফিরিয়ে দেওয়া ব্যতীত অন্য কিছু করছেন, যদি আপনার হয় তবে আপনার কেবল প্রতিনিধি প্রয়োজন। আপনি যদি কেবল নতুন জেনারেটরটি ফিরিয়ে দেন তবে কোনও প্রতিনিধিদলের প্রয়োজন নেই। তাই yield fromঅর্থহীন যদি না আপনার মোড়কের নেই কিছু আর জেনারেটরের-Y।
ShadowRanger

14

return generator(list)আপনি যা চান তা করে তবে নোট করুন

yield from generator(list)

সমতুল্য হবে, তবে generatorঅবসন্ন হওয়ার পরে আরও মান দেওয়ার সুযোগ রয়েছে । উদাহরণ স্বরূপ:

def generator_all_and_then_some():
    list = get_the_list()
    yield from generator(list)
    yield "one last thing"

5
আমি বিশ্বাস করি যে জেনারেটরের গ্রাহক এর অভ্যন্তরে একটি ব্যতিক্রম - এবং স্ট্যাক ট্রেস দ্বারা প্রভাবিত অন্যান্য ক্রিয়াকলাপগুলির মধ্যে yield fromএবং এর মধ্যে একটি সূক্ষ্ম পার্থক্য রয়েছে । returnthrows
ওয়ার্ল্ডসেন্ডার

9

নিম্নলিখিত দুটি বিবৃতি এই বিশেষ ক্ষেত্রে কার্যকরীভাবে সমতুল্য বলে মনে হবে:

return generator(list)

এবং

yield from generator(list)

পরে প্রায় একই হিসাবে

for i in generator(list):
    yield i

returnবিবৃতি জেনারেটরের আপনি খুঁজছেন ফেরৎ। একটি yield fromবা yieldবিবৃতিটি আপনার পুরো ফাংশনটিকে এমন কোনও জিনিসে রূপান্তরিত করে যা কোনও জেনারেটর ফিরিয়ে দেয়, যা আপনি খুঁজছেন তার মধ্য দিয়ে যায়।

ব্যবহারকারীর দৃষ্টিকোণ থেকে, কোনও পার্থক্য নেই। অভ্যন্তরীণভাবে, তবে returnএটি যুক্তিযুক্তভাবে আরও কার্যকর কারণ এটি generator(list)একটি অতিরিক্ত অতিরিক্ত পাস-থ্রু জেনারেটরে মোড় না দেয়। যদি আপনি মোড়ানো জেনারেটরের উপাদানগুলিতে কোনও প্রক্রিয়াজাতকরণ করার পরিকল্পনা করেন তবে yieldঅবশ্যই কিছু ফর্ম ব্যবহার করুন ।


4

আপনি returnএটা করতে হবে।

yieldআইএন * * generate_all()নিজেই একটি জেনারেটরের কাছে মূল্যায়ন করতে পারে এবং nextসেই বাহ্যিক জেনারেটরে ফোন করলে প্রথম ফাংশনটি দিয়ে অভ্যন্তরীণ জেনারেটর ফিরে আসবে, যা আপনি চান তা নয়।

* অন্তরগত yield from

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