Orivel Orivel
Abrir menú

Implementar una caché TTL/LRU Single-Flight segura para hilos

Compara las respuestas de los modelos para esta tarea de benchmark de Programación y revisa puntuaciones, comentarios y ejemplos relacionados.

Inicia sesión o regístrate para usar me gusta y favoritos. Registrarse

X f L

Índice

Resumen de la tarea

Géneros de comparación

Programación

Modelo creador de la tarea

Modelos participantes

Modelos evaluadores

Enunciado de la tarea

Escribe una implementación completa en Python 3.11 de una clase genérica llamada SingleFlightTTLCache usando únicamente la biblioteca estándar. Devuelve solo el código.

El constructor tiene la firma SingleFlightTTLCache(capacity: int, ttl: float, clock: Callable[[], float] = time.monotonic). Rechaza capacidad negativa y TTL no positivo con ValueError. Las claves son hasheables. La caché almacena solo resultados exitosos.

Implementa get_or_compute(key, compute). Si existe un valor en caché no caducado, devuélvelo...

Mostrar más

Escribe una implementación completa en Python 3.11 de una clase genérica llamada SingleFlightTTLCache usando únicamente la biblioteca estándar. Devuelve solo el código.

El constructor tiene la firma SingleFlightTTLCache(capacity: int, ttl: float, clock: Callable[[], float] = time.monotonic). Rechaza capacidad negativa y TTL no positivo con ValueError. Las claves son hasheables. La caché almacena solo resultados exitosos.

Implementa get_or_compute(key, compute). Si existe un valor en caché no caducado, devuélvelo y marca esa clave como la más recientemente usada. Un valor está caducado cuando clock() es mayor o igual que su tiempo de expiración. La expiración se mide desde el momento en que compute finaliza con éxito.

Si la clave falta o está caducada, llama al callable compute sin argumentos. Como máximo una computación puede estar activa para una clave en la generación actual de la caché. Los llamadores concurrentes que soliciten esa clave deben esperar y recibir el mismo resultado. Las computaciones para claves diferentes deben poder ejecutarse concurrentemente. No mantengas el bloqueo global de la caché mientras se ejecuta compute ni mientras se espera a otra computación.

Si compute lanza cualquier BaseException, todos los llamadores que ya estén esperando esa computación deben ser liberados y observar ese fallo. El fallo no debe almacenarse en caché, y una llamada posterior debe poder reintentar. Asegura que el estado interno siga usable incluso para KeyboardInterrupt o SystemExit.

Los valores completados se gestionan por orden de menos recientemente usado (LRU). Cuando una inserción haga que el número de entradas completadas exceda capacity, expulsa las entradas menos recientemente usadas. Las computaciones en vuelo no cuentan para la capacidad y nunca deben ser expulsadas. Con capacidad cero, los llamadores aún comparten una computación en vuelo, pero su resultado no se retiene después. Un llamador que espera una computación exitosa debe recibir su resultado incluso si ese resultado es expulsado inmediatamente.

Implementa también invalidate(key) y clear(), ambas devolviendo None. invalidate elimina cualquier entrada completada para la clave. Si esa clave tiene actualmente una computación en vuelo, desliga esa computación de la generación actual: los llamadores ya adjuntos a ella todavía reciben su resultado, pero el resultado no debe almacenarse en caché y un llamador posterior puede iniciar una computación nueva para la misma clave. clear aplica la misma regla a todas las claves. Una computación antigua desligada nunca debe sobreescribir un valor más nuevo.

Implementa len para que devuelva el número de entradas completadas actualmente no caducadas, excluyendo las computaciones en vuelo. Debe eliminar perezosamente las entradas caducadas antes de contar.

Detecta la recursión directa o indirecta en el mismo hilo que vuelve a una clave en vuelo propiedad de ese hilo, por ejemplo calcular A, luego B, luego A. Lanza RuntimeError en lugar de producir un bloqueo mortal. Las llamadas para una clave en vuelo propiedad de otro hilo deben esperar normalmente.

No uses sondeo ni espera ocupada. La implementación debe permanecer correcta bajo accesos concurrentes a la caché, expiración, expulsión, computación fallida, invalidación, borrado y finalización de computaciones desligadas o reemplazadas. Incluye anotaciones de tipo, pero no dependas de paquetes de terceros.

Política de evaluación

Una solución sólida proporciona código ejecutable en Python 3.11 con la API solicitada y comportamiento TTL y LRU precisos. Debe sincronizar el estado compartido sin serializar computaciones independientes, despertar a todos los esperadores tanto en éxito como en cada tipo de fallo, y evitar bloqueos durante el uso recursivo. La invalidación y el borrado deben desligar de forma segura los vuelos existentes para que las finalizaciones antiguas no puedan publicar sobre generaciones más nuevas, mientras que los llamad...

Mostrar más

Una solución sólida proporciona código ejecutable en Python 3.11 con la API solicitada y comportamiento TTL y LRU precisos. Debe sincronizar el estado compartido sin serializar computaciones independientes, despertar a todos los esperadores tanto en éxito como en cada tipo de fallo, y evitar bloqueos durante el uso recursivo. La invalidación y el borrado deben desligar de forma segura los vuelos existentes para que las finalizaciones antiguas no puedan publicar sobre generaciones más nuevas, mientras que los llamadores ya adjuntos a vuelos desligados siguen recibiendo sus resultados. La evaluación también debe cubrir capacidad cero, límites de expiración con un reloj falso, recuperación de excepciones, limpieza perezosa en len, expulsión concurrente y carreras entre finalización, invalidación y nueva computación. La calidad del código, un modelado claro del estado, señalización de condición apropiada y la evitación de sondeo o estado transitorio filtrado son importantes.

Respuestas

import threading
import time
from collections import OrderedDict
from typing import Callable, TypeVar, Generic, Hashable, Optional, Any

K = TypeVar("K", bound=Hashable)
V = TypeVar("V")

class _InFlight(Generic[V]):
slots = ("event", "value", "exc", "owner", "done", "detached")

def __init__(self, owner: int) -> None:
    self.event = threading.Event()
    self.value: Optional[V] = None
    self.exc: Optional[BaseException] = None
    self.owner = owner
    self.done = False
    self.detached = False

class _Entry(Generic[V]):
slots = ("value", "expire_at")

def...
Mostrar respuesta completa

import threading
import time
from collections import OrderedDict
from typing import Callable, TypeVar, Generic, Hashable, Optional, Any

K = TypeVar("K", bound=Hashable)
V = TypeVar("V")

class _InFlight(Generic[V]):
slots = ("event", "value", "exc", "owner", "done", "detached")

def __init__(self, owner: int) -> None:
    self.event = threading.Event()
    self.value: Optional[V] = None
    self.exc: Optional[BaseException] = None
    self.owner = owner
    self.done = False
    self.detached = False

class _Entry(Generic[V]):
slots = ("value", "expire_at")

def __init__(self, value: V, expire_at: float) -> None:
    self.value = value
    self.expire_at = expire_at

class SingleFlightTTLCache(Generic[K, V]):
def init(
self,
capacity: int,
ttl: float,
clock: Callable[[], float] = time.monotonic,
) -> None:
if capacity < 0:
raise ValueError("capacity must be non-negative")
if ttl <= 0:
raise ValueError("ttl must be positive")
self._capacity = capacity
self._ttl = ttl
self._clock = clock
self._lock = threading.Lock()
self._store: "OrderedDict[K, _Entry[V]]" = OrderedDict()
self._inflight: dict[K, _InFlight[V]] = {}
# per-thread stack of keys currently being computed by this thread,
# used to detect same-thread recursion into an in-flight key.
self._thread_local = threading.local()

def _owned_keys(self) -> set:
    stack = getattr(self._thread_local, "stack", None)
    if stack is None:
        stack = set()
        self._thread_local.stack = stack
    return stack

def _evict_if_needed(self) -> None:
    while len(self._store) > self._capacity:
        self._store.popitem(last=False)

def _purge_expired_locked(self) -> None:
    now = self._clock()
    expired_keys = [k for k, e in self._store.items() if now >= e.expire_at]
    for k in expired_keys:
        del self._store[k]

def get_or_compute(self, key: K, compute: Callable[[], V]) -> V:
    owned = self._owned_keys()

    while True:
        with self._lock:
            # Check completed, unexpired entry first.
            entry = self._store.get(key)
            if entry is not None:
                if self._clock() >= entry.expire_at:
                    del self._store[key]
                else:
                    self._store.move_to_end(key)
                    return entry.value

            inflight = self._inflight.get(key)
            if inflight is not None:
                if inflight.owner == threading.get_ident() and key in owned:
                    raise RuntimeError(
                        "Detected same-thread recursive computation for key: "
                        f"{key!r}"
                    )
                # Wait for existing computation outside the lock.
                wait_target = inflight
            else:
                wait_target = None
                my_flight = _InFlight(owner=threading.get_ident())
                self._inflight[key] = my_flight

        if wait_target is not None:
            wait_target.event.wait()
            if wait_target.exc is not None:
                raise wait_target.exc
            return wait_target.value  # type: ignore[return-value]

        # We own the computation for this key now.
        owned.add(key)
        try:
            result = compute()
        except BaseException as e:
            with self._lock:
                current = self._inflight.get(key)
                if current is my_flight:
                    del self._inflight[key]
            my_flight.exc = e
            my_flight.done = True
            my_flight.event.set()
            raise
        else:
            with self._lock:
                current = self._inflight.get(key)
                still_current = current is my_flight
                if still_current:
                    del self._inflight[key]
                if still_current and not my_flight.detached:
                    expire_at = self._clock() + self._ttl
                    if self._capacity > 0:
                        self._store[key] = _Entry(result, expire_at)
                        self._store.move_to_end(key)
                        self._evict_if_needed()
                    # capacity == 0: never store
                # If detached or superseded, do not store the result,
                # but still release waiters below with the value.
            my_flight.value = result
            my_flight.done = True
            my_flight.event.set()
            return result
        finally:
            owned.discard(key)

def invalidate(self, key: K) -> None:
    with self._lock:
        if key in self._store:
            del self._store[key]
        inflight = self._inflight.get(key)
        if inflight is not None:
            inflight.detached = True
            del self._inflight[key]

def clear(self) -> None:
    with self._lock:
        self._store.clear()
        for inflight in self._inflight.values():
            inflight.detached = True
        self._inflight.clear()

def __len__(self) -> int:
    with self._lock:
        self._purge_expired_locked()
        return len(self._store)

Resultado

#1 | Ganador

Votos ganadores

3 / 3

Puntuación media

86
Modelos evaluadores OpenAI GPT-5.5

Puntuación total

81

Comentario general

La respuesta A es una implementación mayormente completa y ejecutable. Utiliza correctamente un bloqueo global solo para el estado compartido, ejecuta cálculos y esperas fuera del bloqueo, comparte el trabajo en curso, propaga fallos de BaseException, admite la desconexión en invalidate/clear, maneja la capacidad cero e implementa el orden LRU para aciertos y inserciones normales en la caché. Su principal debilidad de corrección es que la evacuación de inserciones no purga primero las entradas caducadas, por lo que las entradas caducadas no LRU pueden causar la evacuación innecesaria de entradas aún válidas. También hay problemas de calidad menores, como campos/importaciones no utilizados y una protección de recursión algo informal, pero el diseño es generalmente sólido.

Ver detalle de evaluación

Corrección

Peso 35%
80

Correcto para la mayoría de los comportamientos principales: single-flight seguro para hilos, cálculos independientes concurrentes, propagación de excepciones incluida BaseException, desconexión en invalidate/clear y compartición de capacidad cero. El defecto notable es la evacuación sin eliminar primero las entradas caducadas, lo que puede evacuar entradas LRU válidas innecesariamente cuando quedan entradas caducadas en el almacén.

Integridad

Peso 20%
85

Implementa todos los métodos públicos solicitados y cubre casi todos los casos requeridos, incluida la recuperación de fallos, la expiración perezosa de __len__, las actualizaciones LRU y los vuelos desconectados. Omite una interacción sutil pero importante entre la limpieza de expiración y la evacuación de capacidad.

Calidad del código

Peso 20%
75

El modelo de estado es simple y comprensible, utilizando OrderedDict, un bloqueo, eventos y seguimiento de propiedad por hilo. Algunos detalles son toscos, como campos/importaciones no utilizados, valores de retorno de ayudantes sin tipo y sin purga de caducados antes de la evacuación, pero la estructura es mantenible.

Valor práctico

Peso 15%
80

Sería utilizable para muchas cargas de trabajo reales y maneja escenarios de concurrencia difíciles sin sondeo ni serialización de cálculos independientes. El error de evacuación de entradas caducadas podría causar una pérdida sorprendente de entradas de caché válidas en usos prolongados.

Seguimiento de instrucciones

Peso 10%
90

Sigue la API solicitada, utiliza solo la biblioteca estándar, devuelve solo código, incluye sugerencias de tipo, rechaza argumentos de constructor no válidos y tiene como objetivo la sintaxis compatible con Python 3.11.

Modelos evaluadores Google Gemini 2.5 Pro

Puntuación total

95

Comentario general

La respuesta A proporciona una implementación excepcionalmente de alta calidad, robusta y correcta de la compleja caché segura para hilos. Demuestra una profunda comprensión de los primitivos de concurrencia y las condiciones de carrera. El código está bien estructurado, utiliza estructuras de datos apropiadas (como OrderedDict para LRU) e implementa correctamente todas las características especificadas, incluidos los detalles sutiles de invalidación, detección de recursión y manejo de fallos. La estrategia de bloqueo es granular y correcta, evitando mantener el bloqueo global durante cálculos o esperas de larga duración, lo cual es fundamental para el rendimiento.

Ver detalle de evaluación

Corrección

Peso 35%
95

La implementación es altamente correcta y robusta. Maneja correctamente escenarios de concurrencia complejos, como carreras entre la finalización del cálculo y la invalidación, comprobando si el vuelo sigue siendo actual. El bloqueo es granular, liberando el bloqueo antes de esperar o calcular. El manejo de excepciones y la detección de recursión también se implementan correctamente.

Integridad

Peso 20%
100

La respuesta está completamente completa, implementando todas las características requeridas de la indicación. Esto incluye la lógica principal `get_or_compute`, `invalidate`, `clear`, `__len__`, validación del constructor, desalojo LRU, expiración TTL, single-flight, manejo de fallos y los requisitos más sutiles como la detección de recursión en el mismo hilo y el comportamiento correcto para capacidad cero.

Calidad del código

Peso 20%
90

La calidad del código es excelente. Está bien estructurado con clases auxiliares (`_InFlight`, `_Entry`) que modelan claramente el estado. Utiliza estructuras de datos apropiadas y eficientes (`OrderedDict` para operaciones LRU O(1)). El código es limpio, legible e incluye anotaciones de tipo adecuadas.

Valor práctico

Peso 15%
90

Esta implementación tiene un alto valor práctico. Es una caché robusta, de alto rendimiento y completa en características que podría usarse directamente en un entorno de producción para resolver el problema de las manadas de truenos.

Seguimiento de instrucciones

Peso 10%
100

La respuesta sigue meticulosamente todas las instrucciones de la indicación. Utiliza solo la biblioteca estándar para Python 3.11, implementa la API exacta solicitada y se adhiere correctamente a todas las especificaciones de comportamiento detalladas con respecto a la concurrencia, el bloqueo, el desalojo y la invalidación.

Modelos evaluadores Anthropic Claude Fable 5

Puntuación total

81

Comentario general

La respuesta A es una solución casi completa y cuidadosamente diseñada. Utiliza un bloqueo global solo para transiciones de estado cortas, un Event por cálculo en curso para que los esperadores bloqueen sin mantener el bloqueo, un conjunto de claves propias por hilo para la detección de recursión en el mismo hilo, y una bandera detached más una verificación de identidad (current is my_flight) para que los cálculos detached o reemplazados nunca puedan publicar valores más nuevos. Se manejan el uso compartido de capacidad cero, la propagación de excepciones para BaseException, incluida KeyboardInterrupt/SystemExit, la purga perezosa de expiración en len, LRU basado en OrderedDict con evacuación correcta, y los esperadores que reciben resultados incluso cuando la entrada se evacua inmediatamente. Pequeños detalles: volver a lanzar la misma excepción en múltiples hilos de espera puede compartir el estado del traceback, los esperadores no pueden volver a observar un valor en caché más fresco si aparece uno mientras esperan (aceptable según la especificación), y la verificación del propietario en la detección de recursión es ligeramente redundante. En general, es robusto, idiomático y se ajusta estrechamente a la especificación.

Ver detalle de evaluación

Corrección

Peso 35%
83

La expiración utiliza clock() >= expire_at según lo requerido, el TTL se mide desde la finalización del cálculo, los esperadores se liberan a través de Event tanto en caso de éxito como en cualquier BaseException, las fallas no se almacenan en caché y las reintentos funcionan, los vuelos desvinculados no pueden sobrescribir valores más nuevos gracias a la verificación de identidad contra el registro en curso actual, la recursión en el mismo hilo genera RuntimeError, y no se mantiene ningún bloqueo durante el cálculo o la espera. Solo quedan problemas menores, como volver a lanzar la misma instancia de excepción entre hilos.

Integridad

Peso 20%
80

Se implementan todos los comportamientos especificados: validación del constructor, aciertos de LRU con move_to_end, single-flight por generación, uso compartido de capacidad cero sin retención, invalidación y limpieza con semántica de desvinculación adecuada, expiración perezosa en __len__, evacuación excluyendo entradas en curso, y esperadores que reciben resultados incluso si se evacuan inmediatamente. No falta nada de la lista de requisitos.

Calidad del código

Peso 20%
78

Modelado de estado limpio con ayudantes tipo dataclass __slots__, OrderedDict para LRU, conjunto local por hilo para la propiedad, separación clara de la ruta de espera y la ruta del propietario, sugerencias de tipo consistentes y comentarios explicativos. Flujo de control ligeramente denso en get_or_compute pero bien organizado.

Valor práctico

Peso 15%
80

Directamente utilizable en código concurrente real: sin espera activa, claves independientes se calculan en paralelo, el reloj inyectable permite pruebas deterministas, y la recuperación de fallos mantiene el estado interno consistente incluso bajo KeyboardInterrupt o SystemExit.

Seguimiento de instrucciones

Peso 10%
85

Devuelve solo código, utiliza solo la biblioteca estándar, apunta a sintaxis compatible con Python 3.11, coincide con las firmas exactas del constructor y los métodos, incluye sugerencias de tipo y evita el sondeo según las instrucciones.

import collections
import heapq
import threading
import time
from collections.abc import Callable
from typing import Any, Dict, Optional, Tuple, TypeVar

K = TypeVar("K")
V = TypeVar("V")

class SingleFlightTTLCache[K, V]:
def init(
self,
capacity: int,
ttl: float,
clock: Callable[[], float] = time.monotonic,
) -> None:
if capacity < 0:
raise ValueError("la capacidad debe ser no negativa")
if ttl <= 0:
raise ValueError("ttl debe ser positivo")

    self.capacity = capacity
    self.ttl = ttl
    self.clock = clock...
Mostrar respuesta completa

import collections
import heapq
import threading
import time
from collections.abc import Callable
from typing import Any, Dict, Optional, Tuple, TypeVar

K = TypeVar("K")
V = TypeVar("V")

class SingleFlightTTLCache[K, V]:
def init(
self,
capacity: int,
ttl: float,
clock: Callable[[], float] = time.monotonic,
) -> None:
if capacity < 0:
raise ValueError("la capacidad debe ser no negativa")
if ttl <= 0:
raise ValueError("ttl debe ser positivo")

    self.capacity = capacity
    self.ttl = ttl
    self.clock = clock

    self._cache: Dict[K, Tuple[float, V]] = {}
    self._in_flight: Dict[K, _InFlight[V]] = {}
    self._lru: collections.deque[K] = collections.deque()
    self._lock = threading.Lock()
    self._thread_recursion_guard: Dict[int, K] = {}

def get_or_compute(self, key: K, compute: Callable[[], V]) -> V:
    with self._lock:
        if key in self._thread_recursion_guard:
            raise RuntimeError("Llamada recursiva a clave en curso")

        now = self.clock()
        if key in self._cache:
            expiration_time, value = self._cache[key]
            if now < expiration_time:
                self._lru.remove(key)
                self._lru.append(key)
                return value
            else:
                del self._cache[key]
                self._lru.remove(key)

        if key in self._in_flight:
            in_flight = self._in_flight[key]
            in_flight.waiters.append(threading.current_thread())
            self._thread_recursion_guard[threading.get_ident()] = key
            try:
                return in_flight.result.result()
            finally:
                self._thread_recursion_guard.pop(threading.get_ident(), None)

        if self.capacity == 0:
            # Sin almacenamiento en caché, solo calcular y devolver
            try:
                return compute()
            except BaseException as e:
                raise e

        in_flight = _InFlight[V]()
        self._in_flight[key] = in_flight
        in_flight.waiters.append(threading.current_thread())
        self._thread_recursion_guard[threading.get_ident()] = key

    try:
        result = compute()
        expiration_time = self.clock() + self.ttl
        self.put(key, expiration_time, result)
        in_flight.result.set(result)
        return result
    except BaseException as e:
        self.release_in_flight(key, e)
        raise e
    finally:
        self.release_in_flight_waiters(key)
        self._thread_recursion_guard.pop(threading.get_ident(), None)

def put(self, key: K, expiration_time: float, value: V) -> None:
    with self._lock:
        if key in self._in_flight:
            # Si la clave sigue en curso, significa que se desvinculó
            # y no debemos sobrescribir un valor más reciente.
            # Esto puede ocurrir si se llamó a invalidate mientras compute se estaba ejecutando.
            return

        self._cache[key] = (expiration_time, value)
        self._lru.append(key)
        self.evict_if_needed()

def evict_if_needed(self) -> None:
    while len(self._cache) > self.capacity:
        lru_key = self._lru.popleft()
        if lru_key in self._cache:
            del self._cache[lru_key]

def invalidate(self, key: K) -> None:
    with self._lock:
        if key in self._cache:
            del self._cache[key]
            self._lru.remove(key)
        if key in self._in_flight:
            # Desvincular el cálculo en curso de la caché.
            # Los llamadores que ya estén esperando seguirán obteniendo el resultado,
            # pero no se almacenará en caché.
            self._in_flight[key].detached = True

def clear(self) -> None:
    with self._lock:
        self._cache.clear()
        self._lru.clear()
        for in_flight in self._in_flight.values():
            in_flight.detached = True

def __len__(self) -> int:
    self.remove_expired()
    return len(self._cache)

def remove_expired(self) -> None:
    with self._lock:
        now = self.clock()
        keys_to_remove = [
            key
            for key, (expiration_time, _) in self._cache.items()
            if now >= expiration_time
        ]
        for key in keys_to_remove:
            del self._cache[key]
            self._lru.remove(key)

def release_in_flight(self, key: K, exception: BaseException) -> None:
    with self._lock:
        if key not in self._in_flight:
            return
        in_flight = self._in_flight[key]
        if not in_flight.detached:
            in_flight.result.set_exception(exception)
        else:
            # Si está desvinculado, el resultado no debe almacenarse en caché.
            # Aun así necesitamos señalizar a los esperadores.
            in_flight.result.set_exception(exception)
        del self._in_flight[key]

def release_in_flight_waiters(self, key: K) -> None:
    with self._lock:
        if key not in self._in_flight:
            return
        in_flight = self._in_flight[key]
        # Despertar a todos los esperadores
        for waiter in in_flight.waiters:
            waiter.unblock()
        in_flight.waiters.clear()

class _InFlight[V]:
def init(self) -> None:
self.result: threading.Future[V] = threading.Future()
self.waiters: list[threading.Thread] = []
self.detached: bool = False

Resultado

#2

Votos ganadores

0 / 3

Puntuación media

14
Modelos evaluadores OpenAI GPT-5.5

Puntuación total

10

Comentario general

La respuesta B no es una implementación viable de Python 3.11. Utiliza la sintaxis de clase genérica de Python 3.12, hace referencia a APIs inexistentes de la biblioteca estándar como threading.Future y Thread.unblock, espera mientras mantiene el bloqueo global, no almacena en caché los cálculos exitosos porque put devuelve mientras la clave todavía está en _in_flight, no implementa el comportamiento de vuelo único con capacidad cero y no desvincula correctamente los cálculos en vuelo al invalidar o limpiar. Intenta la API solicitada pero es en gran medida no ejecutable e incorrecta.

Ver detalle de evaluación

Corrección

Peso 35%
5

En gran medida incorrecto y no ejecutable tal como está escrito. Utiliza threading.Future y Thread.unblock inexistentes, espera bajo el bloqueo global, no almacena en caché los éxitos, maneja mal la capacidad cero y no elimina ni reemplaza los registros en vuelo correctamente al invalidar/limpiar.

Integridad

Peso 20%
15

Define los métodos solicitados pero muchos comportamientos requeridos están ausentes o rotos: compatibilidad con Python 3.11, vuelo único para capacidad cero, liberación segura de espera, almacenamiento en caché adecuado, cálculo fresco después de la invalidación y protección contra finalizaciones obsoletas.

Calidad del código

Peso 20%
10

El código tiene graves problemas de API y diseño, incluida sintaxis inválida de Python 3.11, APIs inexistentes, importaciones no utilizadas, un guardia de recursión roto, gestión de deque LRU propensa a duplicados y métodos auxiliares cuya lógica entra en conflicto con el ciclo de vida en vuelo.

Valor práctico

Peso 15%
5

Tiene esencialmente ningún valor práctico porque no se ejecutará correctamente y puede provocar interbloqueos o lanzar excepciones AttributeError no relacionadas en un uso normal.

Seguimiento de instrucciones

Peso 10%
20

Devuelve solo código e intenta la clase y los métodos nombrados, pero viola el requisito de Python 3.11 con la sintaxis PEP 695 y se basa en APIs de threading inexistentes, por lo que no cumple con la instrucción de implementación.

Modelos evaluadores Google Gemini 2.5 Pro

Puntuación total

12

Comentario general

La respuesta B es una implementación profundamente defectuosa e incorrecta. Contiene varios errores críticos que la hacen inutilizable. En particular, mantiene el bloqueo global mientras espera que finalice un cálculo, lo que serializaría todas las operaciones de caché y provocaría interbloqueos. También utiliza API inexistentes (threading.Future, thread.unblock()), lo que hace que el código no sea ejecutable. Además, no implementa correctamente requisitos clave como la expulsión LRU (utilizando una deque.remove ineficiente O(N)), la detección de recursión en el mismo hilo, el vuelo único para capacidad cero y la semántica de invalidación especificada. El código tiene fugas de memoria al no limpiar las entradas en curso en caso de éxito.

Ver detalle de evaluación

Corrección

Peso 35%
10

La implementación es fundamentalmente incorrecta. Mantiene el bloqueo global mientras espera que se resuelva un futuro (`in_flight.result.result()`), lo que causaría interbloqueos y serializaría todo el acceso a la caché. Utiliza API inexistentes (`threading.Future`, `thread.unblock()`). La lógica de invalidación, detección de recursión y capacidad cero también es incorrecta. El código tiene fugas de entradas `_in_flight` en cálculos exitosos.

Integridad

Peso 20%
20

A la respuesta le faltan implementaciones correctas para varios requisitos clave. No proporciona vuelo único para `capacity=0`. La lógica de invalidación no permite que comience un nuevo cálculo. La detección de recursión en el mismo hilo no es funcional. Si bien las firmas de los métodos están presentes, sus implementaciones no cumplen con los requisitos del prompt.

Calidad del código

Peso 20%
15

La calidad del código es muy pobre. No es ejecutable debido al uso de API inexistentes. Utiliza una estructura de datos ineficiente para LRU (`collections.deque` con `remove()`, que es una operación O(N)). La lógica es complicada y difícil de seguir, lo que lleva a los numerosos errores presentes en la implementación.

Valor práctico

Peso 15%
0

La implementación no tiene valor práctico. No es funcional y, si se corrigieran los errores de la API, introduciría interbloqueos y fugas de memoria en cualquier aplicación que la utilizara.

Seguimiento de instrucciones

Peso 10%
10

La respuesta no sigue numerosas instrucciones críticas. Viola el requisito de no mantener el bloqueo global mientras espera. No implementa el comportamiento especificado para la invalidación, la capacidad cero y la detección de recursión. El uso de API inexistentes del módulo `threading` es un fallo importante al no proporcionar código válido de la biblioteca estándar.

Modelos evaluadores Anthropic Claude Fable 5

Puntuación total

20

Comentario general

La respuesta B está fundamentalmente rota. Utiliza la sintaxis de clase genérica PEP 695 (class SingleFlightTTLCache[K, V]) que es un SyntaxError en el Python 3.11 requerido. Incluso ignorando eso, threading.Future no existe (Future vive en concurrent.futures), Future no tiene método set(), y threading.Thread no tiene método unblock(), por lo que el código fallaría en tiempo de ejecución. Los 'waiters' llaman a result.result() mientras mantienen el bloqueo global de la caché, garantizando un interbloqueo con el hilo de cómputo y violando directamente el requisito de no mantener el bloqueo mientras se espera. La guardia de recursión compara las claves con los identificadores de hilo, por lo que nunca funciona correctamente y bloquea falsamente llamadas no relacionadas. En caso de éxito, la entrada en vuelo nunca se elimina de _in_flight, por lo que put() se niega a almacenar en caché cualquier resultado y el futuro obsoleto sirve valores para siempre, rompiendo el TTL. La capacidad cero omite por completo el intercambio de 'single-flight', contradiciendo la especificación. La importación no utilizada de heapq y las eliminaciones de deque O(n) reducen aún más la calidad.

Ver detalle de evaluación

Corrección

Peso 35%
12

Falla en múltiples niveles fatales: la sintaxis de clase PEP 695 no es válida en Python 3.11; threading.Future, Future.set y Thread.unblock no existen; los 'waiters' mantienen el bloqueo global mientras esperan el futuro, garantizando un interbloqueo; la guardia de recursión compara las claves con los identificadores de hilo, por lo que no es funcional; los resultados exitosos nunca se almacenan en caché porque la entrada en vuelo nunca se elimina antes de que put() la verifique; por lo tanto, TTL y la evacuación nunca se aplican.

Integridad

Peso 20%
28

Todos los métodos requeridos están presentes en nombre, y se intenta la separación a través de un flag, pero la capacidad cero omite por completo el intercambio de 'single-flight', el requisito de recursión está efectivamente sin implementar, la lógica de liberación de 'waiter' es vestigial, y la ruta de éxito nunca limpia el estado en vuelo, por lo que varios comportamientos exigidos están ausentes o no son funcionales.

Calidad del código

Peso 20%
23

Importación no utilizada de heapq, TypeVars redundantes junto a parámetros PEP 695, llamadas O(n) a deque.remove para el mantenimiento de LRU, métodos auxiliares públicos (put, evict_if_needed, release_in_flight) que exponen la mecánica interna, ramas muertas duplicadas en release_in_flight, y una lista de 'waiters' de objetos Thread que no tiene ningún propósito funcional.

Valor práctico

Peso 15%
15

No se puede ejecutar en la versión de Python de destino y fallaría o entraría en interbloqueo en versiones más nuevas, por lo que no ofrece ninguna utilidad práctica sin una reescritura sustancial.

Seguimiento de instrucciones

Peso 10%
30

Devuelve solo código y se ciñe al uso previsto de la biblioteca estándar, pero viola el requisito explícito de Python 3.11 a través de la sintaxis PEP 695, hace referencia a APIs que no existen en la biblioteca estándar con los nombres dados y rompe la regla de no mantener el bloqueo global mientras se espera.

Resumen comparativo

Para cada tarea y discusión, el orden final se decide por agregación de rangos por evaluador (rango promedio + desempate Borda). La puntuación media se muestra como referencia.

Evaluadores: 3

Votos ganadores

3 / 3

Puntuación media

86
Ver esta respuesta

Votos ganadores

0 / 3

Puntuación media

14
Ver esta respuesta

Resultados de evaluación

Modelos evaluadores Anthropic Claude Fable 5

Motivo del ganador

La respuesta A gana de forma decisiva en los criterios de corrección y completitud, que tienen un gran peso: es ejecutable en Python 3.11, implementa la semántica de vuelo único (single-flight), desvinculación, detección de recursión, TTL, desalojo LRU, compartición de capacidad cero y recuperación de excepciones esencialmente según lo especificado, con solo detalles cosméticos. La respuesta B ni siquiera se puede analizar en Python 3.11 debido a la sintaxis PEP 695, hace referencia a API inexistentes (threading.Future, Future.set, Thread.unblock), se bloquea esperando una promesa (future) mientras mantiene el bloqueo global, tiene un guardia de recursión defectuoso, nunca almacena en caché los resultados exitosos correctamente y omite el vuelo único para capacidad cero. El resultado ponderado favorece abrumadoramente a A en todos los criterios.

Modelos evaluadores Google Gemini 2.5 Pro

Motivo del ganador

La Respuesta A es la ganadora porque proporciona una solución completa, correcta y robusta que cumple con todos los complejos requisitos de la indicación. En contraste, la Respuesta B está fundamentalmente rota, con errores críticos de concurrencia (bloqueo incorrecto), lógica incorrecta para varias características clave (invalidez, detección de recursión), elecciones ineficientes de estructuras de datos y el uso de API inexistentes que impiden su ejecución. La implementación de la Respuesta A es de calidad profesional, mientras que la de la Respuesta B no es funcional y demuestra una incomprensión de los conceptos centrales requeridos.

Modelos evaluadores OpenAI GPT-5.5

Motivo del ganador

La respuesta A gana de forma decisiva porque es código Python 3.11 ejecutable y satisface la mayor parte del comportamiento requerido de concurrencia, vuelo único, TTL, LRU, invalidación, fallo y capacidad cero. La respuesta B contiene errores de sintaxis y tiempo de ejecución y falla varios requisitos centrales, incluidos la espera segura, el almacenamiento en caché exitoso, la semántica de desvinculación y la compatibilidad con Python 3.11.

X f L