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

۱. انتخابِ پردازشگر را از اعتبارسنجیِ داده جدا کنیم

انتخابِ جایگاه و بررسیِ محتوا دو کارِ جدا هستند
تصویر مفهومی: انتخابِ جایگاه و بررسیِ محتوا دو کارِ جدا هستند؛ بدون مقیاس عددی و مستقل از نتایج اجرای کد.

نام‌هایی مانند Voltage و Current در این مثال، برچسب خانواده‌های پردازش‌اند. عددِ نگه‌داری‌شده در فیلد value را ولت یا آمپر تفسیر نمی‌کنیم. این تفکیک برای کار مهندسی ضروری است: انتخاب تابعِ مناسب بر اساس کلاس، به‌تنهایی درست‌بودن واحد، منشأ داده یا دامنهٔ فیزیکی آن را ثابت نمی‌کند. اگر چنین تضمینی لازم باشد، قرارداد مستقل و شاهد متناسب می‌خواهد.

تابع تألیفیِ route_samples دو ورودیِ ازپیش‌ساخته می‌گیرد: نام کلاس‌هایی که پردازشگر اختصاصی دارند، و مشخصات نمونه‌ها. هر مشخصه شامل نام کلاس، شمارش صحیح از منفی چهار تا مثبت چهار، و نوع یک آرگومان کمکی است. حداکثر هشت ثبت و هشت نمونه می‌پذیریم. این سقف‌ها سیاست آموزشی خودمان‌اند؛ محدودیت رسمی پایتون یا مشخصات دستگاه نیستند.

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

۲. فقط نوعِ نخستین آرگومان، مسیر را انتخاب می‌کند

حاملِ اصلی مسیر را انتخاب می‌کند و قطعهٔ همراه جدا می‌ماند
تصویر مفهومی: حاملِ اصلی مسیر را انتخاب می‌کند و قطعهٔ همراه جدا می‌ماند؛ بدون مقیاس عددی و مستقل از نتایج اجرای کد.

مطابق تعریف رسمی singledispatch، گزینش بر پایهٔ نوعِ نخستین آرگومان انجام می‌شود. تابع پایه، مسیر پیش‌فرضِ object را فراهم می‌کند و ثبت صریحِ register(cls, handler) پردازشگر کلاس مشخص را اضافه می‌کند. در برنامهٔ ما هر فراخوانی، ثبت‌خانهٔ تازه‌ای می‌سازد؛ بنابراین تنظیمات یک درخواست قرار نیست به درخواست بعدی نشت کند.

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

دو ابزار بازبینی هم مفیدند: dispatch(cls) تابعِ انتخابی را برای یک کلاس نشان می‌دهد، و registry نمای فقط‌خواندنیِ ثبت‌هاست. پرسیدنِ مسیر، با اجرای پردازشگر برابر نیست. کد تعدادِ ورود به پردازشگرها را ثبت می‌کند تا این تفاوت دیده شود؛ نه اینکه از مشاهدهٔ ثبت‌خانه، موفقیتِ بدنهٔ دلخواه را نتیجه بگیریم.

۳. تبارِ کلاس و ترتیبِ پایه‌ها را دقیق بخوانیم

دو چیدمانِ متفاوت از تکیه‌گاه‌های یک خانواده
تصویر مفهومی: دو چیدمانِ متفاوت از تکیه‌گاه‌های یک خانواده؛ بدون مقیاس عددی و مستقل از نتایج اجرای کد.

وقتی نوع دقیق ثبت نشده باشد، جست‌وجوی پردازشگر در تبار کلاس ادامه پیدا می‌کند. اگر Sample و Voltage ثبت شده باشند، نمونهٔ FilteredVoltage به پردازشگر نزدیک‌ترِ Voltage می‌رسد. افزودن ثبت اختصاصی برای خودِ زیرکلاس، همان مسیر نزدیک‌تر را جایگزین می‌کند. ترتیبِ زمانیِ ثبتِ دو کلاس، جای ترتیبِ تبارِ اسمی را نمی‌گیرد.

دو کلاس تشخیصی، پایه‌های یکسان را با ترتیب مخالف اعلام می‌کنند: یکی ابتدا Voltage و سپس Tagged، و دیگری برعکس. وقتی هر دو پایه ثبت شده‌اند، این دو نمونه به دو پردازشگر متفاوت می‌رسند. این شاهد دربارهٔ ترتیبِ ارث‌بری است؛ معنایش قابل‌جایگزینی‌بودنِ دو کمیت فیزیکی نیست. برای نمونهٔ اصلی، انتظار دستی چنین است: زیرکلاس پالایش‌شده و کلاسِ نخست به مسیر ولتاژ، کلاسِ معکوس به مسیر برچسب، و جریان به مسیر عمومی نمونه می‌روند.

کلاس Lookalike نیز فیلد هم‌نام و مقدار مشابه دارد، اما از این خانواده ارث نمی‌برد؛ بدون ثبت مستقل به مسیر پیش‌فرض می‌رسد. این پرسش با مرزِ Protocol در زمان اجرا فرق دارد: حضورِ ساختاریِ عضو، انتخابِ پردازشگر در یک ثبت‌خانهٔ اسمی نیست. کلاس‌های پایهٔ انتزاعیِ مجازی و تبارهای سفارشی را وارد این آزمایش نمی‌کنیم.

۴. کدِ کامل را با جدولِ تبارِ مستقل بسنجیم

مسیرِ انتخاب و مرجعِ مستقل در دو بخشِ جداگانه
تصویر مفهومی: مسیرِ انتخاب و مرجعِ مستقل در دو بخشِ جداگانه؛ بدون مقیاس عددی و مستقل از نتایج اجرای کد.

فایل را با نام singledispatch_mro_resolution.py ذخیره کنید. کد فقط به کتابخانهٔ استاندارد نیاز دارد و خروجی کاملِ JSON تولید می‌کند. مسیر واقعی از خودِ functools.singledispatch استفاده می‌کند؛ انتظار مستقل، یک جدول صریحِ تبار برای همین هشت کلاس است. این جدول نه تابعِ گزینش را صدا می‌زند، نه تبارِ واقعیِ شیء را می‌خواند و نه ادعا دارد الگوریتم عمومیِ ترتیب کلاس‌ها را دوباره پیاده کرده است.

"""Fixed nominal telemetry fixtures; dispatch chooses a handler, not valid units."""
from dataclasses import dataclass, FrozenInstanceError
from functools import singledispatch
from itertools import combinations
import json
import platform
import sys


CLASS_CODES = ('sample', 'voltage', 'current', 'tagged', 'filtered_voltage',
               'voltage_first', 'tagged_first', 'lookalike')
MAX_ITEMS = 8
LOW_COUNT, HIGH_COUNT = -4, 4


class Sample:
    __slots__ = ('value',)

    def __init__(self, value):
        self.value = value


class Voltage(Sample):
    __slots__ = ()


class Current(Sample):
    __slots__ = ()


class Tagged(Sample):
    __slots__ = ()


class FilteredVoltage(Voltage):
    __slots__ = ()


class VoltageFirst(Voltage, Tagged):
    __slots__ = ()


class TaggedFirst(Tagged, Voltage):
    __slots__ = ()


class Lookalike:
    __slots__ = ('value',)

    def __init__(self, value):
        self.value = value


CLASSES = dict(zip(CLASS_CODES, (Sample, Voltage, Current, Tagged,
                               FilteredVoltage, VoltageFirst, TaggedFirst,
                               Lookalike)))


@dataclass(frozen=True)
class RouteReport:
    # Rows: input class code, chosen handler tag, raw count, auxiliary type.
    rows: tuple
    callback_trace: tuple
    registry: tuple
    lookups: tuple


def _validate_request(registrations, sample_specs):
    if type(registrations) is not tuple or type(sample_specs) is not tuple:
        raise TypeError('registrations and sample_specs must be exact tuples')
    if len(registrations) > MAX_ITEMS or len(sample_specs) > MAX_ITEMS:
        raise ValueError('authored request size exceeds eight')
    seen = set()
    for code in registrations:
        if type(code) is not str:
            raise TypeError('registration code must be an exact str')
        if code not in CLASSES:
            raise ValueError('unknown registration code')
        if code in seen:
            raise ValueError('duplicate registration code')
        seen.add(code)
    for spec in sample_specs:
        if type(spec) is not tuple:
            raise TypeError('sample specification must be an exact tuple')
        if len(spec) != 3:
            raise ValueError('sample specification needs three fields')
        code, count, auxiliary_kind = spec
        if type(code) is not str:
            raise TypeError('sample class code must be an exact str')
        if code not in CLASSES:
            raise ValueError('unknown sample class code')
        if type(count) is not int or type(auxiliary_kind) is not int:
            raise TypeError('count and auxiliary kind must be exact ints')
        if not LOW_COUNT <= count <= HIGH_COUNT:
            raise ValueError('raw count outside the authored interval')
        if auxiliary_kind not in (0, 1):
            raise ValueError('auxiliary kind must be zero or one')


def _make_dispatcher(registrations):
    trace = []

    def route_object(sample, auxiliary):
        kind = type(auxiliary).__name__
        trace.append(('object', sample.value, kind))
        return 'object', sample.value, kind

    generic = singledispatch(route_object)

    def make_handler(tag):
        def handler(sample, auxiliary):
            kind = type(auxiliary).__name__
            trace.append((tag, sample.value, kind))
            return tag, sample.value, kind
        handler.__name__ = 'route_' + tag
        return handler

    for code in registrations:
        generic.register(CLASSES[code], make_handler(code))
    return generic, trace


def _construct_sample(code, count):
    return CLASSES[code](count)


def route_samples(registrations, sample_specs):
    """Only eight declared ordinary classes and already materialized fixtures."""
    # No dispatcher, sample or formatter is constructed before whole admission.
    _validate_request(registrations, sample_specs)
    generic, trace = _make_dispatcher(registrations)
    rows = []
    for code, count, auxiliary_kind in sample_specs:
        sample = _construct_sample(code, count)
        auxiliary = 'diagnostic' if auxiliary_kind == 0 else 7
        tag, preserved, kind = generic(sample, auxiliary)
        rows.append((code, tag, preserved, kind))
    names = {object: 'object', **{cls: code for code, cls in CLASSES.items()}}
    registry = tuple(sorted(names[cls] for cls in generic.registry))
    lookups = tuple((code, generic.dispatch(CLASSES[code]).__name__[6:])
                    for code in CLASS_CODES)
    return RouteReport(tuple(rows), tuple(trace), registry, lookups)


# Independently declared nominal precedence, confined to this fixed universe.
# No actual __mro__, functools or production dispatch helper in expected path.
DECLARED_ANCESTRY = {
    'sample': ('sample',),
    'voltage': ('voltage', 'sample'),
    'current': ('current', 'sample'),
    'tagged': ('tagged', 'sample'),
    'filtered_voltage': ('filtered_voltage', 'voltage', 'sample'),
    'voltage_first': ('voltage_first', 'voltage', 'tagged', 'sample'),
    'tagged_first': ('tagged_first', 'tagged', 'voltage', 'sample'),
    'lookalike': ('lookalike',),
}


def declared_reference(registrations, sample_specs):
    registered = frozenset(registrations)
    chosen = {}
    for code, ancestors in DECLARED_ANCESTRY.items():
        chosen[code] = next((name for name in ancestors if name in registered),
                            'object')
    rows = tuple((code, chosen[code], count, ('str', 'int')[kind])
                 for code, count, kind in sample_specs)
    return RouteReport(rows, tuple(row[1:] for row in rows),
                       tuple(sorted(('object',) + registrations)),
                       tuple((code, chosen[code]) for code in CLASS_CODES))


def rejects(call, error_type):
    try:
        call()
    except error_type:
        return True
    return False


def invalid_admission_observations():
    class StrChild(str):
        pass

    class IntChild(int):
        pass

    class TupleChild(tuple):
        pass

    consumed = []

    def lazy_specs():
        consumed.append(True)
        yield ('sample', 0, 0)

    lazy = lazy_specs()
    valid = ('voltage', 0, 0)
    cases = [
        ([], (), TypeError), ((), [], TypeError),
        (TupleChild(), (), TypeError), ((), TupleChild(), TypeError),
        ((True,), (), TypeError), ((StrChild('sample'),), (), TypeError),
        (('unknown',), (), ValueError),
        (('sample', 'sample'), (), ValueError),
        (CLASS_CODES + ('sample',), (), ValueError),
        ((), (valid,) * 9, ValueError),
        ((), (list(valid),), TypeError),
        ((), (TupleChild(valid),), TypeError),
        ((), (('sample', 0),), ValueError),
        ((), (('sample', 0, 0, 0),), ValueError),
        ((), ((False, 0, 0),), TypeError),
        ((), ((StrChild('sample'), 0, 0),), TypeError),
        ((), (('unknown', 0, 0),), ValueError),
        ((), (('sample', True, 0),), TypeError),
        ((), (('sample', IntChild(0), 0),), TypeError),
        ((), (('sample', 0.0, 0),), TypeError),
        ((), (('sample', -5, 0),), ValueError),
        ((), (('sample', 5, 0),), ValueError),
        ((), (('sample', 0, False),), TypeError),
        ((), (('sample', 0, IntChild(0)),), TypeError),
        ((), (('sample', 0, -1),), ValueError),
        ((), (('sample', 0, 2),), ValueError),
        ((), (valid, ('sample', 0, 2)), ValueError),
        (('voltage', 'missing'), (valid,), ValueError),
        ((), lazy, TypeError),
    ]
    old_dispatcher, old_constructor = _make_dispatcher, _construct_sample
    events = []

    def forbidden(*args):
        events.append('construction')
        raise AssertionError('whole invalid request reached construction')

    try:
        globals()['_make_dispatcher'] = forbidden
        globals()['_construct_sample'] = forbidden
        passed = all(rejects(lambda r=r, s=s: route_samples(r, s), error)
                     for r, s, error in cases)
    finally:
        globals()['_make_dispatcher'] = old_dispatcher
        globals()['_construct_sample'] = old_constructor
        lazy.close()
    return {'passed': passed and not events and not consumed,
            'invalid_profiles': len(cases), 'construction_events': len(events),
            'generator_consumed': bool(consumed)}


def run_example():
    registrations = ('sample', 'voltage', 'tagged')
    specs = (('filtered_voltage', -4, 0), ('voltage_first', 0, 1),
             ('tagged_first', 4, 0), ('current', 2, 1),
             ('lookalike', -1, 0))
    manual = route_samples(registrations, specs)
    manual_rows = (
        ('filtered_voltage', 'voltage', -4, 'str'),
        ('voltage_first', 'voltage', 0, 'int'),
        ('tagged_first', 'tagged', 4, 'str'),
        ('current', 'sample', 2, 'int'),
        ('lookalike', 'object', -1, 'str'))
    all_specs = tuple((code, 0, 0) for code in CLASS_CODES)
    fallback = route_samples((), all_specs)
    inherited = route_samples(('sample', 'voltage'),
                              (('filtered_voltage', 1, 0),))
    exact = route_samples(('sample', 'voltage', 'filtered_voltage'),
                          (('filtered_voltage', 1, 0),))
    opposite = route_samples(('voltage', 'tagged'),
                             (('voltage_first', 0, 0),
                              ('tagged_first', 0, 0)))
    forward = route_samples(registrations, all_specs)
    reverse = route_samples(tuple(reversed(registrations)), all_specs)
    auxiliary = route_samples(('voltage',),
                              (('voltage', 3, 0), ('voltage', 3, 1)))
    lookup_generic, lookup_trace = _make_dispatcher(('sample', 'voltage'))
    lookup_names = tuple(lookup_generic.dispatch(CLASSES[code]).__name__
                         for code in ('current', 'filtered_voltage'))
    registry_before = tuple(lookup_generic.registry)
    readonly = rejects(lambda: lookup_generic.registry.__setitem__(
        Lookalike, lambda sample, auxiliary: None), AttributeError)
    # Assignment syntax exercises mappingproxy's mutation refusal directly.
    try:
        lookup_generic.registry[Lookalike] = lambda sample, auxiliary: None
        direct_assignment_refused = False
    except TypeError:
        direct_assignment_refused = True
    repeats = route_samples(('sample',),
                            (('current', 2, 1), ('sample', -2, 0),
                             ('current', 2, 1)))
    family_ok, profiles, queries = True, 0, 0
    for size in range(len(CLASS_CODES) + 1):
        for chosen in combinations(CLASS_CODES, size):
            profiles += 1
            for count in (-4, 0, 4):
                for kind in (0, 1):
                    batch = tuple((code, count, kind) for code in CLASS_CODES)
                    actual = route_samples(chosen, batch)
                    expected = declared_reference(chosen, batch)
                    family_ok = family_ok and actual == expected
                    queries += len(batch)
    admission = invalid_admission_observations()
    boundary = route_samples(CLASS_CODES,
                             tuple((code, (-4, 4)[i % 2], i % 2)
                                   for i, code in enumerate(CLASS_CODES)))
    before = route_samples(('sample',), all_specs)
    changed = route_samples(('tagged',), all_specs)
    again = route_samples(('sample',), all_specs)
    frozen = rejects(lambda: setattr(before, 'rows', ()), FrozenInstanceError)
    checks = {
        'manual_mixed_routes_match_five_literal_expectations': (
            manual.rows == manual_rows
            and manual.callback_trace == tuple(row[1:] for row in manual_rows)),
        'no_registered_ancestor_uses_object_fallback_for_all_eight_classes': (
            all(row[1] == 'object' for row in fallback.rows)),
        'nearest_registered_nominal_ancestor_beats_more_distant_sample': (
            inherited.rows == (('filtered_voltage', 'voltage', 1, 'str'),)),
        'exact_registered_subclass_overrides_inherited_handler': (
            exact.rows == (('filtered_voltage', 'filtered_voltage', 1, 'str'),)),
        'opposite_declared_base_orders_choose_different_registered_handlers': (
            tuple(row[1] for row in opposite.rows) == ('voltage', 'tagged')),
        'registration_order_is_not_nominal_ancestor_precedence': forward == reverse,
        'second_argument_type_changes_payload_metadata_but_not_selected_handler': (
            auxiliary.rows == (('voltage', 'voltage', 3, 'str'),
                               ('voltage', 'voltage', 3, 'int'))),
        'same_value_attribute_on_unrelated_class_does_not_imply_registration': (
            manual.rows[-1][1] == 'object' and Lookalike(-1).value == -1),
        'dispatch_lookup_and_readonly_registry_do_not_run_formatters': (
            lookup_names == ('route_sample', 'route_voltage') and not lookup_trace
            and readonly and direct_assignment_refused
            and tuple(lookup_generic.registry) == registry_before),
        'input_order_duplicates_counts_and_callback_order_are_preserved': (
            repeats.rows == (('current', 'sample', 2, 'int'),
                             ('sample', 'sample', -2, 'str'),
                             ('current', 'sample', 2, 'int'))
            and repeats.callback_trace == tuple(row[1:] for row in repeats.rows)),
        'all_fixed_universe_registration_subsets_match_declared_table_ONE_family': (
            family_ok and profiles == 256 and queries == 12288),
        'whole_late_invalid_requests_rejected_before_construction_or_callbacks': (
            admission['passed']),
        'authored_caps_inclusive_boundaries_and_empty_batch_are_supported': (
            len(boundary.rows) == 8 and len(boundary.registry) == 9
            and all(row[0] == row[1] for row in boundary.rows)
            and route_samples((), ()).rows == ()),
        'reports_are_frozen_and_interleaved_registrations_do_not_leak': (
            before == again and before is not again and before != changed
            and frozen and type(before.rows) is tuple
            and all(type(row) is tuple for row in before.rows)),
    }
    assert len(checks) == 14 and all(type(v) is bool and v for v in checks.values()), checks
    return {
        'python': platform.python_version(),
        'implementation': platform.python_implementation(),
        'mechanism': 'actual_functools_singledispatch_explicit_nominal_registration',
        'scope': 'eight authored ordinary classes; raw counts, not calibrated units',
        'class_codes': CLASS_CODES,
        'manual_rows': manual.rows,
        'manual_callback_trace': manual.callback_trace,
        'manual_registry': manual.registry,
        'manual_dispatch_lookups': manual.lookups,
        'lookup_observation': {'callbacks': len(lookup_trace),
                               'registry_assignment_refused': direct_assignment_refused},
        'independent_reference': 'literal declared ancestry table; no actual mro or dispatch',
        'case_counts_not_extra_groups': {'registration_subsets': profiles,
                                         'sample_queries': queries,
                                         'finite_family_groups': 1},
        'invalid_admission': admission,
        'authored_limits': {'registrations': MAX_ITEMS, 'samples': MAX_ITEMS,
                            'raw_count_closed_interval': [LOW_COUNT, HIGH_COUNT]},
        'ABC_or_static_typechecker_or_device_or_network_executed': False,
        'checks': checks,
    }


if __name__ == '__main__':
    sys.stdout.reconfigure(encoding='utf-8', newline='\n')
    print(json.dumps(run_example(), ensure_ascii=True, indent=2, allow_nan=False))

چهارده گروهِ نام‌دار، شاهدهای دستی، مسیر پیش‌فرض، ثبت دقیق، ترتیب مخالفِ پایه‌ها، نوع آرگومان دوم، بازبینی بدون فراخوانی، حفظ ترتیب و تکرار ورودی، اعتبارسنجی کامل، سقف‌ها و استقلال درخواست‌ها را در بر می‌گیرند. خانوادهٔ متناهی، تمام ۲۵۶ زیرمجموعهٔ ثبت را برای هشت کلاس، سه شمارش و دو نوع آرگومان کمکی بررسی می‌کند: ۱۲۲۸۸ پرس‌وجو، همگی در یک گروه. تعدادِ حالت‌ها را به تعدادِ گروه‌های آزمون تبدیل نمی‌کنیم.

{
  "python": "3.14.5",
  "implementation": "CPython",
  "mechanism": "actual_functools_singledispatch_explicit_nominal_registration",
  "scope": "eight authored ordinary classes; raw counts, not calibrated units",
  "class_codes": [
    "sample",
    "voltage",
    "current",
    "tagged",
    "filtered_voltage",
    "voltage_first",
    "tagged_first",
    "lookalike"
  ],
  "manual_rows": [
    [
      "filtered_voltage",
      "voltage",
      -4,
      "str"
    ],
    [
      "voltage_first",
      "voltage",
      0,
      "int"
    ],
    [
      "tagged_first",
      "tagged",
      4,
      "str"
    ],
    [
      "current",
      "sample",
      2,
      "int"
    ],
    [
      "lookalike",
      "object",
      -1,
      "str"
    ]
  ],
  "manual_callback_trace": [
    [
      "voltage",
      -4,
      "str"
    ],
    [
      "voltage",
      0,
      "int"
    ],
    [
      "tagged",
      4,
      "str"
    ],
    [
      "sample",
      2,
      "int"
    ],
    [
      "object",
      -1,
      "str"
    ]
  ],
  "manual_registry": [
    "object",
    "sample",
    "tagged",
    "voltage"
  ],
  "manual_dispatch_lookups": [
    [
      "sample",
      "sample"
    ],
    [
      "voltage",
      "voltage"
    ],
    [
      "current",
      "sample"
    ],
    [
      "tagged",
      "tagged"
    ],
    [
      "filtered_voltage",
      "voltage"
    ],
    [
      "voltage_first",
      "voltage"
    ],
    [
      "tagged_first",
      "tagged"
    ],
    [
      "lookalike",
      "object"
    ]
  ],
  "lookup_observation": {
    "callbacks": 0,
    "registry_assignment_refused": true
  },
  "independent_reference": "literal declared ancestry table; no actual mro or dispatch",
  "case_counts_not_extra_groups": {
    "registration_subsets": 256,
    "sample_queries": 12288,
    "finite_family_groups": 1
  },
  "invalid_admission": {
    "passed": true,
    "invalid_profiles": 29,
    "construction_events": 0,
    "generator_consumed": false
  },
  "authored_limits": {
    "registrations": 8,
    "samples": 8,
    "raw_count_closed_interval": [
      -4,
      4
    ]
  },
  "ABC_or_static_typechecker_or_device_or_network_executed": false,
  "checks": {
    "manual_mixed_routes_match_five_literal_expectations": true,
    "no_registered_ancestor_uses_object_fallback_for_all_eight_classes": true,
    "nearest_registered_nominal_ancestor_beats_more_distant_sample": true,
    "exact_registered_subclass_overrides_inherited_handler": true,
    "opposite_declared_base_orders_choose_different_registered_handlers": true,
    "registration_order_is_not_nominal_ancestor_precedence": true,
    "second_argument_type_changes_payload_metadata_but_not_selected_handler": true,
    "same_value_attribute_on_unrelated_class_does_not_imply_registration": true,
    "dispatch_lookup_and_readonly_registry_do_not_run_formatters": true,
    "input_order_duplicates_counts_and_callback_order_are_preserved": true,
    "all_fixed_universe_registration_subsets_match_declared_table_ONE_family": true,
    "whole_late_invalid_requests_rejected_before_construction_or_callbacks": true,
    "authored_caps_inclusive_boundaries_and_empty_batch_are_supported": true,
    "reports_are_frozen_and_interleaved_registrations_do_not_leak": true
  }
}

در جدول دستی، هر ردیف هم نام کلاس ورودی و هم برچسب پردازشگر را دارد؛ شمارش خام نیز بدون تبدیل حفظ می‌شود. ردّ فراخوانی‌ها باید با همان ترتیب ردیف‌ها سازگار باشد. موفقیت این بررسی‌ها شاهدِ همین جهان کوچک است، نه اثباتِ همهٔ رفتارهای پایتون. مستندات خوانده‌شده نسخهٔ ۳٫۱۴٫۹ و نسخهٔ اجرای این برنامه CPython ۳٫۱۴٫۵ است؛ زمان خواندنِ منبع و نسخهٔ اجرا را یکی نمی‌گیریم.

۵. چهار پرسش دربارهٔ ثبتِ نوع و مرزِ نتیجه

دامنهٔ مشخصِ حامل‌ها و حالت‌های بیرون از قرارداد
تصویر مفهومی: دامنهٔ مشخصِ حامل‌ها و حالت‌های بیرون از قرارداد؛ بدون مقیاس عددی و مستقل از نتایج اجرای کد.

پرسش اول: چرا زیرکلاس پالایش‌شده، مسیر عمومی نمونه را انتخاب نمی‌کند؟ پاسخ: در ثبتِ شاهد، پردازشگرِ نزدیک‌تر برای Voltage وجود دارد. اگر ثبتِ آن حذف شود، جست‌وجو به Sample می‌رسد؛ اگر خودِ زیرکلاس ثبت شود، پردازشگر دقیق مقدم می‌شود. پس پیش از تغییر کد، نوعِ واقعی و ثبت‌های موجود را جدا بررسی می‌کنیم.

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

پرسش سوم: چرا شیءِ هم‌شکل به مسیر پیش‌فرض می‌رود؟ پاسخ: داشتن فیلدِ value، نسبتِ ارث‌بری یا ثبت صریح ایجاد نمی‌کند. می‌توان برای همان کلاس، پردازشگرِ جدا ثبت کرد؛ اما این تصمیم همچنان گواهِ اعتبارِ مقدار، واحد یا رفتارِ تمامِ روش‌های آن نیست. معیارِ انتخاب را با معیارِ پذیرش داده جایگزین نمی‌کنیم.

پرسش چهارم: آیا جدول مستقل، ابهامِ کلاس‌های انتزاعی را هم حل کرده است؟ پاسخ: خیر؛ جدول فقط تبارهای عادیِ اعلام‌شدهٔ این آزمایش را پوشش می‌دهد. PEP443 بحثِ گسترده‌تری دربارهٔ کلاس‌های انتزاعی دارد. همچنین ردِ ثبت مبتنی بر راهنمای نوع در متنِ مهِ ۲۰۱۳، با افزوده‌شدنِ پشتیبانیِ آن در نسخهٔ ۳٫۷ که مرجع جاری ذکر می‌کند، دو مقطع تاریخی‌اند. این دو سندِ رسمی از یک تولیدکننده‌اند؛ برای ادعاهای فراتر از این دامنه، شاهد مستقلِ متناسب لازم است.

منابع و مطالعهٔ بیشتر

بررسی منابع: ۳۰ مهر ۱۴۰۵. تحلیل‌ها و پیشنهادهای اجرایی، جمع‌بندی نویسنده‌اند.