4. مدل اجرا

4.1. ساختار یک برنامه

یک برنامه پایتون از بلوک‌های کد ساخته شده است. یک بلوک <block> بخشی از متن برنامه پایتون است که به‌عنوان یک واحد اجرا می‌شود. موارد زیر بلوک هستند: ماژول، بدنه تابع، و تعریف کلاس. هر دستوری که به‌صورت تعاملی تایپ شود یک بلوک است. یک پرونده اسکریپت (پرونده‌ای که به‌عنوان ورودی استاندارد به مفسر داده شود یا به‌عنوان آرگومان خط فرمان برای مفسر مشخص شود) یک بلوک کد است. یک دستور اسکریپت (دستوری که در خط فرمان مفسر با گزینه -c مشخص شده است) یک بلوک کد است. ماژولی که به‌عنوان اسکریپت سطح بالا (به‌عنوان ماژول __main__) از خط فرمان با استفاده از آرگومان -m اجرا می‌شود نیز یک بلوک کد است. آرگومان رشته‌ای که به توابع توکار eval() و exec() داده می‌شود یک بلوک کد است.

یک بلوک کد در یک فریم اجرا <execution frame> اجرا می‌شود. یک فریم شامل برخی اطلاعات مدیریتی (که برای اشکال‌زدایی استفاده می‌شود) است و تعیین می‌کند که پس از تکمیل اجرای بلوک کد، اجرا در کجا و چگونه ادامه یابد.

4.2. نام‌گذاری و مقیدسازی

4.2.1. پیوند نام‌ها

نام‌ها <Names> به اشیاء اشاره می‌کنند. نام‌ها توسط عملیات مقیدسازی نام معرفی می‌شوند.

سازه‌های زیر نام‌ها را مقید می‌کنند:

  • پارامترهای صوری توابع،

  • تعاریف کلاس،

  • تعریف توابع،

  • عبارت‌های انتساب،

  • اهداف که اگر در یک انتساب ظاهر شوند، شناسه هستند:

    • سرآیند حلقه‌ی for،

    • پس از as در یک دستور with، بند except، بند except*، یا در الگوی as در تطبیق الگوی ساختاری،

    • در یک الگوی ثبت‌کننده (capture pattern) در تطبیق الگوی ساختاری

  • دستورهای import.

  • دستورهای type.

  • فهرست‌های پارامتر نوع.

دستور import به شکل from ... import * همه‌ی نام‌های تعریف‌شده در ماژول ایمپورت‌شده را مقید می‌کند، به‌جز آن‌هایی که با یک زیرخط آغاز می‌شوند. این شکل فقط می‌تواند در سطح ماژول استفاده شود.

هدفی که در یک دستور del ظاهر می‌شود نیز برای این منظور مقید در نظر گرفته می‌شود (هرچند معنای واقعی آن آزاد کردن نام است).

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

اگر نامی در یک بلوک مقید شود، یک متغیر محلی آن بلوک است، مگر اینکه به‌عنوان nonlocal یا global اعلام شده باشد. اگر نامی در سطح ماژول مقید شود، یک متغیر سراسری است. (متغیرهای بلوک کد ماژول هم محلی و هم سراسری هستند.) اگر متغیری در یک بلوک کد استفاده شود اما در آنجا تعریف نشده باشد، یک متغیر آزاد است.

هر ظهور یک نام در متن برنامه به پیوند <binding> آن نام اشاره دارد که بر اساس قواعد حل نام زیر برقرار شده است.

4.2.2. حل نام‌ها

یک محدوده <scope> قابلیت مشاهده‌ی یک نام درون یک بلوک را تعریف می‌کند. اگر یک متغیر محلی در یک بلوک تعریف شود، محدوده‌ی آن شامل آن بلوک می‌شود. اگر تعریف در یک بلوک تابع انجام شود، محدوده به هر بلوکی که درون بلوک تعریف‌کننده قرار دارد گسترش می‌یابد، مگر اینکه بلوکی درون آن، مقیدسازی متفاوتی برای آن نام ایجاد کند.

هنگامی که از یک نام در یک بلوک کد استفاده می‌شود، آن نام با استفاده از نزدیک‌ترین محدوده‌ی دربرگیرنده تعیین می‌شود. مجموعه‌ی همه‌ی چنین محدوده‌هایی که برای یک بلوک کد قابل مشاهده هستند، محیط <environment> آن بلوک نامیده می‌شود.

هنگامی که نامی به‌هیچ‌وجه پیدا نشود، یک استثنای NameError پرتاب می‌شود. اگر محدوده فعلی یک محدوده تابع باشد و نام به متغیر محلی اشاره کند که هنوز در نقطه‌ای که نام استفاده می‌شود به مقداری متصل نشده باشد، یک استثنای UnboundLocalError پرتاب می‌شود. UnboundLocalError زیرکلاسی از NameError است.

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

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

دستور global همان محدوده‌ی یک عملیات مقیدسازی نام در همان بلوک را دارد. اگر نزدیک‌ترین محدوده‌ی دربرگیرنده برای یک متغیر آزاد شامل یک دستور global باشد، آن متغیر آزاد به‌عنوان یک متغیر سراسری در نظر گرفته می‌شود.

دستور nonlocal باعث می‌شود نام‌های مربوطه به متغیرهای از پیش مقیدشده در نزدیک‌ترین محدوده تابع دربرگیرنده ارجاع دهند. اگر نام داده‌شده در هیچ محدوده تابع دربرگیرنده‌ای وجود نداشته باشد، SyntaxError در زمان کامپایل پرتاب می‌شود. پارامترهای نوع نمی‌توانند با دستور nonlocal دوباره مقید شوند.

فضای نام یک ماژول به‌طور خودکار در اولین باری که یک ماژول ایمپورت می‌شود ایجاد می‌شود. ماژول اصلی یک اسکریپت همیشه __main__ نامیده می‌شود.

بلوک‌های تعریف کلاس و آرگومان‌های exec() و eval() در زمینه‌ی حل نام خاص هستند. تعریف کلاس یک دستور قابل اجرا است که ممکن است از نام‌ها استفاده کند و آن‌ها را تعریف کند. این ارجاع‌ها از قوانین عادی وضوح نام پیروی می‌کنند، با این استثنا که متغیرهای محلی مقیدنشده در فضای نام سراسری جستجو می‌شوند. فضای نام تعریف کلاس به دیکشنری ویژگی‌های کلاس تبدیل می‌شود. محدوده‌ی نام‌های تعریف‌شده در یک بلوک کلاس به همان بلوک کلاس محدود است؛ این محدوده به بلوک‌های کد متدها گسترش نمی‌یابد. این شامل درک‌ها و عبارات تولیدگر می‌شود، اما شامل محدوده‌های annotation نمی‌شود، که به محدوده‌های کلاس دربرگیرنده خود دسترسی دارند. این بدان معناست که کد زیر شکست خواهد خورد:

class A:
    a = 42
    b = list(a + i for i in range(10))

با این حال، موارد زیر موفق خواهند بود:

class A:
    type Alias = Nested
    class Nested: pass

print(A.Alias.__value__)  # <type 'A.Nested'>

4.2.3. محدوده‌های حاشیه‌نویسی

حاشیه‌نویسی‌ها، فهرست‌های پارامتر نوع و دستورات type، محدوده‌های حاشیه‌نویسی را معرفی می‌کنند که عمدتاً مانند محدوده‌های تابع رفتار می‌کنند، اما با چند استثنا که در زیر توضیح داده شده‌اند.

محدوده‌های حاشیه‌نویسی در زمینه‌های زیر استفاده می‌شوند:

محدوده‌های annotation به روش‌های زیر با محدوده‌های تابع تفاوت دارند:

  • محدوده‌های annotation (annotation scope) به فضای نام کلاس دربرگیرنده‌ی خود دسترسی دارند. اگر یک محدوده‌ی annotation بلافاصله درون یک محدوده‌ی کلاس، یا درون یک محدوده‌ی annotation دیگر که خود بلافاصله درون یک محدوده‌ی کلاس قرار داشته باشد، کد درون محدوده‌ی annotation می‌تواند از نام‌های تعریف‌شده در محدوده‌ی کلاس استفاده کند، گویی مستقیماً درون بدنه‌ی کلاس اجرا شده است. این در تضاد با توابع معمولی تعریف‌شده درون کلاس‌ها است؛ آن‌ها نمی‌توانند به نام‌های تعریف‌شده در محدوده‌ی کلاس دسترسی داشته باشند.

  • در محدوده‌های حاشیه‌نویسی، عبارت‌ها نمی‌توانند شامل yield، yield from، await یا عبارت‌های := باشند. (این عبارت‌ها در سایر محدوده‌هایی که در محدوده حاشیه‌نویسی قرار دارند، مجاز هستند.)

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

  • در حالی که محدوده‌های annotation (annotation scopes) یک نام داخلی دارند، آن نام در qualified name اشیای تعریف‌شده در محدوده منعکس نمی‌شود. در عوض، __qualname__ چنین اشیایی به‌گونه‌ای است که گویی شیء در محدوده دربرگیرنده تعریف‌شده باشد.

اضافه شده در نسخه‌ی 3.12: محدوده‌های حاشیه‌نویسی (annotation scopes) در پایتون 3.12 به‌عنوان بخشی از PEP 695 معرفی شدند.

تغییر یافته در نسخه‌ی 3.13: محدوده‌های حاشیه‌نویسی همچنین برای مقادیر پیش‌فرض پارامترهای نوع استفاده می‌شوند، همان‌طور که در PEP 696 معرفی شده است.

تغییر یافته در نسخه‌ی 3.14: محدوده‌های حاشیه‌نویسی اکنون برای حاشیه‌نویسی‌ها نیز به کار می‌روند، همان‌طور که در PEP 649 و PEP 749 مشخص شده است.

4.2.4. ارزیابی تنبل

بیشتر محدوده‌های حاشیه‌نویسی به‌صورت تنبل ارزیابی می‌شوند. این شامل حاشیه‌نویسی‌ها، مقدارهای نام‌های مستعار نوع ایجادشده از طریق دستور type، و کران‌ها، قیدها و مقدارهای پیش‌فرض متغیرهای نوع ایجادشده از طریق سینتکس پارامترهای نوع می‌شود. این بدان معناست که آن‌ها هنگام ایجاد نام مستعار نوع یا متغیر نوع، یا هنگام ایجاد شیء حامل حاشیه‌نویسی‌ها ارزیابی نمی‌شوند. در عوض، آن‌ها فقط در صورت لزوم ارزیابی می‌شوند، برای مثال، زمانی که به ویژگی __value__ یک نام مستعار نوع دسترسی پیدا می‌شود.

مثال:

>>> type Alias = 1/0
>>> Alias.__value__
Traceback (most recent call last):
  ...
ZeroDivisionError: division by zero
>>> def func[T: 1/0](): pass
>>> T = func.__type_params__[0]
>>> T.__bound__
Traceback (most recent call last):
  ...
ZeroDivisionError: division by zero

در اینجا استثنا تنها زمانی پرتاب می‌شود که به ویژگی __value__ نام مستعار نوع یا ویژگی __bound__ متغیر نوع دسترسی پیدا شود.

این رفتار عمدتاً برای ارجاع به انواعی مفید است که هنوز در زمان ایجاد نام مستعار نوع یا متغیر نوع تعریف نشده‌اند. برای مثال، ارزیابی تنبل امکان ایجاد نام‌های مستعار نوع بازگشتی متقابل را فراهم می‌کند:

from typing import Literal

type SimpleExpr = int | Parenthesized
type Parenthesized = tuple[Literal["("], Expr, Literal[")"]]
type Expr = SimpleExpr | tuple[SimpleExpr, Literal["+", "-"], Expr]

مقادیری که به‌صورت تنبل ارزیابی می‌شوند، در محدوده حاشیه‌نویسی (annotation scope) ارزیابی می‌شوند، به این معنا که نام‌هایی که درون مقدار با ارزیابی تنبل ظاهر می‌شوند، به‌گونه‌ای جستجو می‌شوند که گویی در محدوده دربرگیرنده مستقیم استفاده شده‌اند.

اضافه شده در نسخه‌ی 3.12.

4.2.5. توکارها و اجرای محدود

کاربران نباید __builtins__ را دستکاری کنند؛ این صرفاً یک جزئیات پیاده‌سازی است. کاربرانی که می‌خواهند مقادیر موجود در فضای نام builtins را بازنویسی کنند، باید ماژول builtins را import کنند و ویژگی‌های آن را به‌طور مناسب تغییر دهند.

فضای نام builtins مرتبط با اجرای یک بلوک کد، در واقع با جستجوی نام __builtins__ در فضای نام سراسری آن یافت می‌شود؛ این باید یک دیکشنری یا یک ماژول باشد (در حالت دوم، از دیکشنری آن ماژول استفاده می‌شود). به‌طور پیش‌فرض، هنگامی که در ماژول __main__ هستید، __builtins__ ماژول توکار builtins است؛ هنگامی که در هر ماژول دیگری هستید، __builtins__ نام مستعاری برای دیکشنری خود ماژول builtins است.

4.2.6. تعامل با ویژگی‌های پویا

حل نام متغیرهای آزاد در ران‌تایم رخ می‌دهد، نه در زمان کامپایل. این بدان معناست که کد زیر ۴۲ را چاپ خواهد کرد:

i = 10
def f():
    print(i)
i = 42
f()

توابع eval() و exec() برای حل نام‌ها به محیط کامل دسترسی ندارند. نام‌ها ممکن است در فضای نام محلی و سراسری فراخواننده حل شوند. متغیرهای آزاد در نزدیک‌ترین فضای نام دربرگیرنده حل نمی‌شوند، بلکه در فضای نام سراسری حل می‌شوند. [1] توابع exec() و eval() آرگومان‌های اختیاری برای جایگزینی فضای نام سراسری و محلی دارند. اگر تنها یک فضای نام مشخص شود، از آن برای هر دو استفاده می‌شود.

4.3. استثناها

استثناها راهی برای خارج شدن از جریان عادی کنترل یک بلوک کد، به منظور مدیریت خطاها یا دیگر شرایط استثنایی هستند. یک استثنا در نقطه‌ای که خطا شناسایی می‌شود پرتاب می‌شود؛ ممکن است این استثنا توسط بلوک کد پیرامونی یا هر بلوک کدی که به‌طور مستقیم یا غیرمستقیم بلوک کد محل وقوع خطا را فراخوانی کرده است، مدیریت شود.

مفسر پایتون هنگامی که یک خطای ران‌تایم (مانند تقسیم بر صفر) را تشخیص می‌دهد، استثنایی را پرتاب می‌کند. یک برنامه پایتون نیز می‌تواند به‌صورت صریح استثنایی را با دستور raise پرتاب کند. هندلرهای استثنا با دستور try ... except مشخص می‌شوند. از بند finally چنین دستوری می‌توان برای مشخص کردن کد پاک‌سازی استفاده کرد که به استثنا رسیدگی نمی‌کند، اما اجرا می‌شود، چه استثنایی در کد پیشین رخ داده باشد و چه رخ نداده باشد.

پایتون از مدل «پایان‌دهی» (termination) برای مدیریت خطا استفاده می‌کند: یک هندلر استثنا می‌تواند دریابد چه اتفاقی رخ داده است و اجرا را در سطح بیرونی ادامه دهد، اما نمی‌تواند علت خطا را برطرف کند و عملیات ناموفق را دوباره اجرا کند (مگر با ورود دوباره به قطعه‌کد مشکل‌ساز از ابتدا).

هنگامی که به یک استثنا به هیچ وجه رسیدگی نشود، مفسر اجرای برنامه را خاتمه می‌دهد، یا به حلقه‌ی اصلی تعاملی خود بازمی‌گردد. در هر صورت، یک ردگیری پشته را چاپ می‌کند، مگر زمانی که استثنا SystemExit باشد.

استثناها با نمونه‌های کلاس شناسایی می‌شوند. بند except بر اساس کلاس نمونه انتخاب می‌شود: این بند باید به کلاس نمونه یا به یک کلاس پایه غیرمجازی آن ارجاع دهد. نمونه می‌تواند توسط هندلر دریافت شود و می‌تواند حاوی اطلاعات بیشتری درباره وضعیت استثنایی باشد.

توجه

پیام‌های استثنا بخشی از API پایتون نیستند. محتوای آن‌ها ممکن است از یک نسخه پایتون به نسخه بعدی بدون هشدار تغییر کند و کدی که تحت نسخه‌های متعددی از مفسر اجرا خواهد شد، نباید به آن‌ها اتکا کند.

همچنین توضیح دستور try را در بخش دستور try و توضیح دستور raise را در بخش پرتاب ببینید.

4.4. کامپوننت‌های ران‌تایم

4.4.1. مدل محاسباتی عمومی

مدل اجرای پایتون در خلأ عمل نمی‌کند. این مدل روی یک ماشین میزبان و از طریق محیط ران‌تایم آن میزبان، از جمله سیستم‌عامل آن (OS) در صورت وجود، اجرا می‌شود. هنگامی که یک برنامه اجرا می‌شود، لایه‌های مفهومی چگونگی اجرای آن روی میزبان چیزی شبیه به این هستند:

ماشین میزبان
فرایند (منابع سراسری)
نخ (کد ماشین را اجرا می‌کند)

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

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

فرایندها از یکدیگر جدا و مستقل هستند. (همین موضوع برای میزبان‌ها نیز صادق است.) میزبان، علاوه بر هماهنگی میان فرایندها، دسترسی فرایند به منابع اختصاص‌یافته‌ی آن را مدیریت می‌کند.

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

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

نکته‌ی اساسی درباره‌ی نخ‌ها این است که هر کدام به‌طور مستقل اجرا می‌شود، همزمان با سایر نخ‌ها. این هم‌زمانی ممکن است فقط به‌صورت مفهومی («همزمان») یا به‌صورت فیزیکی («موازی») باشد. در هر صورت، نخ‌ها در عمل با آهنگی غیرهمگام اجرا می‌شوند.

توجه

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

هزینه‌ی این نیازمندی گسترده و بدون ساختار، معاوضه‌ای است که برای گونه‌ای از همروندی خام که نخ‌ها فراهم می‌کنند، پرداخت می‌شود. جایگزین انضباط مورد نیاز، معمولاً به معنای مواجهه با اشکال‌های غیرقطعی و خرابی داده‌ها است.

4.4.2. مدل ران‌تایم پایتون

همان لایه‌های مفهومی برای هر برنامه پایتون صدق می‌کنند، با برخی لایه‌های داده اضافی مختص پایتون:

ماشین میزبان
فرایند (منابع سراسری)
ران‌تایم سراسری پایتون (وضعیت)
مفسر پایتون (وضعیت)
نخ (بایت‌کد پایتون و «C-API» را اجرا می‌کند)
وضعیت نخ پایتون

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

توجه

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

توجه

مفسر اولیه معمولاً مفسر «main» نامیده می‌شود. برخی پیاده‌سازی‌های پایتون، مانند CPython، نقش‌های ویژه‌ای به مفسر اصلی اختصاص می‌دهند.

به‌همین ترتیب، نخ میزبانی که ران‌تایم در آن مقداردهی اولیه شده است، به‌عنوان نخ «اصلی» شناخته می‌شود. ممکن است این نخ با نخ اولیه‌ی فرایند متفاوت باشد، اگرچه این دو اغلب یکسان هستند. در برخی موارد، «نخ اصلی» ممکن است حتی خاص‌تر باشد و به وضعیت اولیه‌ی نخ اشاره کند. یک ران‌تایم پایتون ممکن است مسئولیت‌های مشخصی را به نخ اصلی محول کند، مانند مدیریت سیگنال‌ها.

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

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

در مقابل، یک «مفسر» از نظر مفهومی همان چیزی است که معمولاً به‌عنوان «ران‌تایم پایتون» (تمام‌امکانات) در نظر می‌گیریم. هنگامی که کد ماشینی در حال اجرا در یک نخ میزبان با ران‌تایم پایتون تعامل می‌کند، در زمینه‌ی یک مفسر مشخص، پایتون را فراخوانی می‌کند.

توجه

اصطلاح «مفسر» در اینجا همان «مفسر بایت‌کد» نیست؛ چیزی که به‌طور معمول در نخ‌ها اجرا می‌شود و کد پایتونِ کامپایل‌شده را اجرا می‌کند.

در یک دنیای ایده‌آل، «ران‌تایم پایتون» به چیزی اشاره می‌کرد که امروزه آن را «مفسر» می‌نامیم. با این حال، حداقل از زمان معرفی آن در سال ۱۹۹۷، آن را «مفسر» نامیده‌اند (CPython:a027efa5b).

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

یک پیاده‌سازی پایتون ممکن است از استفاده‌ی همزمان از چندین مفسر در یک فرایند پشتیبانی کند. آن‌ها مستقل و از یکدیگر جدا هستند. برای مثال، هر مفسر sys.modules خاص خود را دارد.

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

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

توجه

اصطلاح «نخ پایتون» گاهی می‌تواند به وضعیت نخ اشاره کند، اما معمولاً به نخی گفته می‌شود که با استفاده از ماژول threading ایجاد شده است.

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

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

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

هنگامی که برنامه‌ای در حال اجرا است، می‌توان نخ‌های جدید پایتون را با استفاده از ماژول threading ایجاد کرد (در پلتفرم‌ها و پیاده‌سازی‌های پایتون که از نخ‌ها پشتیبانی می‌کنند). می‌توان فرآیندهای اضافی را با استفاده از ماژول‌های os، subprocess و multiprocessing ایجاد کرد. می‌توان مفسرها را با استفاده از ماژول interpreters ایجاد کرد و به کار برد. می‌توان هم‌روال‌ها (ناهمگام) را با استفاده از asyncio در هر مفسر اجرا کرد، معمولاً فقط در یک نخ (اغلب نخ اصلی).

پانویس‌ها