Preguntas de entrevista de Python — 20 que más caen (con respuestas)

Llevo desde 2018 a los dos lados de la mesa en entrevistas técnicas de Python: como candidato y, cada vez más, entrevistando a otros. Y hay un patrón claro. Las mismas preguntas caen una y otra vez. No porque los entrevistadores no tengan imaginación, sino porque destilan muy bien quién entiende Python de verdad y quién lo ha copiado de Stack Overflow.
Aquí tienes las 20 que más se repiten, agrupadas por bloques, con respuestas que puedes decir en voz alta y código que se ejecuta tal cual. No las memorices como un loro: entiéndelas. En la entrevista se nota la diferencia.
Si todavía estás decidiendo a qué perfil tirar (backend, data, IA…), antes lee salidas profesionales de Python — te ahorra prepararte para el puesto equivocado.
Contenido
Bloque 1 — Conceptos que SIEMPRE caen
Estas son las de calentamiento. Si fallas una de estas, la entrevista se acaba antes de empezar.
1. ¿Diferencia entre is y ==?
== compara valores: ¿son iguales? is compara identidad: ¿son el mismo objeto en memoria?
a = [1, 2, 3]
b = [1, 2, 3]
print(a == b) # True -> mismo contenido
print(a is b) # False -> objetos distintos
Regla práctica: usa == para comparar valores y is solo con None, True y False (if x is None:). La trampa con enteros pequeños la vemos en el bloque 3.
2. ¿Listas vs tuplas?
La lista es mutable (puedes cambiarla); la tupla es inmutable (no). Por eso una tupla puede ser clave de un diccionario y una lista no.
coordenadas = (40.4, -3.7) # no va a cambiar -> tupla
historial = [40.4, -3.7] # va creciendo -> lista
posiciones = {(0, 0): "origen"} # tupla como clave: OK
# posiciones = {[0, 0]: "origen"} # TypeError: unhashable type: 'list'
Buena respuesta extra: las tuplas señalan intención (“esto no debería cambiar”) y son ligeramente más eficientes.
3. ¿Qué es mutable e inmutable en Python?
Inmutables: int, float, str, tuple, frozenset, bool. Mutables: list, dict, set, y los objetos de tus clases por defecto.
Importa porque cambia cómo se pasan a las funciones: con un mutable, la función puede modificar el original.
def añadir(lista):
lista.append(99) # modifica el original
datos = [1, 2]
añadir(datos)
print(datos) # [1, 2, 99]
4. ¿Qué son *args y **kwargs?
*args recoge argumentos posicionales extra en una tupla; **kwargs, los nombrados en un diccionario. Sirven para escribir funciones que aceptan un número variable de argumentos.
def registrar(*args, **kwargs):
print(args) # (1, 2)
print(kwargs) # {'nivel': 'INFO'}
registrar(1, 2, nivel="INFO")
Si quieres la guía a fondo: *args y **kwargs explicados.
5. ¿Qué es una list comprehension y cuándo NO usarla?
Una forma compacta de construir una lista. Es más rápida y legible que un for con append… hasta que deja de serlo.
cuadrados = [n * n for n in range(10) if n % 2 == 0]
La respuesta que impresiona: “La uso cuando cabe en una línea y se lee de un vistazo. Si tiene dos for anidados y tres condiciones, vuelvo al bucle normal: la legibilidad gana.” Más detalle en list comprehensions.
6. ¿append vs extend?
append añade un elemento (aunque sea una lista entera). extend añade cada elemento de un iterable.
a = [1, 2]
a.append([3, 4]) # [1, 2, [3, 4]]
b = [1, 2]
b.extend([3, 4]) # [1, 2, 3, 4]
7. ¿Cómo funciona el slicing?
secuencia[inicio:fin:paso]. fin no se incluye. Los índices negativos cuentan desde el final.
texto = "Python"
print(texto[1:4]) # 'yth'
print(texto[::-1]) # 'nohtyP' -> invertir
print(texto[-2:]) # 'on'
El truco [::-1] para invertir cae mucho.
Bloque 2 — Estructuras de datos y rendimiento
Aquí es donde un junior y alguien con criterio empiezan a separarse. No basta con conocer la sintaxis: hay que saber qué cuesta cada operación.
8. ¿Por qué buscar en un set o dict es O(1) y en una lista O(n)?
Porque set y dict son tablas hash: calculan dónde está el elemento directamente. La lista tiene que recorrer elemento a elemento.
nombres_lista = ["ana", "luis", "marta"] # "marta" in lista -> O(n)
nombres_set = {"ana", "luis", "marta"} # "marta" in set -> O(1)
Si vas a comprobar pertenencia muchas veces sobre datos grandes, convertir a set primero es la diferencia entre milisegundos y minutos. Tienes el desglose completo en Big-O en Python para entrevistas.
9. ¿Cuándo usar un set?
Dos casos estrella: eliminar duplicados y comprobar pertenencia rápido.
emails = ["a@x.com", "b@x.com", "a@x.com"]
unicos = list(set(emails)) # sin duplicados
10. ¿Qué es un diccionario por dentro?
Una tabla hash: cada clave se pasa por una función hash que decide dónde se guarda. Por eso las claves deben ser hashables (inmutables) y por eso el acceso es tan rápido. Desde Python 3.7, además, mantienen el orden de inserción.
11. collections: Counter, defaultdict, deque
Conocer el módulo collections señala experiencia real:
from collections import Counter, defaultdict, deque
Counter("banana") # Counter({'a': 3, 'n': 2, 'b': 1})
grupos = defaultdict(list)
grupos["frutas"].append("pera") # sin comprobar si la clave existe
cola = deque([1, 2, 3])
cola.appendleft(0) # O(1) por ambos extremos (una lista no)
Bloque 3 — Las trampas que descartan candidatos
Estas son las preguntas con “truco”. No buscan que las sepas de memoria: buscan ver si entiendes cómo funciona Python por dentro.
12. El argumento mutable por defecto (la pregunta trampa nº 1)
def añadir(item, lista=[]):
lista.append(item)
return lista
print(añadir(1)) # [1]
print(añadir(2)) # [1, 2] <- ¡sorpresa!
El valor por defecto se evalúa una sola vez, al definir la función, no en cada llamada. Así que todas comparten la misma lista. La solución correcta:
def añadir(item, lista=None):
if lista is None:
lista = []
lista.append(item)
return lista
Si respondes esta bien, subes un escalón inmediato.
13. Late binding en closures (el clásico del bucle)
funciones = [lambda: i for i in range(3)]
print([f() for f in funciones]) # [2, 2, 2] <- no [0, 1, 2]
Las lambdas capturan la variable i, no su valor en ese momento. Cuando se ejecutan, i ya vale 2. Solución: fijar el valor con un argumento por defecto.
funciones = [lambda i=i: i for i in range(3)]
print([f() for f in funciones]) # [0, 1, 2]
14. Copia superficial vs copia profunda
import copy
original = [[1, 2], [3, 4]]
superficial = original.copy() # copia la lista externa, no las internas
profunda = copy.deepcopy(original) # copia todo, en todos los niveles
original[0][0] = 99
print(superficial) # [[99, 2], [3, 4]] <- afectada
print(profunda) # [[1, 2], [3, 4]] <- intacta
15. is con enteros pequeños
a = 256; b = 256
print(a is b) # True -> Python cachea -5..256
a = 257; b = 257
print(a is b) # False (normalmente) -> fuera del caché
La moraleja no es memorizar el rango: es “por eso nunca comparo números con is, uso ==“. Eso es lo que el entrevistador quiere oír.
📥 Llévate el cheatsheet de Python (gratis)
PDF de 6 páginas con lo esencial: tipos, condicionales, bucles, estructuras de datos, funciones y los errores que más vas a cometer. Para tener al lado mientras programas.
Sin spam. Te apuntas a la lista, descargas el cheatsheet y recibes contenido de Python cada semana.
Bloque 4 — Para subir de nivel (intermedio/senior)
16. ¿Qué es un generador y yield?
Una función que produce valores uno a uno, sin construir toda la lista en memoria. Ideal para datos grandes o infinitos.
def numeros():
n = 0
while True:
yield n # pausa aquí y devuelve n
n += 1
La frase ganadora: “Una list comprehension materializa todo en memoria; un generador es perezoso y solo calcula el siguiente cuando se lo pides.” Profundiza en generadores y yield.
17. ¿Qué es un decorador?
Una función que recibe otra función y devuelve una versión envuelta, sin tocar el original. El @ es solo azúcar sintáctico.
@medir_tiempo
def procesar():
...
# equivale a: procesar = medir_tiempo(procesar)
Los ves por todos lados: @app.route, @property, @pytest.fixture. Guía completa: decoradores explicados.
18. ¿Qué es el GIL? ¿Threads o procesos?
El GIL (Global Interpreter Lock) hace que en CPython solo un hilo ejecute bytecode Python a la vez. Consecuencia práctica:
- Tareas I/O-bound (red, disco): los threads sí ayudan (el GIL se libera esperando).
- Tareas CPU-bound (cálculo puro): los threads NO aceleran; usa
multiprocessingoasynciosegún el caso.
Si quieres entender el “Python es lento” que hay detrás: ¿por qué Python es lento?.
19. ¿Duck typing? ¿EAFP vs LBYL?
Duck typing: “si camina como un pato y hace cuac, es un pato” — importa lo que un objeto hace, no su tipo. De ahí el estilo EAFP (easier to ask forgiveness than permission), idiomático en Python:
# EAFP (pythónico)
try:
valor = config["clave"]
except KeyError:
valor = "por defecto"
# LBYL (menos pythónico aquí)
if "clave" in config:
valor = config["clave"]
else:
valor = "por defecto"
20. ¿Cómo manejas excepciones? try/except/else/finally
try:
f = abrir_archivo()
except FileNotFoundError:
print("No existe")
else:
print("Todo bien") # solo si NO hubo excepción
finally:
print("Siempre se ejecuta") # limpieza
La buena práctica que valoran: captura excepciones concretas, no un except: pelado que se traga hasta los errores de teclado.
Cómo prepararte el resto (no solo memorizar)
Saber las respuestas es el 50%. El otro 50% es resolver el ejercicio en vivo y explicar tu razonamiento en voz alta sin quedarte en blanco. Eso se entrena aparte:
- Para los ejercicios de código: patrones de coding interview en Python.
- Para no bloquearte hablando: cómo explicar tu código en una entrevista técnica.
Y si quieres llevarte todo esto en una hoja para repasar el día antes, descárgate el cheatsheet (justo aquí abajo).
En resumen
Las entrevistas de Python repiten patrones: conceptos base (is/==, mutabilidad), estructuras de datos con su coste, las trampas clásicas (argumento mutable por defecto, late binding) y un par de temas senior (GIL, generadores). Si entiendes el porqué de cada una —no solo la respuesta— vas a transmitir criterio, que es justo lo que se evalúa.
Todo lo que has visto aquí (y más) está explicado paso a paso, con proyectos reales, en el curso. Si quieres pasar de “sé responder preguntas sueltas” a “domino Python para trabajar de esto”, ese es el camino.
¿Quieres aprender Python en orden, no a saltos?
Esto que has leído es solo una pieza. En El Pythonista lo verás todo encadenado: 11 módulos, 37+ horas de vídeo, 734 actividades y un proyecto real (MovieTracker) que crece contigo desde la primera variable hasta el deploy a producción.
37+ horas · 734 actividades · Proyecto real · Acceso de por vida · 14 días de garantía
