Aprende Python — cada pregunta, cada respuesta
Las 125 preguntas en 5 niveles con ejemplos de código, respuestas aceptadas y explicaciones detalladas. Gratis, sin registro, totalmente indexado.
Una referencia completa de Python: desde tipos básicos y control de flujo hasta dataclasses, decoradores y contextlib, asyncio, FastAPI y Pydantic. Cada entrada muestra el código ejecutable, la respuesta correcta y la regla que la justifica.
python-basic · Básico · 25
pb1¿Qué imprime este código?
x = 7
y = 2
print(x // y)- A3.5
- B3✓ Respuesta correcta
- C4
- D3.0
Por qué: El operador // realiza división entera, devolviendo 3 con dos enteros.
pb2¿Qué identificador NO es válido en Python?
- A_value
- Bmy_var2
- C2nd_place✓ Respuesta correcta
- DCamelCase
Por qué: Los identificadores no pueden empezar con un dígito; deben empezar con letra o guion bajo.
pb3¿Qué tipo se imprime?
print(type(3 / 2))- A<class 'int'>
- B<class 'float'>✓ Respuesta correcta
- C<class 'number'>
- D<class 'double'>
Por qué: El operador / siempre devuelve un float en Python 3, incluso con enteros.
pb4¿Qué imprime este código?
s = " Hello "
print(s.strip().upper())- A HELLO
- BHELLO✓ Respuesta correcta
- CHello
- D Hello
Por qué: .strip() elimina los espacios alrededor, luego .upper() convierte a mayúsculas.
pb5¿Qué imprime este slice?
s = "python"
print(s[1:4])- Apyt
- Byth✓ Respuesta correcta
- Cytho
- Dthon
Por qué: El slice s[1:4] toma los índices 1, 2, 3 — el final es exclusivo.
pb6¿Qué imprime este f-string?
name = "Ada"
print(f"Hi, {name}!")- AHi, {name}!
- BHi, Ada!✓ Respuesta correcta
- CHi, "Ada"!
- DSyntaxError
Por qué: Los f-strings interpolan el valor de las expresiones entre llaves.
pb7¿Qué imprime este código?
xs = [1, 2]
xs.append([3, 4])
print(len(xs))- A2
- B3✓ Respuesta correcta
- C4
- DTypeError
Por qué: append añade un solo elemento (la lista interna) — la longitud es 3.
pb8¿Qué imprime este código?
xs = [1, 2, 3]
xs.extend([4, 5])
print(xs)- A[1, 2, 3, [4, 5]]
- B[1, 2, 3, 4, 5]✓ Respuesta correcta
- C[4, 5, 1, 2, 3]
- D[1, 2, 3]
Por qué: extend itera el argumento y añade cada elemento individualmente a la lista.
pb9¿Qué ocurre al ejecutar el código?
t = (1, 2, 3)
t[0] = 9
print(t)- AImprime (9, 2, 3)
- BImprime (1, 2, 3)
- CTypeError✓ Respuesta correcta
- DIndexError
Por qué: Las tuplas son inmutables; asignar a un índice lanza TypeError.
pb10¿Qué imprime este código?
d = {"a": 1, "b": 2}
print(d.get("c", 0))- ANone
- BKeyError
- C0✓ Respuesta correcta
- D1
Por qué: dict.get(clave, defecto) devuelve el valor por defecto si la clave no existe.
pb11¿Qué comprueba el operador in en un dict?
d = {"x": 1}
print("x" in d)- APertenencia de valores
- BPertenencia de claves✓ Respuesta correcta
- CPertenencia de pares
- DSiempre False
Por qué: Para dicts, el operador in comprueba la existencia de una clave, no valores.
pb12¿Qué imprime este código?
s = {1, 2, 2, 3, 3, 3}
print(len(s))- A6
- B3✓ Respuesta correcta
- C2
- D1
Por qué: Los sets solo guardan elementos únicos, los duplicados se eliminan dejando {1, 2, 3}.
pb13¿Qué imprime este código?
vals = [0, "", None, [], "x"]
truthy = [v for v in vals if v]
print(len(truthy))- A5
- B4
- C1✓ Respuesta correcta
- D0
Por qué: 0, "", None y [] son falsy; solo "x" pasa el filtro.
pb14¿Qué imprime este código?
x = 10
if x > 20:
print("A")
elif x > 5:
print("B")
else:
print("C")- AA
- BB✓ Respuesta correcta
- CC
- DBC
Por qué: Solo se ejecuta la primera rama que coincide; 10 > 5 es cierto, imprime "B".
pb15¿Qué imprime este código?
total = 0
for i in range(1, 5):
total += i
print(total)- A15
- B10✓ Respuesta correcta
- C6
- D4
Por qué: range(1, 5) genera 1, 2, 3, 4 (fin exclusivo); la suma es 10.
pb16¿Cuál es la primera línea impresa?
names = ["a", "b", "c"]
for i, n in enumerate(names):
print(i, n)- A1 a
- B0 a✓ Respuesta correcta
- Ca 0
- D0 "a"
Por qué: enumerate produce pares (índice, valor) empezando en 0 por defecto.
pb17¿Qué imprime este código?
a = [1, 2, 3]
b = ["x", "y"]
print(list(zip(a, b)))- A[(1, 'x'), (2, 'y'), (3, None)]
- B[(1, 'x'), (2, 'y')]✓ Respuesta correcta
- C[1, 'x', 2, 'y', 3]
- DError
Por qué: zip se detiene en el iterable más corto, así que el tercer elemento de a se descarta.
pb18¿Qué imprime esta comprensión?
squares = [x*x for x in range(4)]
print(squares)- A[1, 4, 9, 16]
- B[0, 1, 4, 9]✓ Respuesta correcta
- C[0, 1, 2, 3]
- D[1, 2, 4, 8]
Por qué: range(4) genera 0, 1, 2, 3; al elevar al cuadrado da [0, 1, 4, 9].
pb19¿Qué imprime este código?
def greet(name, greeting="Hi"):
return f"{greeting}, {name}"
print(greet("Sam"))- AHi, Sam✓ Respuesta correcta
- BSam, Hi
- CTypeError
- DHi, name
Por qué: Los argumentos por defecto se usan si no se pasan; greeting es "Hi".
pb20¿Qué imprime este código?
def total(*args, **kwargs):
return sum(args) + sum(kwargs.values())
print(total(1, 2, x=3, y=4))- A3
- B7
- C10✓ Respuesta correcta
- DTypeError
Por qué: *args recoge (1, 2) y **kwargs {x:3, y:4}; las sumas dan 3 + 7 = 10.
pb21¿Qué imprime este código?
def f():
pass
print(type(f()).__name__)- ANone
- BNoneType✓ Respuesta correcta
- Cfunction
- Dvoid
Por qué: Una función sin return devuelve None, cuya clase se llama NoneType.
pb22¿Qué imprime este código?
a = [1, 2, 3]
b = [1, 2, 3]
print(a == b, a is b)- ATrue True
- BTrue False✓ Respuesta correcta
- CFalse True
- DFalse False
Por qué: == compara valores (iguales); is comprueba identidad — son objetos distintos.
pb23¿Qué imprime este código?
print(isinstance(True, int))- ATrue✓ Respuesta correcta
- BFalse
- CTypeError
- DNone
Por qué: En Python, bool es subclase de int, por lo que isinstance(True, int) es True.
pb24¿Qué imprime este código?
try:
x = int("abc")
except ValueError:
x = -1
print(x)- A0
- B-1✓ Respuesta correcta
- Cabc
- DValueError
Por qué: int("abc") lanza ValueError, que se captura y x se establece en -1.
pb25¿Qué imprime este código?
import math
print(math.sqrt(16))- A4
- B4.0✓ Respuesta correcta
- C16
- DImportError
Por qué: math.sqrt siempre devuelve un float, por eso sqrt(16) imprime 4.0.
python-medium · Intermedio · 25
pm1¿Qué imprime esto?
class Dog:
species = 'canine'
def __init__(self, name):
self.name = name
a = Dog('Rex')
Dog.species = 'wolf'
print(a.species)- Acanine
- Bwolf✓ Respuesta correcta
- CRex
- DAttributeError
Por qué: species es atributo de clase; cambiarlo afecta a instancias sin sobrescritura propia.
pm2¿Qué imprime esto?
class A:
def hi(self): return 'A'
class B(A):
def hi(self): return 'B' + super().hi()
print(B().hi())- AB
- BA
- CBA✓ Respuesta correcta
- DAB
Por qué: B.hi devuelve "B" + super().hi() (A.hi -> "A"), resultando "BA".
pm3¿Qué imprime esto?
class Temp:
def __init__(self, c):
self._c = c
@property
def f(self):
return self._c * 9/5 + 32
t = Temp(100)
print(t.f)- A212.0✓ Respuesta correcta
- B212
- C<property>
- DTypeError
Por qué: @property permite f sin paréntesis; 100*9/5+32 es 212.0 (división real).
pm4¿Qué imprime esto?
class Math:
@staticmethod
def add(a, b): return a + b
@classmethod
def name(cls): return cls.__name__
print(Math.add(2, 3), Math.name())- A5 Math✓ Respuesta correcta
- B5 cls
- CTypeError
- D23 Math
Por qué: @staticmethod no tiene argumentos implícitos; @classmethod recibe cls ligado a la clase.
pm5¿Qué imprime esto?
def shout(fn):
def wrap(*a, **k):
return fn(*a, **k).upper()
return wrap
@shout
def greet(n): return f'hi {n}'
print(greet('ann'))- AHI ANN✓ Respuesta correcta
- Bhi ann
- CHI ann
- DTypeError
Por qué: El decorador envuelve greet; "hi ann" pasa a mayúsculas como "HI ANN".
pm6¿Cuál es el orden de salida?
class Open:
def __enter__(self):
print('in'); return self
def __exit__(self, *a):
print('out')
with Open():
print('mid')- Ain, mid, out✓ Respuesta correcta
- Bmid, in, out
- Cin, out, mid
- Dout, mid, in
Por qué: Primero __enter__, luego el cuerpo del with, y finalmente __exit__ al salir.
pm7¿Qué imprime esto?
def gen():
yield 1
yield 2
yield 3
g = gen()
print(next(g), next(g))- A1 2✓ Respuesta correcta
- B1 1
- C2 3
- D1 2 3
Por qué: Cada next() avanza el generador al siguiente yield, devolviendo 1 y luego 2.
pm8¿Qué imprime esto?
try:
raise ValueError('a')
except ValueError:
print('v')
else:
print('e')
finally:
print('f')- Av luego f✓ Respuesta correcta
- Bv luego e luego f
- Ce luego f
- Dsolo f
Por qué: else se ejecuta solo sin excepción; finally siempre se ejecuta.
pm9¿Qué imprime esto?
class C:
def __init__(self): self.i = 0
def __iter__(self): return self
def __next__(self):
if self.i >= 2: raise StopIteration
self.i += 1
return self.i
print(list(C()))- A[1, 2]✓ Respuesta correcta
- B[0, 1]
- C[1, 2, 3]
- D[]
Por qué: list() itera hasta StopIteration; se devuelven 1 y 2 antes de que i llegue a 2.
pm10¿Qué imprime esto?
nums = [1, 2, 3, 4, 5]
print([x*x for x in nums if x % 2])- A[1, 9, 25]✓ Respuesta correcta
- B[1, 4, 9, 16, 25]
- C[4, 16]
- D[1, 3, 5]
Por qué: La comprensión conserva los impares y los eleva al cuadrado: 1, 9, 25.
pm11¿Qué imprime esto?
nums = [1, 2, 3, 4]
print(list(filter(lambda x: x > 2, nums)))- A[3, 4]✓ Respuesta correcta
- B[1, 2]
- C[2, 3, 4]
- D[True, True]
Por qué: filter conserva elementos donde la lambda es verdadera; solo 3 y 4 superan 2.
pm12¿Qué imprime esto?
from functools import partial
def power(base, exp): return base ** exp
square = partial(power, exp=2)
print(square(5))- A25✓ Respuesta correcta
- B10
- C32
- DTypeError
Por qué: partial fija exp=2; square(5) calcula 5**2, es decir 25.
pm13¿Qué imprime esto?
from pathlib import Path
p = Path('/tmp/data.txt')
print(p.suffix, p.stem)- A.txt data✓ Respuesta correcta
- Btxt data
- C.txt /tmp/data
- Ddata .txt
Por qué: Path.suffix incluye el punto (".txt"); Path.stem es el nombre sin extensión ("data").
pm14¿Qué imprime esto?
from collections import Counter
print(Counter('mississippi').most_common(2))- A[('i', 4), ('s', 4)]✓ Respuesta correcta
- B[('i', 4), ('m', 1)]
- C[('s', 4), ('i', 4)]
- D[('p', 2), ('m', 1)]
Por qué: "mississippi" tiene cuatro i y cuatro s; most_common(2) devuelve esos dos pares.
pm15¿Cuál es la primera línea impresa?
for i, v in enumerate(['a', 'b', 'c'], start=10):
print(i, v)- A10 a✓ Respuesta correcta
- B0 a
- C1 a
- D10 c
Por qué: enumerate(start=10) empieza en 10, así que el primer par es (10, "a").
pm16¿Qué imprime esto?
def classify(x):
match x:
case 0: return 'zero'
case int(): return 'int'
case _: return 'other'
print(classify(5))- Aint✓ Respuesta correcta
- Bzero
- Cother
- DSyntaxError
Por qué: 5 no es 0 pero coincide con int(), por eso devuelve "int" antes del comodín.
pm17¿Qué imprime esto?
pi = 3.14159
print(f'{pi:.2f}')- A3.14✓ Respuesta correcta
- B3.142
- C3.14159
- D3.1
Por qué: El especificador .2f redondea el flotante a dos decimales.
pm18¿Qué imprime esto?
import copy
a = [[1, 2], [3, 4]]
b = copy.copy(a)
b[0].append(9)
print(a)- A[[1, 2, 9], [3, 4]]✓ Respuesta correcta
- B[[1, 2], [3, 4]]
- C[[1, 2], [3, 4, 9]]
- D[[1, 2, 9], [3, 4, 9]]
Por qué: copy.copy es superficial; las sublistas se comparten, modificar b[0] afecta a[0].
pm19¿Qué imprime esto?
print('a,b,c,d'.split(',', maxsplit=2))- A['a', 'b', 'c,d']✓ Respuesta correcta
- B['a', 'b', 'c', 'd']
- C['a,b', 'c', 'd']
- D['a', 'b,c,d']
Por qué: maxsplit=2 produce tres trozos; el resto "c,d" queda como un solo elemento.
pm20¿Qué imprime esto?
a = {'x': 1, 'y': 2}
b = {'y': 9, 'z': 3}
print(a | b)- A{'x': 1, 'y': 9, 'z': 3}✓ Respuesta correcta
- B{'x': 1, 'y': 2, 'z': 3}
- C{'y': 9, 'z': 3}
- DTypeError
Por qué: El operador | fusiona dicts; ante conflicto gana el derecho, así que y=9.
pm21¿Qué imprime esto?
pairs = [(1, 'b'), (3, 'a'), (2, 'c')]
print(sorted(pairs, key=lambda p: p[1]))- A[(3, 'a'), (1, 'b'), (2, 'c')]✓ Respuesta correcta
- B[(1, 'b'), (2, 'c'), (3, 'a')]
- C[(1, 'b'), (3, 'a'), (2, 'c')]
- D[(2, 'c'), (1, 'b'), (3, 'a')]
Por qué: La clave extrae el segundo elemento de la tupla, ordenando alfabéticamente.
pm22¿Cuál es la segunda línea impresa?
def add(x, items=[]):
items.append(x)
return items
print(add(1))
print(add(2))- A[1, 2]✓ Respuesta correcta
- B[2]
- C[1]
- D[2, 1]
Por qué: Los argumentos por defecto mutables se comparten; la misma lista acumula 1 y luego 2.
pm23¿Qué imprime esto?
from collections import defaultdict
d = defaultdict(int)
for c in 'abca':
d[c] += 1
print(dict(d))- A{'a': 2, 'b': 1, 'c': 1}✓ Respuesta correcta
- B{'a': 1, 'b': 1, 'c': 1}
- CKeyError
- D{'a': 2}
Por qué: defaultdict(int) crea claves faltantes con 0; contar "abca" da a=2, b=1, c=1.
pm24¿Qué imprime esto?
from functools import reduce
print(reduce(lambda a, b: a + b, [1, 2, 3, 4], 10))- A20✓ Respuesta correcta
- B10
- C24
- D14
Por qué: reduce parte de 10 y suma 1+2+3+4=10, en total 20.
pm25¿Qué imprime esto?
from collections import deque
d = deque([1, 2, 3])
d.appendleft(0)
d.append(4)
print(list(d))- A[0, 1, 2, 3, 4]✓ Respuesta correcta
- B[1, 2, 3, 0, 4]
- C[4, 0, 1, 2, 3]
- D[0, 4, 1, 2, 3]
Por qué: appendleft añade 0 al inicio; append añade 4 al final, dando [0, 1, 2, 3, 4].
python-advanced · Avanzado · 25
pa1¿Pasa el type-check y qué imprime?
from typing import Protocol
class Greeter(Protocol):
def hi(self) -> str: ...
class En:
def hi(self) -> str:
return 'hello'
def shout(g: Greeter) -> str:
return g.hi().upper()
print(shout(En()))- ANo, En no hereda de Greeter
- BSí, imprime HELLO✓ Respuesta correcta
- CSí, imprime hello
- DTypeError en runtime
Por qué: Protocol usa tipado estructural: cualquier clase con métodos coincidentes lo cumple sin herencia explícita.
pa2¿Qué pasa con esta dataclass congelada?
from dataclasses import dataclass, field
@dataclass(frozen=True)
class P:
name: str
tags: list[str] = field(default_factory=list)
p = P('a')
p.tags.append('x')
print(p.tags)- AFrozenInstanceError en append
- BImprime []
- CImprime ['x']✓ Respuesta correcta
- DTypeError en construcción
Por qué: frozen=True bloquea reasignación, pero los objetos mutables referenciados pueden mutarse en su lugar.
pa3¿Qué imprime fmt(True)?
from functools import singledispatch
@singledispatch
def fmt(x): return f'obj:{x}'
@fmt.register
def _(x: int): return f'int:{x}'
@fmt.register
def _(x: list): return f'list:{x}'
print(fmt(True))- Aobj:True
- Bint:True✓ Respuesta correcta
- Cbool:True
- DTypeError
Por qué: bool es subclase de int, así que singledispatch resuelve True a la implementación int por MRO.
pa4¿Qué garantiza el GIL de Python?
- AEjecución CPU paralela de hilos
- BSolo un hilo ejecuta bytecode a la vez✓ Respuesta correcta
- CCódigo de usuario thread-safe
- DI/O más rápida que asyncio
Por qué: El GIL serializa la ejecución de bytecode a un hilo, impidiendo paralelismo CPU pero no haciendo el código thread-safe.
pa5¿Qué se imprime?
from itertools import accumulate
import operator
nums = [1, 2, 3, 4]
print(list(accumulate(nums, operator.mul)))- A[1, 2, 6, 24]✓ Respuesta correcta
- B[1, 3, 6, 10]
- C[24]
- D[1, 2, 3, 4]
Por qué: accumulate produce resultados acumulados: 1, 1*2, 1*2*3, 1*2*3*4 con el operador dado.
pa6¿Qué ocurre?
class C:
__slots__ = ('x',)
c = C()
c.x = 1
c.y = 2
print(c.x, c.y)- AImprime 1 2
- BAttributeError en c.y = 2✓ Respuesta correcta
- CTypeError en definición
- DImprime 1 None
Por qué: __slots__ deshabilita el __dict__ de instancia, asignar atributos fuera de slots lanza AttributeError.
pa7¿Es válido y qué imprime?
from typing import TypedDict, NotRequired
class User(TypedDict):
name: str
age: NotRequired[int]
u: User = {'name': 'Ada'}
print(u.get('age', 0))- AError de tipo: falta age
- BVálido, imprime 0✓ Respuesta correcta
- CVálido, imprime None
- DKeyError en runtime
Por qué: NotRequired marca la clave como opcional, omitir age es válido; .get devuelve el predeterminado 0.
pa8¿Por qué usar ExitStack aquí?
from contextlib import ExitStack
def open_all(paths):
with ExitStack() as stack:
files = [stack.enter_context(open(p)) for p in paths]
return [f.read() for f in files]
print(type(open_all([])).__name__)- APara abrir en paralelo
- BPara gestionar un número dinámico de context managers✓ Respuesta correcta
- CPara suprimir IOError
- DReemplaza try/except
Por qué: ExitStack permite entrar en un número desconocido de context managers y garantiza su salida correcta.
pa9¿Qué imprime print?
class Desc:
def __set_name__(self, owner, name):
self.name = '_' + name
def __get__(self, obj, t=None):
return getattr(obj, self.name, 0)
def __set__(self, obj, v):
setattr(obj, self.name, v * 2)
class A:
x = Desc()
a = A(); a.x = 5
print(a.x)- A5
- B10✓ Respuesta correcta
- C0
- DAttributeError
Por qué: El __set__ del descriptor guarda 5*2 en _x; __get__ devuelve 10.
pa10¿Qué se imprime?
from enum import StrEnum, auto
class Color(StrEnum):
RED = auto()
BLUE = auto()
print(Color.RED == 'red', Color.RED.value)- AFalse 1
- BTrue red✓ Respuesta correcta
- CTrue RED
- DFalse red
Por qué: StrEnum.auto() genera el nombre en minúsculas; los miembros StrEnum son iguales a su valor string.
pa11¿Qué produce itertools.groupby?
from itertools import groupby
data = [1, 1, 2, 2, 1, 1]
print([(k, list(g)) for k, g in groupby(data)])- A[(1,[1,1,1,1]),(2,[2,2])]
- B[(1,[1,1]),(2,[2,2]),(1,[1,1])]✓ Respuesta correcta
- C[(1,4),(2,2)]
- D[(1,[1,1,1,1,1,1])]
Por qué: groupby agrupa solo elementos iguales consecutivos; rachas no adyacentes son grupos separados.
pa12¿Qué se imprime?
import weakref
class Node: pass
n = Node()
r = weakref.ref(n)
print(r() is n)
del n
print(r())- ATrue luego None✓ Respuesta correcta
- BTrue luego <Node object>
- CFalse luego None
- DReferenceError
Por qué: Un weakref no mantiene vivo al objeto; tras borrar la referencia fuerte, el weakref devuelve None.
pa13¿Cuál es la salida?
from typing import TypeVar, Generic
T = TypeVar('T')
class Box(Generic[T]):
def __init__(self, x: T) -> None:
self.x = x
def get(self) -> T:
return self.x
b: Box[int] = Box(3)
print(b.get() + 1)- A4✓ Respuesta correcta
- B3
- CTypeError
- DBox[int]
Por qué: Generic[T] habilita tipado paramétrico; en runtime los tipos se borran y 3 + 1 es 4.
pa14¿Qué afirmación sobre pickle vs json es VERDADERA?
- Ajson serializa cualquier objeto Python
- Bpickle es seguro desde fuentes no confiables
- Cpickle puede ejecutar código arbitrario al cargar✓ Respuesta correcta
- Djson preserva tuple vs list
Por qué: pickle.load puede ejecutar código con datos manipulados; nunca cargues datos no confiables. json es seguro.
pa15¿Cuántas veces se imprime "compute"?
from functools import cached_property
class C:
@cached_property
def v(self):
print('compute')
return 42
c = C()
print(c.v); print(c.v)- A0
- B1✓ Respuesta correcta
- C2
- DError: necesita __slots__
Por qué: cached_property calcula una vez por instancia y guarda en __dict__; los accesos siguientes evitan recalcular.
pa16¿Qué imprime?
ba = bytearray(b'hello')
mv = memoryview(ba)
mv[0] = ord('H')
print(ba)- Abytearray(b'hello')
- Bbytearray(b'Hello')✓ Respuesta correcta
- CTypeError: bytes inmutable
- DBufferError
Por qué: memoryview comparte el buffer del bytearray (mutable), escribir a través modifica el original.
pa17¿Qué devuelve re.findall?
import re
text = 'a1 b22 c333'
print(re.findall(r'\d+', text))- A['1', '22', '333']✓ Respuesta correcta
- Biterador de Match
- C['a1', 'b22', 'c333']
- D[1, 22, 333]
Por qué: findall devuelve lista de strings; finditer devuelve iterador de Match.
pa18¿Qué hace assert_never aquí?
from typing import Literal, assert_never
def area(shape: Literal['sq', 'tr'], v: float) -> float:
match shape:
case 'sq': return v * v
case 'tr': return v * v / 2
case _: assert_never(shape)
print(area('sq', 3))- ASiempre lanza en runtime
- BChequeo estático de exhaustividad; error si faltan casos✓ Respuesta correcta
- CSuprime warnings
- DAfirma shape None
Por qué: assert_never le dice al verificador que la rama es inalcanzable; un caso olvidado provoca error de tipo.
pa19¿Mejor opción para paralelismo CPU-bound en CPython?
- Athreading
- Basyncio
- Cmultiprocessing✓ Respuesta correcta
- Dconcurrent.futures.ThreadPoolExecutor
Por qué: multiprocessing crea procesos separados con su propio GIL, logrando paralelismo CPU real.
pa20¿Qué se imprime?
class Meta(type):
def __new__(mcs, name, bases, ns):
ns['greeting'] = 'hi'
return super().__new__(mcs, name, bases, ns)
class A(metaclass=Meta):
pass
print(A.greeting)- Ahi✓ Respuesta correcta
- BAttributeError
- CNone
- DTypeError: conflicto metaclase
Por qué: La metaclase inyecta greeting en el namespace antes de crear la clase, por eso A.greeting es "hi".
pa21¿Qué es r?
match point := (1, 0):
case (0, 0): r = 'origin'
case (x, 0): r = f'x-axis:{x}'
case (0, y): r = f'y-axis:{y}'
case _: r = 'other'
print(r)- Aorigin
- Bx-axis:1✓ Respuesta correcta
- Cy-axis:0
- Dother
Por qué: Los patrones se prueban en orden; (1,0) no coincide con (0,0), coincide con (x,0) con x=1, da "x-axis:1".
pa22¿Por qué ContextVar en vez de global?
from contextvars import ContextVar
import asyncio
user: ContextVar[str] = ContextVar('user')
async def who():
return user.get()
async def main():
user.set('ada')
print(await who())
asyncio.run(main())- AMás rápido que global
- BAislamiento por tarea/corrutina sin pasar explícito✓ Respuesta correcta
- CSustituto de lock
- DRequerido por asyncio.run
Por qué: ContextVar se ata al contexto actual (tarea); tareas concurrentes ven valores independientes sin pasarlos.
pa23¿Qué preserva ParamSpec?
from typing import ParamSpec, TypeVar, Callable
from functools import wraps
P = ParamSpec('P')
R = TypeVar('R')
def log(f: Callable[P, R]) -> Callable[P, R]:
@wraps(f)
def w(*a: P.args, **k: P.kwargs) -> R:
return f(*a, **k)
return w- ASolo el tipo de retorno
- BLa firma completa de parámetros✓ Respuesta correcta
- CEl rendimiento
- DEl nombre de la función
Por qué: ParamSpec captura parámetros posicionales y de palabra clave para que los decoradores los reenvíen con fidelidad de tipos.
pa24¿Qué imprime?
from itertools import islice, count
print(list(islice(count(10, 2), 2, 5)))- A[14, 16, 18]✓ Respuesta correcta
- B[10, 12, 14]
- C[12, 14, 16, 18]
- D[2, 3, 4]
Por qué: count(10,2) da 10,12,14,16,18,...; islice toma índices 2,3,4 → 14,16,18.
pa25¿Por qué anotar el retorno como Self en vez de "Builder"?
from typing import Self
class Builder:
def __init__(self) -> None:
self.parts: list[str] = []
def add(self, p: str) -> Self:
self.parts.append(p)
return self
class SubBuilder(Builder): pass- ASelf es más rápido
- BSelf preserva el tipo de la subclase para encadenado fluido✓ Respuesta correcta
- CSelf prohíbe herencia
- DSon equivalentes
Por qué: Self refiere al tipo real del receptor; SubBuilder().add("x") se tipa como SubBuilder, permitiendo encadenado consciente de subclases.
python-async · Async (asyncio) · 25
pas1¿Qué imprime este código?
import asyncio
async def f(n):
await asyncio.sleep(0.01)
return n * 2
async def main():
out = await asyncio.gather(f(3), f(1), f(2))
print(out)
asyncio.run(main())- A[6, 2, 4]✓ Respuesta correcta
- B[2, 4, 6]
- C[1, 2, 3]
- DUn objeto coroutine
Por qué: gather conserva el orden de envío, por eso el resultado coincide con f(3), f(1), f(2) sin importar el tiempo.
pas2¿Qué se imprime?
async def greet():
return "hi"
result = greet()
print(type(result).__name__)- Acoroutine✓ Respuesta correcta
- Bstr
- Cfunction
- DTask
Por qué: Llamar a una función async def devuelve un objeto coroutine; el cuerpo se ejecuta solo al await o programar.
pas3¿Cuánto tarda aproximadamente?
import asyncio, time
async def work():
time.sleep(1)
return 42
async def main():
return await asyncio.gather(work(), work(), work())
asyncio.run(main())- A~3 segundos✓ Respuesta correcta
- B~1 segundo
- C~0 segundos
- DLanza de inmediato
Por qué: time.sleep bloquea el bucle de eventos, por lo que las tres corrutinas se ejecutan en serie.
pas4¿Qué se imprime?
async def boom():
raise ValueError("x")
async def main():
res = await asyncio.gather(boom(), boom(), return_exceptions=True)
print([type(r).__name__ for r in res])
asyncio.run(main())- A['ValueError', 'ValueError']✓ Respuesta correcta
- BLanza ValueError
- C['NoneType', 'NoneType']
- D['Exception', 'Exception']
Por qué: Con return_exceptions=True, gather devuelve las excepciones como valores en vez de lanzarlas.
pas5¿Qué ocurre aquí?
async def slow():
await asyncio.sleep(5)
return 1
async def main():
try:
return await asyncio.wait_for(slow(), timeout=0.1)
except asyncio.TimeoutError:
print("timeout")
asyncio.run(main())- AImprime "timeout"✓ Respuesta correcta
- BDevuelve 1
- CSe cuelga 5 s
- DLanza ValueError
Por qué: wait_for cancela la corrutina interna al vencer el tiempo y lanza asyncio.TimeoutError.
pas6¿Qué hace asyncio.create_task?
- APrograma una corrutina en el bucle y devuelve una Task✓ Respuesta correcta
- BEjecuta la corrutina y bloquea
- CCrea un nuevo bucle de eventos
- DLanza un hilo del SO
Por qué: create_task envuelve una corrutina en una Task y la programa en el bucle activo.
pas7¿Cuál es el error?
async def f():
return 7
async def main():
print(f())
asyncio.run(main())- AFalta await — imprime un objeto coroutine✓ Respuesta correcta
- BFalta asyncio.run
- Cf debe ser sync
- Dmain no se espera
Por qué: Sin await, f() devuelve un objeto coroutine en vez del valor, y se emite un RuntimeWarning.
pas8¿Qué se imprime?
async def gen():
for i in range(3):
yield i
async def main():
total = 0
async for x in gen():
total += x
print(total)
asyncio.run(main())- A3✓ Respuesta correcta
- B6
- C0
- DSyntaxError
Por qué: gen produce 0, 1, 2 y async for los itera, suma = 3.
pas9¿Qué impone Semaphore(2)?
async def main():
sem = asyncio.Semaphore(2)
async def worker(i):
async with sem:
await asyncio.sleep(0.1)
return i
return await asyncio.gather(*(worker(i) for i in range(4)))
print(asyncio.run(main()))- AComo máximo 2 workers en el bloque a la vez✓ Respuesta correcta
- BExactamente 2 ejecuciones
- CRetraso de 2 segundos
- DDos reintentos
Por qué: Semaphore(2) limita a dos los poseedores concurrentes; los demás esperan una ranura.
pas10¿Qué se imprime?
async def main():
q = asyncio.Queue()
await q.put(1)
await q.put(2)
print(await q.get(), await q.get())
asyncio.run(main())- A1 2✓ Respuesta correcta
- B2 1
- CNone None
- DBloqueo mutuo
Por qué: asyncio.Queue es FIFO por defecto, los elementos salen en el orden de inserción.
pas11¿Cuándo asyncio no encaja?
- ACálculos intensivos de CPU✓ Respuesta correcta
- BMuchas peticiones de red concurrentes
- CWebsockets long-poll
- DConsultas DB lentas con driver async
Por qué: asyncio sirve para I/O; las tareas de CPU bloquean el bucle y necesitan procesos o executors.
pas12¿Qué se imprime al final?
async def hb():
while True:
print("tick")
await asyncio.sleep(1)
async def main():
t = asyncio.create_task(hb())
await asyncio.sleep(0.05)
t.cancel()
try:
await t
except asyncio.CancelledError:
print("done")
asyncio.run(main())- Adone✓ Respuesta correcta
- Btick
- CCancelledError
- DNada
Por qué: Cancelar la tarea inyecta CancelledError; el await la relanza, except la maneja y se imprime done.
pas13¿Qué hace asyncio.shield(coro)?
- AProtege la tarea interna de cancelaciones propagadas por shield✓ Respuesta correcta
- BCaptura excepciones en silencio
- CFuerza ejecución sincrónica
- DFija la tarea a una CPU
Por qué: shield protege la tarea interna de la cancelación del que espera; termina normalmente.
pas14¿Por qué async with con httpx.AsyncClient?
async def main():
async with httpx.AsyncClient() as client:
r = await client.get("https://api.example.com/x")
return r.status_code- AGarantiza el cierre del pool de conexiones✓ Respuesta correcta
- BRequerido para cualquier await
- CDesactiva verificación TLS
- DVuelve sincrónicas las peticiones
Por qué: El gestor de contexto garantiza aclose(), liberando el pool de conexiones.
pas15¿Diferencia entre httpx.AsyncClient y requests?
- Ahttpx soporta async/await; requests es solo sincrónico✓ Respuesta correcta
- Brequests siempre más rápido
- CAPI idénticas
- Dhttpx no admite POST
Por qué: httpx ofrece clientes sync y async; requests carece de soporte nativo de asyncio.
pas16¿Cuándo sale del async with de TaskGroup?
async def main():
async with asyncio.TaskGroup() as tg:
tg.create_task(asyncio.sleep(0.01))
tg.create_task(asyncio.sleep(0.02))
print("ok")
asyncio.run(main())- ACuando terminan todas las tareas hijas✓ Respuesta correcta
- BJusto tras crearlas
- CSolo al cancelar
- DTras 1 s de timeout
Por qué: TaskGroup espera a que terminen todas las tareas hijas; si alguna falla, las demás se cancelan.
pas17¿Para qué sirve run_in_executor aquí?
import time
def heavy(n):
time.sleep(n); return n
async def main():
loop = asyncio.get_running_loop()
return await loop.run_in_executor(None, heavy, 0.1)
print(asyncio.run(main()))- AEjecuta trabajo bloqueante fuera del bucle en un hilo✓ Respuesta correcta
- BAcelera corrutinas
- CReemplaza asyncio.run
- DHabilita pool de procesos
Por qué: run_in_executor delega una llamada bloqueante a un pool, manteniendo el bucle reactivo.
pas18¿Qué imprime?
async def f():
return 1
async def main():
t = asyncio.create_task(f())
print(isinstance(t, asyncio.Task), asyncio.iscoroutine(f()))
asyncio.run(main())- ATrue True✓ Respuesta correcta
- BTrue False
- CFalse True
- DFalse False
Por qué: create_task devuelve una Task; llamar a f() de nuevo crea otra corrutina.
pas19¿Cuál es el orden de salida?
from contextlib import asynccontextmanager
@asynccontextmanager
async def conn():
print("open")
yield "c"
print("close")
async def main():
async with conn() as c:
print(c)
asyncio.run(main())- Aopen / c / close✓ Respuesta correcta
- Bc / open / close
- Copen / close / c
- Dclose / c / open
Por qué: El código antes del yield corre al entrar, el valor va al as, el código tras yield al salir.
pas20¿Qué se observa?
async def f():
raise RuntimeError("oops")
async def main():
asyncio.create_task(f())
await asyncio.sleep(0.05)
print("ok")
asyncio.run(main())- AImprime ok y registra una excepción no recogida✓ Respuesta correcta
- BLanza RuntimeError de inmediato
- CSe cuelga
- DImprime "oops"
Por qué: Una tarea fire-and-forget oculta la excepción hasta el GC, cuando asyncio registra un aviso.
pas21¿Qué se imprime?
async def gen():
yield 1
yield 2
async def main():
it = gen()
print(await anext(it))
print(await anext(it))
asyncio.run(main())- A1 luego 2✓ Respuesta correcta
- B2 luego 1
- CStopAsyncIteration
- DDos objetos coroutine
Por qué: anext espera __anext__ del iterador asíncrono y devuelve cada valor en orden.
pas22¿En qué difieren seq y par?
async def task(n):
await asyncio.sleep(0.01)
return n
async def seq():
a = await task(1)
b = await task(2)
return a + b
async def par():
a, b = await asyncio.gather(task(1), task(2))
return a + b- Aseq secuencial (~0.02 s); par concurrente (~0.01 s)✓ Respuesta correcta
- BComportamiento idéntico
- Cpar secuencial, seq concurrente
- Dpar requiere hilos
Por qué: Esperar una y luego otra es serial; gather las ejecuta en paralelo y dura como la más lenta.
pas23¿Qué se imprime?
async def main():
ev = asyncio.Event()
async def waiter():
await ev.wait()
return "go"
t = asyncio.create_task(waiter())
await asyncio.sleep(0.01)
ev.set()
print(await t)
asyncio.run(main())- Ago✓ Respuesta correcta
- BNone
- CSe cuelga
- DTimeoutError
Por qué: Event.set() libera a los esperantes, wait() se resuelve y la tarea devuelve "go".
pas24¿Qué controla timeout=2.0?
async def fetch():
async with httpx.AsyncClient(timeout=2.0) as c:
r = await c.get("https://slow.example")
return r.text- ATimeout por defecto por petición para este cliente✓ Respuesta correcta
- BVida máxima del cliente
- CNúmero de reintentos
- DTamaño del pool de conexiones
Por qué: httpx aplica el timeout numérico a las fases connect, read, write y pool de cada petición.
pas25¿Diferencia entre coroutine, Task y Future?
- ACoroutine: resultado de llamada; Task: corrutina planificada; Future: marcador de bajo nivel✓ Respuesta correcta
- BLos tres son idénticos
- CFuture sincrónico, Task en hilo
- DCoroutine en hilo, Task en proceso
Por qué: Task es subclase de Future que impulsa una corrutina; Future es el primitivo de resultado awaitable.
python-fastapi · FastAPI + httpx · 25
pf1¿Qué hace FastAPI con GET /items/abc?
from fastapi import FastAPI
app = FastAPI()
@app.get('/items/{item_id}')
def read(item_id: int):
return {'id': item_id}- ADevuelve 200 con id="abc"
- BDevuelve 422 error de validación✓ Respuesta correcta
- CDevuelve 404 not found
- DLanza ValueError
Por qué: El parámetro es int; "abc" no se puede convertir, así que FastAPI devuelve 422 con detalles.
pf2¿Qué llamada usa ambos valores por defecto?
from fastapi import FastAPI
app = FastAPI()
@app.get('/search')
def search(q: str = 'all', limit: int = 10):
return {'q': q, 'limit': limit}- A/search?q=&limit=
- B/search✓ Respuesta correcta
- C/search?q=all
- D/search?limit=10
Por qué: Omitir ambos parámetros conserva los valores por defecto q="all" y limit=10.
pf3¿Qué estado HTTP produce GET /u/-1?
from fastapi import FastAPI, HTTPException
app = FastAPI()
@app.get('/u/{i}')
def get(i: int):
if i < 0:
raise HTTPException(status_code=404, detail='nope')
return {'i': i}- A200
- B400
- C404✓ Respuesta correcta
- D500
Por qué: HTTPException(status_code=404) se lanza explícitamente; FastAPI devuelve 404.
pf4El body { "name": "x" } devuelve ¿qué estado?
from fastapi import FastAPI
from pydantic import BaseModel, Field
class Item(BaseModel):
name: str = Field(min_length=2)
app = FastAPI()
@app.post('/i')
def add(it: Item):
return it- A200
- B400
- C422✓ Respuesta correcta
- D500
Por qué: Field(min_length=2) rechaza "x" (longitud 1); Pydantic lanza error y FastAPI devuelve 422.
pf5¿Qué recibe el cliente en el JSON?
from fastapi import FastAPI
from pydantic import BaseModel
class Out(BaseModel):
name: str
app = FastAPI()
@app.get('/me', response_model=Out)
def me():
return {'name': 'Ada', 'token': 'secret'}- A{"name":"Ada","token":"secret"}
- B{"name":"Ada"}✓ Respuesta correcta
- C{"token":"secret"}
- DError 500
Por qué: response_model filtra la salida a los campos declarados; "token" se elimina antes de serializar.
pf6¿Qué código de estado devuelve este endpoint?
from fastapi import FastAPI, status
app = FastAPI()
@app.post('/items', status_code=status.HTTP_201_CREATED)
def create():
return {'ok': True}- A200
- B201✓ Respuesta correcta
- C202
- D204
Por qué: El parámetro status_code del decorador sustituye 200 por 201 Created.
pf7¿Qué devuelve GET /i?q=hi?
from fastapi import FastAPI, Depends
app = FastAPI()
def common(q: str = ''):
return {'q': q}
@app.get('/i')
def list_items(c: dict = Depends(common)):
return c- A{"q":""}
- B{"q":"hi"}✓ Respuesta correcta
- CError 422
- D{"c":{"q":"hi"}}
Por qué: Depends(common) ejecuta common(q="hi") e inyecta el dict, devuelto como JSON.
pf8¿Cuándo se ejecuta el bloque finally?
from fastapi import FastAPI, Depends
app = FastAPI()
def get_db():
db = {'x': 1}
try:
yield db
finally:
db.clear()
@app.get('/')
def r(d=Depends(get_db)):
return d- AAntes del endpoint
- BDespués de enviar la respuesta✓ Respuesta correcta
- CNunca, solo en errores
- DAl iniciar la app
Por qué: Las dependencias con yield ejecutan limpieza tras enviar la respuesta, liberando recursos.
pf9¿Por qué async def es correcto en lugar de def?
from fastapi import FastAPI
import httpx
app = FastAPI()
@app.get('/proxy')
async def proxy():
async with httpx.AsyncClient() as c:
r = await c.get('https://api.example.com')
return r.json()- Adef no puede devolver JSON
- Bawait requiere función async✓ Respuesta correcta
- Chttpx solo es sync
- DFastAPI requiere async
Por qué: await solo es válido en funciones async; httpx.AsyncClient debe ser awaited, así que la función es async.
pf10¿Cuándo se ejecuta log() respecto a la respuesta?
from fastapi import FastAPI, BackgroundTasks
app = FastAPI()
def log(msg: str):
open('log.txt', 'a').write(msg)
@app.post('/notify')
def notify(bt: BackgroundTasks):
bt.add_task(log, 'sent')
return {'ok': True}- AAntes de enviar
- BDespués de enviar✓ Respuesta correcta
- CEn paralelo con la respuesta
- DEn la próxima petición
Por qué: BackgroundTasks ejecuta las tareas después de devolver la respuesta al cliente.
pf11¿Qué hace este middleware?
from fastapi import FastAPI, Request
app = FastAPI()
@app.middleware('http')
async def add_header(request: Request, call_next):
resp = await call_next(request)
resp.headers['X-App'] = 'demo'
return resp- ABloquea todas las peticiones
- BAñade X-App a cada respuesta✓ Respuesta correcta
- CRedirige peticiones
- DRegistra los bodies
Por qué: El middleware espera la respuesta, añade el encabezado y la devuelve.
pf12¿Qué origen puede llamar la API desde el navegador?
from fastapi import FastAPI
from fastapi.middleware.cors import CORSMiddleware
app = FastAPI()
app.add_middleware(
CORSMiddleware,
allow_origins=['https://x.com'],
allow_methods=['*'],
)- ACualquiera
- BSolo https://x.com✓ Respuesta correcta
- CSolo mismo origen
- DSolo http://x.com
Por qué: allow_origins es una lista estricta; solo el origen listado recibe encabezados CORS.
pf13¿Qué content-type debe usar el cliente?
from fastapi import FastAPI, UploadFile
app = FastAPI()
@app.post('/up')
async def upload(file: UploadFile):
data = await file.read()
return {'size': len(data), 'name': file.filename}- Aapplication/json
- Bapplication/x-www-form-urlencoded
- Cmultipart/form-data✓ Respuesta correcta
- Dtext/plain
Por qué: UploadFile lee de multipart/form-data; FastAPI necesita python-multipart instalado.
pf14¿Qué debe llamarse antes de send_text o receive_text?
from fastapi import FastAPI, WebSocket
app = FastAPI()
@app.websocket('/ws')
async def ws(websocket: WebSocket):
await websocket.accept()
msg = await websocket.receive_text()
await websocket.send_text(f'echo:{msg}')- Awebsocket.connect()
- Bwebsocket.accept()✓ Respuesta correcta
- Cwebsocket.open()
- DNada, opcional
Por qué: El handshake WebSocket se completa con accept(); antes, los métodos fallan.
pf15¿Por qué usar StreamingResponse?
from fastapi import FastAPI
from fastapi.responses import StreamingResponse
app = FastAPI()
def gen():
for i in range(3):
yield f'chunk{i}\n'
@app.get('/s')
def stream():
return StreamingResponse(gen(), media_type='text/plain')- AComprime automáticamente
- BEnvía el body por partes sin buffering✓ Respuesta correcta
- CRequerido para JSON
- DAñade CORS
Por qué: StreamingResponse itera un generador y envía los chunks según se producen.
pf16¿Cuál es la ruta completa de list_users?
from fastapi import FastAPI, APIRouter
router = APIRouter(prefix='/v1', tags=['users'])
@router.get('/users')
def list_users():
return []
app = FastAPI()
app.include_router(router)- A/users
- B/v1/users✓ Respuesta correcta
- C/users/v1
- D/list_users
Por qué: El prefix se antepone; tags solo agrupa en OpenAPI sin afectar la ruta.
pf17¿Cómo debe llegar el token?
from fastapi import FastAPI, Depends
from fastapi.security import OAuth2PasswordBearer
oauth2 = OAuth2PasswordBearer(tokenUrl='token')
app = FastAPI()
@app.get('/me')
def me(token: str = Depends(oauth2)):
return {'token': token}- AQuery ?token=
- BCookie token
- CHeader Authorization: Bearer✓ Respuesta correcta
- DCampo body token
Por qué: OAuth2PasswordBearer toma el token del header Authorization con esquema Bearer.
pf18¿Qué ejecuta TestClient internamente?
from fastapi import FastAPI
from fastapi.testclient import TestClient
app = FastAPI()
@app.get('/ping')
def ping():
return {'pong': True}
client = TestClient(app)
r = client.get('/ping')- AUn socket real
- BLlamadas ASGI en proceso vía httpx✓ Respuesta correcta
- CUn uvicorn separado
- DSelenium
Por qué: TestClient envuelve httpx con transporte ASGI, llamando la app sin abrir un puerto.
pf19¿Qué reemplaza este patrón en FastAPI moderno?
from contextlib import asynccontextmanager
from fastapi import FastAPI
@asynccontextmanager
async def lifespan(app: FastAPI):
print('startup')
yield
print('shutdown')
app = FastAPI(lifespan=lifespan)- ADecorador middleware
- B@app.on_event("startup"/"shutdown")✓ Respuesta correcta
- CInyección de dependencias
- DBackgroundTasks
Por qué: Lifespan reemplaza los obsoletos on_event, unificando inicio y cierre.
pf20Para {"name":"ada"}, ¿cuál es User().name?
from pydantic import BaseModel, field_validator
class User(BaseModel):
name: str
@field_validator('name')
@classmethod
def upper(cls, v: str) -> str:
if not v:
raise ValueError('empty')
return v.upper()- A"ada"
- B"ADA"✓ Respuesta correcta
- CValidationError
- DNone
Por qué: field_validator devuelve el valor transformado; "ADA" sustituye la entrada original.
pf21¿Qué ocurre con {"name":"a","age":1}?
from pydantic import BaseModel, ConfigDict
class User(BaseModel):
model_config = ConfigDict(extra='forbid')
name: str- AAceptado, age ignorado
- BValidationError✓ Respuesta correcta
- Cage almacenado
- DAdvertencia silenciosa
Por qué: extra="forbid" hace que Pydantic v2 rechace campos no declarados.
pf22¿Qué hace asyncio.gather con las dos peticiones?
import asyncio, httpx
from fastapi import FastAPI
app = FastAPI()
@app.get('/agg')
async def agg():
async with httpx.AsyncClient() as c:
a, b = await asyncio.gather(
c.get('https://x/1'), c.get('https://x/2'))
return {'a': a.status_code, 'b': b.status_code}- ASecuencialmente
- BConcurrentemente, espera ambas✓ Respuesta correcta
- CCrea hilos del SO
- DCachea la 2ª
Por qué: gather agenda ambas corrutinas en el loop; el tiempo total es el máximo, no la suma.
pf23¿Cómo se llama este patrón?
from fastapi import FastAPI, Depends
def auth(token: str = ''):
return token == 'ok'
def admin(ok: bool = Depends(auth)):
return ok
app = FastAPI()
@app.get('/a')
def page(is_admin: bool = Depends(admin)):
return is_admin- ASub-dependencias✓ Respuesta correcta
- BCadena middleware
- CTareas en segundo plano
- DLifespan hooks
Por qué: admin depende de auth y la ruta de admin — FastAPI resuelve la cadena.
pf24¿Por qué sync def es aceptable aunque bloquea?
from fastapi import FastAPI
import time
app = FastAPI()
@app.get('/work')
def work():
time.sleep(2)
return 'done'- AFastAPI ignora el bloqueo
- BSe ejecuta en threadpool, no en el loop✓ Respuesta correcta
- Ctime.sleep no bloquea
- DBloquea el loop fatalmente
Por qué: Los endpoints sync van a un threadpool de anyio, así que no congelan el loop.
pf25¿Por qué yield en vez de return para la sesión?
from fastapi import FastAPI, Depends
from sqlalchemy.orm import Session
def get_session():
s = Session()
try:
yield s
finally:
s.close()
app = FastAPI()
@app.get('/u')
def list_u(db: Session = Depends(get_session)):
return db.execute('SELECT 1').all()- Areturn prohibido
- BPara cerrar la sesión tras la respuesta✓ Respuesta correcta
- Cyield es más rápido
- DSQLAlchemy lo exige
Por qué: Yield permite que FastAPI ejecute finally tras la respuesta, cerrando la sesión siempre.