از طریق منوی جستجو مطلب مورد نظر خود در وبلاگ را به سرعت پیدا کنید
مولدهای پایتون
سرفصلهای مطلب
ژنراتور چیست؟
یک پایتون ژنراتور تابعی است که دنباله ای از نتایج را تولید می کند. با حفظ حالت محلی خود کار می کند، به طوری که عملکرد می تواند دوباره دقیقاً از همان جایی که در دفعات بعدی فراخوانی می شود، از سر بگیرد. بنابراین، شما می توانید یک مولد را چیزی شبیه به یک تکرار کننده قدرتمند در نظر بگیرید.
وضعیت تابع با استفاده از کلمه کلیدی حفظ می شود yield
، که دارای نحو زیر است:
yield (expression_list)
این کلمه کلیدی پایتون بسیار شبیه به استفاده است return
، اما تفاوت های مهمی دارد که در طول این مقاله توضیح خواهیم داد.
ژنراتورها معرفی شدند PEP 255، همراه با yield
بیانیه. آنها از نسخه 2.2 پایتون در دسترس بوده اند.
مولدهای پایتون چگونه کار می کنند؟
برای اینکه بفهمیم ژنراتورها چگونه کار می کنند، از مثال ساده زیر استفاده می کنیم:
def numberGenerator(n):
number = 0
while number < n:
yield number
number += 1
myGenerator = numberGenerator(3)
print(next(myGenerator))
print(next(myGenerator))
print(next(myGenerator))
کد بالا یک ژنراتور به نام را تعریف می کند numberGenerator
، که یک مقدار دریافت می کند n
به عنوان یک آرگومان، و سپس آن را به عنوان مقدار محدود در یک حلقه while تعریف و استفاده می کند. علاوه بر این، متغیری به نام تعریف می کند number
و مقدار صفر را به آن اختصاص می دهد.
فراخوانی ژنراتور “Instantiated” (myGenerator
) با next()
متد کد مولد را تا اولی اجرا می کند yield
عبارت، که در این مورد 1 را برمی گرداند.
حتی پس از برگرداندن یک مقدار به ما، تابع مقدار متغیر را حفظ می کند number
برای دفعه بعد تابع فراخوانی می شود و مقدار آن را یک عدد افزایش می دهد. بنابراین دفعه بعد که این تابع فراخوانی شود، درست از همان جایی که متوقف شد، ادامه می یابد.
با دو بار فراخوانی تابع، 2 عدد بعدی را در ترتیبی که در زیر مشاهده می کنید به ما ارائه می دهد:
$ python generator_example_1.py
0
1
2
اگر قرار بود دوباره با این ژنراتور تماس بگیریم، a دریافت می کردیم StopIteration
استثنا از آنجایی که کامل شده بود و از حلقه while داخلی خود بازگشته بود.
این عملکرد مفید است زیرا میتوانیم از ژنراتورها برای ایجاد پویا تکرارپذیرها استفاده کنیم روی پرواز. اگر قرار بود بپیچیم myGenerator
با list()
، سپس آرایه ای از اعداد (مانند (0, 1, 2)
) به جای یک شی مولد، که کار با آن در برخی از برنامه ها کمی ساده تر است.
تفاوت بین بازده و بازده
کلمه کلیدی return
مقداری را از یک تابع برمی گرداند، در این زمان تابع حالت محلی خود را از دست می دهد. بنابراین، دفعه بعد که آن تابع را فراخوانی می کنیم، از عبارت اولش دوباره شروع می شود.
از سوی دیگر، yield
حالت بین فراخوانی تابع را حفظ می کند و از جایی که با فراخوانی آن متوقف شد، از سر گرفته می شود next()
دوباره روش بنابراین اگر yield
در ژنراتور فراخوانی می شود، سپس دفعه بعد که همان ژنراتور فراخوانی شد، بلافاصله پس از آخرین نسخه پشتیبان گیری می کنیم yield
بیانیه.
استفاده از بازگشت در یک ژنراتور
یک ژنراتور می توان استفاده از a return
بیانیه، اما فقط بدون مقدار بازگشتی، که به شکل زیر است:
return
هنگامی که ژنراتور را پیدا می کند return
عبارت، مانند هر بازگشت تابع دیگری ادامه می یابد.
همانطور که PEP 255 بیان می کند:
توجه داشته باشید که بازگشت به معنای «تمام شد و هیچ چیز جالبی برای بازگشت ندارم» هم برای توابع مولد و هم برای توابع غیر مولد است.
بیایید مثال قبلی خود را با اضافه کردن یک بند if-else اصلاح کنیم، که بین اعداد بالاتر از 20 تبعیض قائل می شود. کد به شرح زیر است:
def numberGenerator(n):
if n < 20:
number = 0
while number < n:
yield number
number += 1
else:
return
print(list(numberGenerator(30)))
در این مثال، از آنجایی که مولد ما هیچ مقداری را به دست نمی دهد، یک آرایه خالی خواهد بود، زیرا عدد 30 بالاتر از 20 است. بنابراین، دستور return در این مورد مشابه دستور break کار می کند.
این را می توان در زیر مشاهده کرد:
$ python generator_example_2.py
()
اگر مقداری کمتر از 20 نسبت می دادیم، نتایج مشابه مثال اول بود.
استفاده از next() برای تکرار از طریق یک Generator
ما می توانیم مقادیر به دست آمده توسط یک ژنراتور را با استفاده از آن تجزیه کنیم next()
روش، همانطور که در مثال اول مشاهده شد. این روش به مولد میگوید که فقط مقدار بعدی تکرارپذیر را برگرداند، اما هیچ چیز دیگری.
به عنوان مثال، کد زیر خواهد بود print روی مقادیر 0 تا 9 را روی صفحه نمایش دهید.
def numberGenerator(n):
number = 0
while number < n:
yield number
number += 1
g = numberGenerator(10)
counter = 0
while counter < 10:
print(next(g))
counter += 1
کد بالا مشابه کدهای قبلی است، اما هر مقداری را که توسط مولد به دست می آید با تابع فراخوانی می کند. next()
. برای انجام این کار، ابتدا باید یک ژنراتور را نمونه سازی کنیم g
، که مانند متغیری است که حالت مولد ما را نگه می دارد.
زمانی که تابع next()
با ژنراتور به عنوان آرگومان فراخوانی می شود، تابع مولد پایتون تا زمانی که یک عدد را پیدا کند اجرا می شود. yield
بیانیه. سپس مقدار به دست آمده به تماس گیرنده برگردانده می شود و وضعیت ژنراتور برای استفاده بعدی ذخیره می شود.
با اجرای کد بالا خروجی زیر تولید می شود:
$ python generator_example_3.py
0
1
2
3
4
5
6
7
8
9
توجه داشته باشید: با این حال، تفاوت نحوی بین Python 2 و 3 وجود دارد. کد بالا از نسخه Python 3 استفاده می کند. در پایتون 2، next()
می تواند از نحو قبلی استفاده کند یا نحو زیر:
print(g.next())
بیان ژنراتور چیست؟
عبارات مولد مانند هستند فهرست درک، اما آنها به جای لیست، یک ژنراتور را برمی گردانند. آنها در PEP 289 پیشنهاد شدند و بعد از نسخه 2.4 بخشی از پایتون شدند.
نحو شبیه به درک لیست است، اما به جای کروشه، از پرانتز استفاده می کنند.
به عنوان مثال، کد قبلی ما را می توان با استفاده از عبارات مولد به صورت زیر تغییر داد:
g = (x for x in range(10))
print(list(g))
نتایج مانند چند مثال اول ما خواهد بود:
$ python generator_example_4.py
(0, 1, 2, 3, 4, 5, 6, 7, 8, 9)
عبارات ژنراتور هنگام استفاده از توابع کاهش مانند sum()
، min()
، یا max()
، زیرا کد را به یک خط کاهش می دهند. آنها همچنین برای تایپ بسیار کوتاهتر از یک تابع مولد کامل پایتون هستند. به عنوان مثال، کد زیر 10 عدد اول را جمع می کند:
g = (x for x in range(10))
print(sum(g))
پس از اجرای این کد، نتیجه به صورت زیر خواهد بود:
$ python generator_example_5.py
45
مدیریت استثناها
نکته مهمی که باید به آن توجه کرد این است که yield
کلمه کلیدی در آن مجاز نیست try
بخشی از یک تلاش/نهایت ساخت. بنابراین، تولیدکنندگان باید منابع را با احتیاط تخصیص دهند.
با این حال، yield
می توان ظاهر شدن در finally
بندها، except
بندها، یا در try
بخشی از بند های try/به جز.
به عنوان مثال، ما می توانستیم کد زیر را ایجاد کنیم:
def numberGenerator(n):
try:
number = 0
while number < n:
yield number
number += 1
finally:
yield n
print(list(numberGenerator(10)))
در کد بالا، در نتیجه finally
بند، عدد 10 در خروجی گنجانده شده است، و نتیجه لیستی از اعداد از 0 تا 10 است. این معمولاً اتفاق نمی افتد زیرا عبارت شرطی number < n
. این را می توان در خروجی زیر مشاهده کرد:
$ python generator_example_6.py
(0, 1, 2, 3, 4, 5, 6, 7, 8, 9, 10)
ارسال ارزش به ژنراتورها
ژنراتورها یک ابزار قدرتمند در send()
روش برای مولد-تکرارگرها این روش در PEP 342 تعریف شده است و از نسخه 2.5 پایتون در دسترس است.
را send()
متد مولد را از سر گرفته و مقداری را ارسال می کند که برای ادامه با بعدی استفاده می شود yield
. متد مقدار جدیدی را که توسط مولد به دست میآید برمیگرداند.
نحو است send(value)
. در تماس اولیه، این متد باید با فراخوانی شود None
به عنوان یک ارزش نتیجه این خواهد بود که ژنراتور اجرای خود را به مرحله اول پیش می برد yield
اصطلاح.
اگر ژنراتور بدون ایجاد یک مقدار جدید خارج شود (مانند استفاده از return
)، send()
روش افزایش می دهد StopIteration
.
مثال زیر استفاده از send()
. در خطوط اول و سوم ژنراتور خود، از برنامه می خواهیم که متغیر را اختصاص دهد number
مقداری که قبلا به دست آمده است در خط اول بعد از تابع مولد خود، ژنراتور را نمونه سازی می کنیم و اولین آن را تولید می کنیم yield
در خط بعدی با تماس با next
تابع. بنابراین، در خط آخر مقدار 5 را ارسال می کنیم که به عنوان ورودی توسط ژنراتور استفاده می شود و به عنوان بازده قبلی آن در نظر گرفته می شود.
def numberGenerator(n):
number = yield
while number < n:
number = yield number
number += 1
g = numberGenerator(10)
next(g)
print(g.send(5))
توجه داشته باشید: زیرا زمانی که ژنراتور برای اولین بار ایجاد می شود، قبل از استفاده، مقدار بازدهی وجود ندارد send()
، باید مطمئن شویم که مولد با استفاده از مقداری تولید می کند next()
یا send(None)
. در مثال بالا، ما را اجرا می کنیم next(g)
خط فقط به همین دلیل است، در غیر این صورت با خطای “TypeError: can’t send non-none value to a new-started generator” دریافت می کنیم.
پس از اجرای برنامه، آن را چاپ می کند روی مقدار 5 را روی صفحه نمایش می دهد که همان چیزی است که به آن ارسال کردیم:
$ python generator_example_7.py
5
خط سوم مولد ما از بالا همچنین یک ویژگی جدید پایتون را نشان می دهد که در همان PEP معرفی شده است: عبارات بازده. این ویژگی اجازه می دهد تا yield
بند مورد استفاده روی سمت راست بیانیه تکلیف مقدار یک عبارت بازدهی است None
، تا زمانی که برنامه متد را فراخوانی کند send(value)
.
اتصال ژنراتورها
از پایتون 3.3، یک ویژگی جدید به ژنراتورها اجازه میدهد تا خود را به یکدیگر متصل کرده و به یک فرعی واگذار کنند.
عبارت جدید در PEP 380 تعریف شده است و نحو آن عبارت است از:
yield from <expression>
جایی که <expression>
عبارتی است که یک تکرارپذیر را ارزیابی میکند که مولد واگذارکننده را تعریف میکند.
بیایید این را با یک مثال ببینیم:
def myGenerator1(n):
for i in range(n):
yield i
def myGenerator2(n, m):
for j in range(n, m):
yield j
def myGenerator3(n, m):
yield from myGenerator1(n)
yield from myGenerator2(n, m)
yield from myGenerator2(m, m+5)
print(list(myGenerator1(5)))
print(list(myGenerator2(5, 10)))
print(list(myGenerator3(0, 10)))
کد بالا سه مولد مختلف را تعریف می کند. اولی به نام myGenerator1
، دارای یک پارامتر ورودی است که برای تعیین حد در یک محدوده استفاده می شود. دومی به نام myGenerator2
، مشابه مورد قبلی است، اما شامل دو پارامتر ورودی است که دو حد مجاز در محدوده اعداد را مشخص می کند. بعد از این، myGenerator3
تماس می گیرد myGenerator1
و myGenerator2
تا ارزش های خود را ارائه دهند.
سه خط آخر کد print روی صفحه نمایش سه لیست تولید شده از هر یک از سه ژنراتور قبلاً تعریف شده است. همانطور که می بینیم وقتی برنامه زیر را اجرا می کنیم، نتیجه این است myGenerator3
از بازده به دست آمده استفاده می کند myGenerator1
و myGenerator2
، به منظور ایجاد لیستی که سه لیست قبلی را ترکیب می کند.
این مثال همچنین کاربرد مهم ژنراتورها را نشان میدهد: ظرفیت تقسیم یک کار طولانی به چندین بخش جداگانه، که میتواند هنگام کار با مجموعههای بزرگ داده مفید باشد.
$ python generator_example_8.py
(0, 1, 2, 3, 4)
(5, 6, 7, 8, 9)
(0, 1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12, 13, 14)
همانطور که می بینید، با تشکر از yield from
نحو، ژنراتورها را می توان برای برنامه نویسی پویاتر به هم متصل کرد.
مزایای ژنراتورها
- کد ساده شده
همانطور که در مثال های نشان داده شده در این مقاله مشاهده می شود، ژنراتورها کد را به شیوه ای بسیار زیبا ساده می کنند. این سادهسازیها و ظرافتهای کد در عبارات مولد، که در آن یک خط کد جایگزین یک بلوک کامل از کد میشود، بیشتر مشهود است.
- عملکرد بهتر
ژنراتورها کار می کنند روی تنبل (روی-تقاضا) تولید ارزش ها. این منجر به دو مزیت می شود. اول، مصرف حافظه را کاهش دهید. با این حال، اگر فقط یک بار از ژنراتور استفاده کنیم، این ذخیره حافظه به نفع ما خواهد بود. اگر چندین بار از مقادیر استفاده کنیم، ممکن است ارزش داشته باشد که آنها را به یکباره تولید کنیم و برای استفاده بعدی نگه داریم.
را روی- ماهیت تقاضای ژنراتورها همچنین به این معنی است که ما ممکن است مجبور نباشیم مقادیری را تولید کنیم که استفاده نشوند، و بنابراین اگر آنها چرخه های هدر رفته باشند. بود تولید شده است. این بدان معنی است که برنامه شما می تواند فقط از مقادیر مورد نیاز استفاده کند بدون اینکه منتظر بمانید تا همه آنها تولید شوند.
زمان استفاده از ژنراتورها
ژنراتورها ابزار پیشرفته ای هستند که در پایتون وجود دارد. چندین مورد برنامه نویسی وجود دارد که ژنراتورها می توانند کارایی را افزایش دهند. برخی از این موارد عبارتند از:
- پردازش مقادیر زیادی داده: مولدها محاسبه را ارائه می دهند روی-تقاضا، که به آن ارزیابی تنبل نیز می گویند. این تکنیک در پردازش جریان استفاده می شود.
- لوله کشی: ژنراتورهای انباشته را می توان به عنوان لوله، به روشی مشابه لوله های یونیکس، استفاده کرد.
- همزمانی: از ژنراتورها می توان برای تولید (شبیه سازی) همزمانی استفاده کرد.
بسته بندی
ژنراتورها نوعی تابع هستند که دنباله ای از مقادیر را تولید می کنند. به این ترتیب آنها می توانند به شیوه ای مشابه با تکرار کننده ها عمل کنند. استفاده از آنها منجر به کد ظریف تر و بهبود عملکرد می شود.
این جنبهها حتی در عبارات مولد آشکارتر هستند، جایی که یک خط کد میتواند دنبالهای از عبارات را خلاصه کند.
ظرفیت کاری ژنراتورها با روش های جدید بهبود یافته است، مانند send()
، و عبارات تقویت شده، مانند yield from
.
در نتیجه این ویژگیها، ژنراتورها کاربردهای مفید زیادی دارند، مانند تولید لولهها، برنامهنویسی همزمان و کمک به ایجاد جریان از مقادیر زیاد داده.
در نتیجه این پیشرفتها، پایتون بیش از پیش به زبان انتخابی در علم داده تبدیل میشود.
از ژنراتورها برای چه کاری استفاده کرده اید؟ در نظرات به ما اطلاع دهید!
(برچسبها به ترجمه)# python
منتشر شده در 1403-01-29 18:02:03