وبلاگ رسانگار
با ما حرفه ای باشید

سرور مجازی NVMe

کوروتین ها در پایتون

0 9
زمان لازم برای مطالعه: 6 دقیقه


معرفی

هر برنامه نویسی با توابع آشنا است – دنباله ای از دستورالعمل ها که به عنوان یک واحد واحد در کنار هم قرار می گیرند تا وظایف از پیش تعیین شده را انجام دهند. آنها یک نقطه ورودی واحد را می پذیرند، می توانند آرگومان ها را بپذیرند، ممکن است مقدار بازگشتی داشته باشند یا نداشته باشند، و می توانند در هر لحظه در طول اجرای برنامه – از جمله توسط توابع دیگر و خودشان – فراخوانی شوند.

هنگامی که یک برنامه تابعی را فراخوانی می کند، متن اجرای فعلی آن قبل از انتقال کنترل به تابع و از سرگیری اجرا، ذخیره می شود. سپس تابع یک زمینه جدید ایجاد می کند – از آنجا روی داده های جدید ایجاد شده منحصراً در طول زمان اجرا وجود دارد.

به محض تکمیل کار، کنترل به تماس گیرنده باز می گردد – زمینه جدید به طور موثر حذف شده و با متن قبلی جایگزین می شود.

کوروتین ها

Coroutine ها نوع خاصی از عملکرد هستند که به عمد بازده کنترل بر روی تماس گیرنده است، اما زمینه آن را به پایان نمی رساند process، در عوض آن را در حالت بیکار نگه دارید.

آنها از توانایی نگهداری داده های خود در طول عمر خود بهره می برند و بر خلاف توابع، می توانند چندین نقطه ورودی برای تعلیق و از سرگیری اجرا داشته باشند.

کوروتین ها در پایتون به روشی بسیار مشابه کار می کنند ژنراتورها. هر دو بر روی داده ها کار می کنند، بنابراین اجازه دهید تفاوت های اصلی را ساده نگه داریم:

ژنراتورها تولید کردن داده ها

کوروتین ها مصرف کردن داده ها

مدیریت متمایز کلمه کلیدی yield تعیین می کند که آیا ما در حال دستکاری یکی یا دیگری هستیم.

تعریف کوروتین

با وجود همه موارد ضروری، اجازه دهید به سرعت وارد شویم و اولین برنامه خود را کدگذاری کنیم:

def bare_bones():
    while True:
        value = (yield)

مشاهده شباهت به یک تابع معمولی پایتون واضح است. را while True: بلوک اجرای مداوم برنامه را تا زمانی که مقادیر دریافت می کند تضمین می کند.

ارزش از طریق جمع آوری می شود yield بیانیه. چند لحظه دیگر به این موضوع باز خواهیم گشت…

واضح است که این کد عملاً بی فایده است، بنابراین ما آن را با چند مورد کامل می کنیم print بیانیه:

def bare_bones():
    print("My first Coroutine!")
    while True:
        value = (yield)
        print(value)

حال، چه اتفاقی می‌افتد وقتی سعی می‌کنیم آن را اینطور بنامیم:

coroutine = bare_bones()

اگر این یک تابع معمولی پایتون بود، می‌توان انتظار داشت که در این نقطه نوعی خروجی تولید کند. اما اگر کد را در حالت فعلی اجرا کنید، متوجه خواهید شد که یک کد نیست print() تماس می گیرد

این به این دلیل است که روال‌ها به این نیاز دارند next() روشی که ابتدا باید فراخوانی شود:

def bare_bones():
    print("My first Coroutine!")
    while True:
        value = (yield)
        print(value)

coroutine = bare_bones()
next(coroutine)

این کار اجرای کوروتین را شروع می کند تا زمانی که به اولین نقطه شکست خود برسد – value = (yield). سپس، متوقف می شود، اجرا را به اصلی برمی گرداند، و در حالی که منتظر ورودی جدید است، بیکار می شود:

My first Coroutine!

ورودی جدید را می توان با ارسال کرد send():

coroutine.send("First Value")

متغیر ما value سپس رشته را دریافت خواهد کرد First Value، print آن، و یک تکرار جدید از while True: حلقه کوروتین را مجبور می کند یک بار دیگر منتظر تحویل مقادیر جدید بماند. می توانید این کار را هر چند بار که دوست دارید انجام دهید.

پیشنهاد می‌کنیم بخوانید:  استفاده از cURL در پایتون با PycURL

در نهایت، هنگامی که کار روتین را تمام کردید و دیگر نمی‌خواهید از آن استفاده کنید، می‌توانید با تماس گرفتن آن منابع را آزاد کنید. close(). این امر الف را بالا می برد GeneratorExit استثنایی که باید به آن رسیدگی شود:

def bare_bones():
    print("My first Coroutine!")
    try:
        while True:
            value = (yield)
            print(value)
    except GeneratorExit:
        print("Exiting coroutine...")

coroutine = bare_bones()
next(coroutine)
coroutine.send("First Value")
coroutine.send("Second Value")
coroutine.close()

خروجی:

My first Coroutine!
First Value
Second Value
Exiting coroutine...

گذراندن استدلال ها

مانند توابع، کوروتین ها نیز قادر به دریافت آرگومان هستند:

def filter_line(num):
    while True:
        line = (yield)
        if num in line:
            print(line)

cor = filter_line("33")
next(cor)
cor.send("Jessica, age:24")
cor.send("Marco, age:33")
cor.send("Filipe, age:55")

خروجی:

Marco, age:33

اعمال چندین نقطه انفصال

چندگانه yield عبارات را می توان در یک برنامه جداگانه با هم ترتیب داد:

def joint_print():
    while True:
        part_1 = (yield)
        part_2 = (yield)
        print("{} {}".format(part_1, part_2))

cor = joint_print()
next(cor)
cor.send("So Far")
cor.send("So Good")

خروجی:

So Far So Good

استثناء StopIteration

پس از بسته شدن یک برنامه، تماس بگیرید send() دوباره a ایجاد خواهد کرد StopIteration استثنا:

def test():
    while True:
        value = (yield)
        print(value)
try:
    cor = test()
    next(cor)
    cor.close()
    cor.send("So Good")
except StopIteration:
    print("Done with the basics")

خروجی:

Done with the basics

روتین با دکوراتورها

این همه خوب و خوب است! اما هنگام کار در پروژه های بزرگتر شروع می شود تک تک coroutine به صورت دستی می تواند چنین کشش بزرگی باشد!

نگران نباشید، فقط موضوع بهره برداری از قدرت است دکوراتورها بنابراین ما دیگر نیازی به استفاده از آن نداریم next() روش:

def coroutine(func):
    def start(*args, **kwargs):
        cr = func(*args, **kwargs)
        next(cr)
        return cr
    return start

@coroutine
def bare_bones():
    while True:
        value = (yield)
        print(value)

cor = bare_bones()
cor.send("Using a decorator!")

با اجرای این قطعه کد به دست می آید:

Using a decorator!

ساخت خطوط لوله

خط لوله دنباله ای از عناصر پردازشی است که به گونه ای سازماندهی شده اند که خروجی هر عنصر ورودی عنصر بعدی باشد.

داده ها از طریق لوله تحت فشار قرار می گیرند تا زمانی که در نهایت مصرف شوند. هر خط لوله حداقل به یک خط لوله نیاز دارد منبع و یکی فرو رفتن.

مراحل باقی مانده از لوله می تواند چندین عملیات مختلف را انجام دهد، از فیلتر کردن تا اصلاح، مسیریابی و کاهش داده ها:

خط لوله

Coroutine ها کاندیدهای طبیعی برای انجام این عملیات هستند، آنها می توانند داده ها را بین یکدیگر ارسال کنند send() عملیات و همچنین می تواند به عنوان مصرف کننده نقطه پایانی خدمت کند. بیایید به مثال زیر نگاه کنیم:

def producer(cor):
    n = 1
    while n < 100:
        cor.send(n)
        n = n * 2

@coroutine
def my_filter(num, cor):
    while True:
        n = (yield)
        if n < num:
            cor.send(n)

@coroutine
def printer():
    while True:
        n = (yield)
        print(n)

prnt = printer()
filt = my_filter(50, prnt)
producer(filt)

خروجی:

1
2
4
8
16
32

بنابراین، آنچه ما در اینجا داریم این است producer() عمل به عنوان منبع، مقادیری را ایجاد می کند که سپس قبل از چاپ توسط فیلتر فیلتر می شوند فرو رفتن، در این مورد، printer() روتین

پیشنهاد می‌کنیم بخوانید:  جاوا اسکریپت: حذف یک ویژگی از یک ObjectAn شی در جاوا اسکریپت مجموعه ای از جفت های کلید-مقدار است. یکی از این جفت‌های کلید-مقدار، ویژگی شی نامیده می‌شود. هر دو کلید و مقادیر خصوصیات می توانند از هر نوع داده ای باشند - Number، String، Array، Object، و غیره. به عنوان مثال: const dog = { name: "Sandy", age: 3, emoji: "...

my_filter(50, prnt) به عنوان تنها مرحله واسطه در خط لوله عمل می کند و برنامه خود را به عنوان استدلال دریافت می کند.

این زنجیره‌بندی کاملاً قدرت برنامه‌ها را نشان می‌دهد: آنها برای پروژه‌های بزرگ‌تر مقیاس‌پذیر هستند (همه آنچه که لازم است اضافه کردن مراحل بیشتر به خط لوله است) و به راحتی قابل نگهداری هستند (تغییر در یکی باعث بازنویسی کامل کد منبع نمی‌شود).

شباهت به اشیا

یک برنامه نویس تیزبین ممکن است متوجه شود روی که کوروتین ها دارای شباهت مفهومی خاصی به اشیاء پایتون هستند. از تعریف قبلی مورد نیاز تا اعلام و مدیریت نمونه. این سوال واضح مطرح می‌شود که چرا باید از کوروتین‌ها بر روی الگوی آزمایش شده و واقعی برنامه‌نویسی شی‌گرا استفاده کرد.

خوب، جدا از این واقعیت واضح که کوروتین ها فقط به یک تعریف تابع نیاز دارند، آنها از سرعت قابل توجهی نیز بهره مند می شوند. بیایید کد زیر را بررسی کنیم:

class obj:
    def __init__(self, value):
        self.i = value
    def send(self, num):
        print(self.i + num)

inst = obj(1)
inst.send(5)
def coroutine(value):
    i = value
    while True:
        num = (yield)
        print(i + num)

cor = coroutine(1)
next(cor)
cor.send(5)

در اینجا این است که چگونه این دو در مقابل یکدیگر ایستادگی می کنند، زمانی که از میان آنها عبور می کنند timeit ماژول، 10000 بار:

هدف – شی کوروتین
0.791811 0.6343617
0.7997058 0.6383156
0.8579286 0.6365501
0.838439 0.648442
0.9604255 0.7242559

هر دو یک کار ساده را انجام می دهند اما مثال دوم سریعتر است. سرعت به دلیل عدم وجود جسم به دست می آید self جستجوها

برای کارهای مالیاتی بیشتر سیستم، این ویژگی دلیل قانع کننده ای برای استفاده از کوروتین ها به جای اشیاء کنترل کننده معمولی است.

هنگام استفاده از کوروتین ها احتیاط کنید

را ارسال() روش است نه نخ ایمن

import threading
from time import sleep

def print_number(cor):
    while True:
        cor.send(1)

def coroutine():
    i = 1
    while True:
        num = (yield)
        print(i)
        sleep(3)
        i += num

cor = coroutine()
next(cor)

t = threading.Thread(target=print_number, args=(cor,))
t.start()

while True:
    cor.send(5)

زیرا send() به درستی همگام سازی نشده بود، و همچنین محافظت ذاتی در برابر فراخوانی اشتباه مربوط به نخ ندارد، خطای زیر مطرح شد: ValueError: generator already executing.

اختلاط کوروتین ها با همزمانی باید با احتیاط زیاد انجام شود.

حلقه کردن کوروتین ها ممکن نیست

def coroutine_1(value):
    while True:
        next_cor = (yield)
        print(value)
        value = value - 1
        if next_cor != None:
            next_cor.send(value)

def coroutine_2(next_cor):
    while True:
        value = (yield)
        print(value)
        value = value - 2
        if next != None:
            next_cor.send(value)

cor1 = coroutine_1(20)
next(cor1)
cor2 = coroutine_2(cor1)
next(cor2)
cor1.send(cor2)

همینطور ValueError چهره خود را نشان می دهد. از این مثال‌های ساده می‌توان نتیجه گرفت که send() متد نوعی پشته تماس ایجاد می کند که تا زمانی که هدف به آن برسد، برنمی گردد yield بیانیه.

بنابراین، استفاده از کوروتین ها تماماً نور خورشید و رنگین کمان نیست، قبل از استفاده باید به دقت فکر کرد.

نتیجه

Coroutine ها جایگزین قدرتمندی برای مکانیسم های معمول پردازش داده ها هستند. واحدهای کد را می توان به راحتی ترکیب، اصلاح و بازنویسی کرد، در حالی که از ماندگاری متغیر در طول چرخه عمر آن سود می برد.

در دست یک برنامه نویس حیله گر، کوروتین ها با اجازه دادن به طراحی و پیاده سازی ساده تر، به ابزارهای جدید و معناداری تبدیل می شوند و در عین حال دستاوردهای عملکردی قابل توجهی را ارائه می دهند.

حذف ایده‌ها در فرآیندهای ساده، باعث صرفه‌جویی در تلاش و زمان برنامه‌نویس می‌شود، و در عین حال از پر کردن کد با اشیاء اضافی که کاری بیش از کارهای ابتدایی انجام نمی‌دهند، اجتناب می‌کند.

(برچسب‌ها به ترجمه)# python



منتشر شده در 1403-01-19 19:15:03

امتیاز شما به این مطلب
دیدگاه شما در خصوص مطلب چیست ؟

آدرس ایمیل شما منتشر نخواهد شد.

لطفا دیدگاه خود را با احترام به دیدگاه های دیگران و با توجه به محتوای مطلب درج کنید