برنامه نویسی

پردازش DAG ها با پایتون ناهمگام و graphlib

Summarize this content to 400 words in Persian Lang اخیراً با یک ماژول جالب در کتابخانه استاندارد بی انتها پایتون مواجه شدم: graphlib. اگر قبلاً با آن کار نکرده اید، این یک ابزار کوچک است که در Python 3.9 اضافه شده است و فقط یک کلاس را پیاده سازی می کند: TopologicalSorter.

نام خود توضیحی است — این یک کلاس برای مرتب سازی توپولوژیکی یک نمودار است. اما من فکر نمی‌کنم که در ابتدا فقط با مرتب‌سازی نوشته شده باشد، زیرا API نسبتاً مرموز، اما فوق‌العاده مفیدی دارد، مانند prepare() روش یا is_active(). این مثال در مستندات به انگیزه پشت آن اشاره می کند:

topological_sorter = TopologicalSorter()

# Add nodes to ‘topological_sorter’…

topological_sorter.prepare()
while topological_sorter.is_active():
for node in topological_sorter.get_ready():
# Worker threads or processes take nodes to work on off the
# ‘task_queue’ queue.
task_queue.put(node)

# When the work for a node is done, workers put the node in
# ‘finalized_tasks_queue’ so we can get more nodes to work on.
# The definition of ‘is_active()’ guarantees that, at this point, at
# least one node has been placed on ‘task_queue’ that hasn’t yet
# been passed to ‘done()’, so this blocking ‘get()’ must (eventually)
# succeed. After calling ‘done()’, we loop back to call ‘get_ready()’
# again, so put newly freed nodes on ‘task_queue’ as soon as
# logically possible.
node = finalized_tasks_queue.get()
topological_sorter.done(node)

بنابراین graphlib یک ماژول فقط برای مرتب‌سازی نمودارها نیست، بلکه ابزاری برای اجرای نمودارهای وظایف به ترتیب توپولوژیکی است، که در صورتی مفید است که حجم کاری شما بسته به نتایج کارهای دیگر وظایفی داشته باشد. نمودارها روشی عالی برای مدل‌سازی این مشکل هستند و ترتیب توپولوژیکی این است که چگونه از پردازش وظایف به ترتیب صحیح مطمئن شوید.

یکی از مواردی که در اسناد وجود ندارد این است asyncio به عنوان مثال، به نظر می رسد که نوشتن آن بسیار آسان است. از آنجایی که با asyncio نیازی نیست که با thread-safety سر و کار داشته باشید، می‌توانید بدون استفاده از صف برای همگام‌سازی نخ‌ها یا هر گونه پیچیدگی اضافی دیگری از این دست بگذرید.

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

async def visit(node: str, sorter: TopologicalSorter):
print(f”processing node {node}”)
sorter.done(node)

در دنیای واقعی، این می‌تواند تا زمانی که شما می‌خواهید پیچیده باشد، تا زمانی که شما کار I/O را انجام می‌دهید، بنابراین از مزایای asyncio بهره ببرید. نکته مهم این است که با آن تماس بگیرید sorter.done(node) در پایان تابع اجازه دهید نمونه از TopologicalSorter بدانید که کار ما با این گره تمام شده است و می توانیم به گره بعدی برویم.

سپس آن را وصل می کنیم visit در اجرای مرتب شده توپولوژیکی ما عمل کنید:

sorter = TopologicalSorter(graph)

sorter.prepare()

while sorter.is_active():
node_group = sorter.get_ready()

if not node_group:
# no nodes are ready yet, so we sleep for a bit
await asyncio.sleep(0.25)
else:
tasks = set()
for node in node_group:
task = asyncio.create_task(visit(node, sorter))
tasks.add(task)
task.add_done_callback(tasks.discard)

کد منبع کامل یک اسکریپت کار را می توان در این اصل یافت.

یک جنبه خاص از graphlib فرمت نمودار است TopologicalSorter به عنوان یک آرگومان می پذیرد — به ترتیب معکوس از نمایش معمولی شما از یک نمودار است. به عنوان مثال اگر شما یک نمودار مانند این دارید A -> B -> C، معمولاً شما باید آن را اینگونه نشان دهید:

graph = {
“A”: [“B”],
“B”: [“C”],
“C”: [],
}

اما TopologicalSorter می خواهد این نمودار در جهت لبه معکوس شود:

اگر آرگومان گراف اختیاری ارائه شود، باید فرهنگ لغتی باشد که یک گراف غیر چرخه ای جهت دار را نشان می دهد که در آن کلیدها گره هستند و مقادیر تکرارپذیر همه پیشینیان آن گره در گراف هستند.

پس راه درست نمایندگی A -> B -> C برای TopologicalSorter این است:

graph = {
“C”: [“B”],
“B”: [“A”],
“A”: [],
}

اطلاعات بیشتر و یک بحث نسبتاً داغ در این مورد را می‌توانید در اینجا پیدا کنید: https://bugs.python.org/issue46071.

کد نویسی مبارک!

اخیراً با یک ماژول جالب در کتابخانه استاندارد بی انتها پایتون مواجه شدم: graphlib. اگر قبلاً با آن کار نکرده اید، این یک ابزار کوچک است که در Python 3.9 اضافه شده است و فقط یک کلاس را پیاده سازی می کند: TopologicalSorter.

نام خود توضیحی است — این یک کلاس برای مرتب سازی توپولوژیکی یک نمودار است. اما من فکر نمی‌کنم که در ابتدا فقط با مرتب‌سازی نوشته شده باشد، زیرا API نسبتاً مرموز، اما فوق‌العاده مفیدی دارد، مانند prepare() روش یا is_active(). این مثال در مستندات به انگیزه پشت آن اشاره می کند:

topological_sorter = TopologicalSorter()

# Add nodes to 'topological_sorter'...

topological_sorter.prepare()
while topological_sorter.is_active():
    for node in topological_sorter.get_ready():
        # Worker threads or processes take nodes to work on off the
        # 'task_queue' queue.
        task_queue.put(node)

    # When the work for a node is done, workers put the node in
    # 'finalized_tasks_queue' so we can get more nodes to work on.
    # The definition of 'is_active()' guarantees that, at this point, at
    # least one node has been placed on 'task_queue' that hasn't yet
    # been passed to 'done()', so this blocking 'get()' must (eventually)
    # succeed.  After calling 'done()', we loop back to call 'get_ready()'
    # again, so put newly freed nodes on 'task_queue' as soon as
    # logically possible.
    node = finalized_tasks_queue.get()
    topological_sorter.done(node)

بنابراین graphlib یک ماژول فقط برای مرتب‌سازی نمودارها نیست، بلکه ابزاری برای اجرای نمودارهای وظایف به ترتیب توپولوژیکی است، که در صورتی مفید است که حجم کاری شما بسته به نتایج کارهای دیگر وظایفی داشته باشد. نمودارها روشی عالی برای مدل‌سازی این مشکل هستند و ترتیب توپولوژیکی این است که چگونه از پردازش وظایف به ترتیب صحیح مطمئن شوید.

یکی از مواردی که در اسناد وجود ندارد این است asyncio به عنوان مثال، به نظر می رسد که نوشتن آن بسیار آسان است. از آنجایی که با asyncio نیازی نیست که با thread-safety سر و کار داشته باشید، می‌توانید بدون استفاده از صف برای همگام‌سازی نخ‌ها یا هر گونه پیچیدگی اضافی دیگری از این دست بگذرید.

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

async def visit(node: str, sorter: TopologicalSorter):
    print(f"processing node {node}")
    sorter.done(node)

در دنیای واقعی، این می‌تواند تا زمانی که شما می‌خواهید پیچیده باشد، تا زمانی که شما کار I/O را انجام می‌دهید، بنابراین از مزایای asyncio بهره ببرید. نکته مهم این است که با آن تماس بگیرید sorter.done(node) در پایان تابع اجازه دهید نمونه از TopologicalSorter بدانید که کار ما با این گره تمام شده است و می توانیم به گره بعدی برویم.

سپس آن را وصل می کنیم visit در اجرای مرتب شده توپولوژیکی ما عمل کنید:

sorter = TopologicalSorter(graph)

sorter.prepare()

while sorter.is_active():
    node_group = sorter.get_ready()

    if not node_group:
        # no nodes are ready yet, so we sleep for a bit
        await asyncio.sleep(0.25)
    else:
        tasks = set()
        for node in node_group:
            task = asyncio.create_task(visit(node, sorter))
            tasks.add(task)
            task.add_done_callback(tasks.discard)

کد منبع کامل یک اسکریپت کار را می توان در این اصل یافت.

یک جنبه خاص از graphlib فرمت نمودار است TopologicalSorter به عنوان یک آرگومان می پذیرد — به ترتیب معکوس از نمایش معمولی شما از یک نمودار است. به عنوان مثال اگر شما یک نمودار مانند این دارید A -> B -> C، معمولاً شما باید آن را اینگونه نشان دهید:

graph = {
  "A": ["B"],
  "B": ["C"],
  "C": [],
}

اما TopologicalSorter می خواهد این نمودار در جهت لبه معکوس شود:

اگر آرگومان گراف اختیاری ارائه شود، باید فرهنگ لغتی باشد که یک گراف غیر چرخه ای جهت دار را نشان می دهد که در آن کلیدها گره هستند و مقادیر تکرارپذیر همه پیشینیان آن گره در گراف هستند.

پس راه درست نمایندگی A -> B -> C برای TopologicalSorter این است:

graph = {
  "C": ["B"],
  "B": ["A"],
  "A": [],
}

اطلاعات بیشتر و یک بحث نسبتاً داغ در این مورد را می‌توانید در اینجا پیدا کنید: https://bugs.python.org/issue46071.

کد نویسی مبارک!

نوشته های مشابه

دیدگاهتان را بنویسید

نشانی ایمیل شما منتشر نخواهد شد. بخش‌های موردنیاز علامت‌گذاری شده‌اند *

دکمه بازگشت به بالا