Sala de Aprendizaje Karinoya

Certificaciones · Laboratorio para aprobar Cloud / IA / Python

Excepciones, clases y biblioteca estándar

Puedes leer las preguntas y explicaciones en Español. Las lecciones (artículos explicativos) solo están disponibles en japonés.

Ver la versión en japonés (con lecciones) →

Pregunta 1 | El orden de una sentencia try

Al ejecutar el siguiente código, ¿cuáles son las líneas que se imprimen, en orden de arriba abajo?

def calc(a, b):    try:        print("try")        r = a / b    except ZeroDivisionError:        print("except")    else:        print("else")    finally:        print("finally")calc(9, 3)
  1. try, else y except
  2. try, else y finally
  3. try, except y else
  4. try, except y finally
RespuestaB. try, else y finally

9 / 3 no produce ninguna excepción (例外), así que no se entra en except. La cláusula else solo se ejecuta cuando no se produjo ninguna excepción, así que else se ejecuta después de try. La cláusula finally siempre se ejecuta, se haya producido o no una excepción, así que se ejecuta al final. Por tanto, las 3 líneas son try, else, finally. Una opción que incluye except es el orden para el caso en que se cree por error que se produjo una excepción; con esta llamada no se produce ZeroDivisionError. Una opción sin finally es el orden que obtendría si confundiera finally con una "limpieza para cuando se produce una excepción".

Pregunta 2 | La cláusula else

¿Cuál es la condición correcta en la que se ejecuta la cláusula else de una sentencia try?

  1. Siempre se ejecuta al final, se haya producido o no una excepción
  2. Solo se ejecuta cuando se produjo una excepción en la cláusula try
  3. Se ejecuta cuando ninguna de las cláusulas except coincidió
  4. Solo se ejecuta cuando no se produjo ninguna excepción en la cláusula try
RespuestaD. Solo se ejecuta cuando no se produjo ninguna excepción en la cláusula try

La cláusula else (else節) de una sentencia try solo se ejecuta cuando la cláusula try terminó hasta el final sin producir ninguna excepción. Conviene recordar que esto va en sentido contrario al else de una sentencia if, que es "cuando la condición es falsa": el else de try está del lado de "cuando todo salió bien". Lo que se ejecuta cuando se produjo una excepción es la cláusula except. Lo que se ejecuta siempre, se haya producido o no una excepción, es, en cambio, la cláusula finally. Cuando ninguna de las cláusulas except coincide, no se pasa a else; esa excepción se propaga hacia quien hizo la llamada.

Pregunta 3 | finally tiene prioridad

¿Cuál es la salida al ejecutar el siguiente código?

def f():    try:        return "T"    finally:        print("F")def g():    try:        return "T"    finally:        return "N"print(f(), g())
  1. Se imprimen las 2 líneas F y T T
  2. Se imprimen las 2 líneas F y T N
  3. Se imprimen las 2 líneas T T y F
  4. Se imprimen las 2 líneas T N y F
RespuestaB. Se imprimen las 2 líneas F y T N

f() prepara el valor que va a devolver, pero antes de devolverlo realmente ejecuta la cláusula finally (finally節). Por eso, F se imprime primero y el valor de retorno sigue siendo T. g() tiene un return dentro de su cláusula finally, y este sobrescribe el return de la cláusula try, así que el valor de retorno pasa a ser N. print evalúa f() y g() y luego los muestra juntos en una sola línea, así que primero va la línea F y después la línea T N. La opción que da T T es la respuesta para el caso en que se piensa que el return de finally se ignora, y la opción en la que F va después es la respuesta para el caso en que se piensa que finally se ejecuta después del return.

Pregunta 4 | Una excepción que no se puede capturar

¿Qué excepción no se puede capturar con una cláusula except escrita como except Exception?

  1. FileNotFoundError
  2. ZeroDivisionError
  3. KeyboardInterrupt
  4. ModuleNotFoundError
RespuestaC. KeyboardInterrupt

KeyboardInterrupt está directamente debajo de BaseException; no hereda de Exception. issubclass(KeyboardInterrupt, Exception) es False. Sirve para cuando un usuario intenta detener el programa con Ctrl-C, así que la jerarquía (階層) está separada para que no la absorba por accidente un except Exception escrito de forma amplia. SystemExit está en la misma posición. Las 3 restantes son todas descendientes de Exception: ZeroDivisionError está debajo de ArithmeticError, FileNotFoundError debajo de OSError y ModuleNotFoundError debajo de ImportError, así que todas se pueden capturar con except Exception.

Pregunta 5 | El padre de una excepción

¿Cuál es la clase padre común más cercana de IndexError y KeyError?

  1. ReferenceError
  2. ArithmeticError
  3. AttributeError
  4. LookupError
RespuestaD. LookupError

Tanto IndexError como KeyError comparten la misma naturaleza: buscar algo mediante un índice o una clave y no encontrarlo, así que se colocan bajo la clase padre (親クラス) común LookupError. Si escribe except LookupError, puede capturar a la vez un acceso fuera de rango en una lista y una clave inexistente en un diccionario. ArithmeticError es el padre de excepciones como ZeroDivisionError, para situaciones en que el propio cálculo no se puede realizar. AttributeError es para cuando no se encuentra un atributo, y ReferenceError es para cuando el destino de una referencia débil ha desaparecido; ambas están directamente debajo de Exception y pertenecen a un linaje distinto del de LookupError.

Pregunta 6 | El orden de except

¿Cuál es la salida correcta al ejecutar el siguiente código?

try:    x = 8 / 0except ArithmeticError:    print("A")except ZeroDivisionError:    print("Z")
  1. No se imprime nada y se detiene
  2. Solo se imprime Z
  3. Se imprimen A y Z
  4. Solo se imprime A
RespuestaD. Solo se imprime A

8 / 0 produce ZeroDivisionError, pero ZeroDivisionError es una clase hija (子クラス) de ArithmeticError. Las cláusulas except se comparan en orden desde arriba, y la comparación se detiene en la primera que coincide, así que la except ArithmeticError escrita primero la captura y solo se imprime A. La except ZeroDivisionError posterior nunca llega a actuar. Solo se ejecuta un except coincidente, así que no ocurre que se impriman A y Z a la vez. El principio es colocar primero la más específica (la clase hija) y después la más amplia (la clase padre), y el código de esta pregunta tiene ese orden invertido.

Pregunta 7 | Varias excepciones

¿Cuál es la única línea que se muestra al ejecutar el siguiente código?

def conv(s):    try:        return int(s) * 2    except (ValueError, TypeError) as e:        return type(e).__name__print(conv("21"), conv("abc"), conv(None))
  1. 42 ValueError TypeError
  2. 42 TypeError TypeError
  3. 42 ValueError ValueError
  4. 42 TypeError ValueError
RespuestaA. 42 ValueError TypeError

Si se escribe una tupla entre paréntesis después de except, se ejecuta si coincide cualquiera de ellas. conv("21") da int("21") igual a 21, así que devuelve 42. conv("abc") se puede pasar como cadena, pero no se puede interpretar como número, así que produce ValueError. conv(None) no es de un tipo que se pueda pasar a int(), así que produce TypeError. La distinción —si el tipo es correcto pero el valor es inadecuado, se produce ValueError; si el tipo no corresponde en absoluto, se produce TypeError— coincide exactamente con el orden de la respuesta.

Pregunta 8 | Volver a lanzar

¿Cuál es la salida del siguiente código, que usa una excepción personalizada y un raise sin argumentos?

class LoadError(Exception):    passdef load():    try:        raise LoadError("bad")    except LoadError:        print("inner")        raisetry:    load()except Exception as e:    print("outer", type(e).__name__)
  1. Solo se imprime inner, y outer no se imprime
  2. Se imprimen las 2 líneas inner y outer Exception
  3. Se imprimen las 2 líneas inner y outer LoadError
  4. Imprime inner y luego se detiene en LoadError
RespuestaC. Se imprimen las 2 líneas inner y outer LoadError

Escribir solo raise, sin argumento, dentro de un bloque except vuelve a lanzar (再送出) tal cual la excepción que se está manejando hacia quien hizo la llamada. Por eso, después de que se imprime inner, LoadError se propaga fuera de load(), y el except Exception exterior la captura. LoadError hereda de Exception, así que except Exception puede capturarla y el programa no se detiene. type(e).__name__ devuelve el nombre de la clase de excepción que realmente se lanzó, así que lo que se imprime es LoadError, no Exception, el nombre del tipo del lado que captura. Que solo apareciera inner sería la salida para el caso en que la excepción se absorbiera ahí mismo sin volver a lanzarse.

Pregunta 9 | Una excepción no capturada

¿Qué resultado produce la ejecución del siguiente código?

def get(seq, key):    try:        return seq[key]    except IndexError:        return "not found"print(get([1, 2, 3], 1))print(get({"a": 1}, "z"))
  1. Imprime 2 y luego se detiene en KeyError
  2. Se imprimen las 2 líneas 2 y not found
  3. Se imprimen las 2 líneas 2 y KeyError
  4. Se imprimen las 2 líneas 2 y None
RespuestaA. Imprime 2 y luego se detiene en KeyError

La primera llamada es un índice 1 de una lista, así que devuelve 2. La segunda busca una clave que no está en el diccionario, así que se produce KeyError, pero en except solo está escrito IndexError. IndexError y KeyError son ambas hijas de LookupError, pero no son padre e hija entre sí, así que un except para IndexError no puede capturar un KeyError. La excepción no capturada se propaga (伝播) hacia quien hizo la llamada, y como no hay nadie que la reciba, el programa se detiene. Si quiere capturar ambas, escriba except LookupError o ponga las dos juntas en una tupla.

Pregunta 10 | assert

¿Cuál de las siguientes opciones describe correctamente la sentencia assert?

  1. Si la condición es falsa, lanza AssertionError, y se desactiva con python3 -O
  2. Si la condición es falsa, lanza AssertionError, y siempre se ejecuta incluso con python3 -O
  3. Si la condición es verdadera, lanza AssertionError, y se desactiva con python3 -O
  4. Si la condición es falsa, lanza ValueError, y siempre se ejecuta incluso con python3 -O
RespuestaA. Si la condición es falsa, lanza AssertionError, y se desactiva con python3 -O

assert condición, mensaje lanza AssertionError cuando la condición es falsa. Cuando la condición es verdadera, no ocurre nada. El punto importante es que, cuando se inicia en un modo optimizado (最適化) como python3 -O, la sentencia assert se elimina por completo y no se ejecuta ni una sola línea de ella. Por eso, assert no debe usarse para validar la entrada del usuario; para ese fin, se comprueba la condición con if y se lanza ValueError(...). Lo mejor es recordar assert como una herramienta para dejar constancia de los supuestos internos durante el desarrollo, y mantener ese papel separado.

Pregunta 11 | self

¿Cuál de las siguientes opciones describe correctamente self, el primer parámetro de un método?

  1. Un nombre reservado para hacer referencia a los atributos de la clase padre
  2. Un argumento que recibe automáticamente la propia instancia sobre la que se llamó
  3. Un argumento para crear una instancia y devolverla a quien llama
  4. Un nombre reservado que recibe automáticamente la propia clase
RespuestaB. Un argumento que recibe automáticamente la propia instancia sobre la que se llamó

Cuando escribe d.speak(), Python lo reinterpreta como Dog.speak(d) antes de llamarlo. Lo que entra en la posición del primer argumento es la propia instancia (インスタンス) que hizo la llamada. La grafía self es solo una convención; no es un nombre reservado por el lenguaje. Como el lenguaje solo especifica que la posición del primer argumento recibe la propia instancia, escribir usted mismo Dog.speak(d) también funciona igual. Crear y devolver una instancia es tarea de __new__ y del mecanismo que hay detrás de llamar a una clase, y __init__ solo inicializa una instancia que ya se ha creado. Para hacer referencia a la clase padre se usa super().

Pregunta 12 | Variables de clase

¿Cuál es la salida del siguiente código, que reescribe una variable de clase?

class Dog:    kind = "canine"    def __init__(self, name):        self.name = namea = Dog("Rex")b = Dog("Mia")Dog.kind = "wolf"print(a.kind, b.kind)
  1. wolf wolf
  2. canine wolf
  3. canine canine
  4. wolf canine
RespuestaA. wolf wolf

kind es una variable de clase (クラス変数) escrita directamente debajo de class, y todas las instancias creadas a partir de esa clase comparten esa única variable. Ni a ni b tienen kind en su propio __dict__, así que, al leer el atributo, ambas suben hasta la clase y ven el mismo valor. Una asignación a Dog.kind reescribe ese único valor compartido, así que wolf se vuelve visible tanto desde a como desde b, que ya estaban creadas. Como el valor no se copia en el momento de crear una instancia, que quede canine no es ninguna de las opciones correctas.

Pregunta 13 | Una sombra sobre el atributo

¿Cuál es la salida del siguiente código, que asigna en el lado de la instancia?

class Dog:    kind = "canine"    def __init__(self, name):        self.name = namea = Dog("Rex")b = Dog("Mia")a.kind = "husky"print(a.kind, b.kind, Dog.kind)print(sorted(a.__dict__))
  1. husky husky husky, y ['name']
  2. husky canine canine, y ['kind', 'name']
  3. husky canine canine, y ['name']
  4. husky husky husky, y ['kind', 'name']
RespuestaB. husky canine canine, y ['kind', 'name']

Asignar a a.kind no reescribe la variable de clase; crea una nueva entrada llamada kind en el diccionario de la instancia a. A partir de entonces, al leer a.kind se encuentra primero esta nueva entrada, que hace sombra a la variable de clase (クラス変数). b y Dog.kind siguen siendo el canine original. Así, en a.__dict__ aparece kind junto a name, que puso __init__, y al ordenarlo resulta ['kind', 'name']. La opción en la que todo pasa a ser husky es la respuesta para el caso en que se piensa por error que asignar en la instancia reescribe la variable de clase, y la opción que da solo ['name'] es la respuesta para el caso en que se piensa que no se crea ninguna sombra.

Pregunta 14 | Compartida

¿Cuál es la salida del siguiente código, que usa una lista como variable de clase?

class Basket:    items = []    def add(self, x):        self.items.append(x)p = Basket()q = Basket()p.add(1)q.add(2)print(p.items, q.items)
  1. p.items es [1], q.items es [1, 2]
  2. p.items es [1, 2], q.items es [2]
  3. p.items es [1, 2], q.items es [1, 2]
  4. p.items es [1], q.items es [2]
RespuestaC. p.items es [1, 2], q.items es [1, 2]

self.items.append(x) no es una asignación a self.items; solo llama a un método del objeto al que se llega mediante self.items. Ni p ni q tienen su propio items, así que ambos llegan a la misma y única lista guardada como variable de clase (クラス変数). Como resultado, tanto el 1 añadido mediante p como el 2 añadido mediante q entran en esa misma lista, y se obtiene [1, 2] se mire desde donde se mire. Si quiere un contenedor separado para cada instancia, debe asignarlo dentro de __init__ escribiendo self.items = []. Así se dividiría en [1] y [2].

Pregunta 15 | super()

¿Cuál es la salida del siguiente código, que usa herencia y super()?

class Animal:    def __init__(self, name):        self.name = name    def cry(self):        return self.name + " ?"class Cat(Animal):    def __init__(self, name, color):        super().__init__(name)        self.color = color    def cry(self):        return self.name + " meow"c = Cat("Tama", "black")print(c.cry(), c.color, isinstance(c, Animal))
  1. Tama meow None True
  2. Tama ? black True
  3. Tama meow black False
  4. Tama meow black True
RespuestaD. Tama meow black True

El __init__ de Cat llama a super().__init__(name), así que la clase padre, Animal, establece self.name. Después, a self.color se le asigna black, así que ambos atributos quedan establecidos. cry se vuelve a definir en la clase hija, así que la implementación de la clase padre queda sobrescrita (上書き), y lo que se llama es la versión meow. isinstance(c, Animal) es True, ya que Cat sí hereda de Animal. Si olvida escribir super(), name nunca se establecería y se produciría un AttributeError, así que no falla en silencio de una forma que dé como resultado None.

Pregunta 16 | MRO

¿Cuál es la salida del siguiente código, que usa herencia múltiple?

class A:    def who(self):        return "A"class B(A):    def who(self):        return "B"class C(A):    def who(self):        return "C"class D(B, C):    passprint(D().who())print([k.__name__ for k in D.__mro__])
  1. B, y ['D', 'B', 'C', 'A', 'object']
  2. C, y ['D', 'C', 'B', 'A', 'object']
  3. B, y ['D', 'B', 'A', 'C', 'object']
  4. A, y ['D', 'B', 'C', 'A', 'object']
RespuestaA. B, y ['D', 'B', 'C', 'A', 'object']

El orden de resolución de métodos (メソッド解決順序) se decide mediante una regla llamada linealización C3, no mediante una simple búsqueda en profundidad. La clase padre común A se coloca después de todas las clases hijas que heredan de A, así que el orden resulta D, B, C, A, object. Si fuera en profundidad, entrando en B y yendo directamente a A, sería D, B, A, C, object, pero no es eso lo que ocurre, y este es el punto en el que hay que tener cuidado. who() busca desde el principio del MRO y usa el primero que encuentra, así que lo que se devuelve es B. Si escribe class E(C, B), resulta E, C, B, A, object; tiene prioridad el que se escribe primero.

Pregunta 17 | Un conflicto de MRO

Con class B(A) ya definida, ¿qué ocurre si escribe class F(A, B): pass?

  1. La definición tiene éxito, y F().who() llama a la versión de A
  2. La definición tiene éxito, y __mro__ resulta F, A, B, object
  3. La definición tiene éxito, y F().who() llama a la versión de B
  4. Se produce un TypeError en el momento de la definición de la clase
RespuestaD. Se produce un TypeError en el momento de la definición de la clase

La linealización C3 tiene dos reglas: conservar el orden en que se enumeran las clases base y colocar a una clase padre después de sus clases hijas. F(A, B) especifica poner A antes que B, pero B es hija de A, así que la clase padre A debe ir después de la hija B. Como estas dos condiciones no pueden cumplirse a la vez, se determina que no se puede construir un MRO (メソッド解決順序) coherente, y se produce un TypeError en el momento en que se evalúa la definición de la clase. Falla en el instante en que se procesa la sentencia class, no solo cuando se intenta ejecutar. Para escribirlo correctamente, hay que escribir primero la clase hija, como en class F(B, A).

Pregunta 18 | Alteración de nombres

¿Cuál es la salida del siguiente código, que tiene atributos que empiezan con guion bajo?

class P:    def __init__(self):        self._one = 1        self.__two = 2p = P()print(p._one, p._P__two)print(sorted(p.__dict__))
  1. 1 2, y ['_one', '_two']
  2. 1 2, y ['_P__one', '_P__two']
  3. 1 2, y ['__two', '_one']
  4. 1 2, y ['_P__two', '_one']
RespuestaD. 1 2, y ['_P__two', '_one']

_one, que empieza con un solo guion bajo, es solo una convención que indica que es de uso interno, y su nombre no cambia; se puede leer normalmente desde fuera. __two, que empieza con dos guiones bajos, sufre la alteración de nombres (名前修飾, name mangling), y su nombre real de atributo se reescribe como _P__two, es decir, _NombreDeClase__nombre. Por eso, escribir p.__two produce un AttributeError, pero escribir p._P__two le permite leerlo. Lo que aparece en __dict__ es también el nombre alterado, así que al ordenarlo resulta ['_P__two', '_one']. Solo se alteran los nombres que empiezan con dos guiones bajos; _one no está sujeto a ello.

Pregunta 19 | Métodos especiales

¿Cuál es la salida del siguiente código, que define métodos especiales?

class Vec:    def __init__(self, x, y):        self.x = x        self.y = y    def __str__(self):        return "V({}, {})".format(self.x, self.y)    def __repr__(self):        return "Vec(x={}, y={})".format(self.x, self.y)    def __len__(self):        return 2    def __add__(self, o):        return Vec(self.x + o.x, self.y + o.y)v = Vec(1, 2)print(v)print([v])print(len(v), v + Vec(3, 4))
  1. Vec(x=1, y=2), y [Vec(x=1, y=2)], y 2 V(4, 6)
  2. V(1, 2), y [Vec(x=1, y=2)], y 2 V(4, 6)
  3. V(1, 2), y [Vec(x=1, y=2)], y 2 Vec(x=4, y=6)
  4. V(1, 2), y [V(1, 2)], y 2 V(4, 6)
RespuestaB. V(1, 2), y [Vec(x=1, y=2)], y 2 V(4, 6)

print(v) y str(v) usan el método especial (特殊メソッド) __str__, así que resulta V(1, 2). Sin embargo, cuando se muestra dentro de un contenedor como una lista o un diccionario, para cada elemento se usa __repr__, así que resulta [Vec(x=1, y=2)]. Esta distinción de usos es el punto clave de esta pregunta. len(v) es el 2 que devuelve __len__, y v + Vec(3, 4) es un nuevo Vec que devuelve __add__; al pasarlo a print se vuelve a usar __str__, así que se muestra V(4, 6). En una clase en la que no está definido __str__, se usa __repr__ como sustituto, pero lo contrario —que se use __str__ en lugar de __repr__— nunca ocurre.

Pregunta 20 | __eq__

¿Cuál es la salida del siguiente código, que define solo __eq__?

class Point:    def __init__(self, x):        self.x = x    def __eq__(self, other):        return self.x == other.xp = Point(1)q = Point(1)print(p == q, p is q)print(Point.__hash__ is None)try:    print(len({p, q}))except TypeError:    print("TypeError")
  1. True False, y False, y 2
  2. True False, y True, y TypeError
  3. True False, y False, y 1
  4. True True, y True, y TypeError
RespuestaB. True False, y True, y TypeError

Como __eq__ está definido, p == q compara el atributo x y da True, pero p y q son objetos creados por separado, así que p is q sigue siendo False. El punto importante aquí es que, en una clase que define __eq__, su __hash__ se establece automáticamente en None, así que Point.__hash__ is None es True. Un objeto sin hash (ハッシュ) no se puede usar en un conjunto ni como clave de un diccionario, así que intentar crear {p, q} produce un TypeError. Si también quiere poder ponerlo en un conjunto, debe definir su propio __hash__ que devuelva el mismo valor para objetos iguales.

Pregunta 21 | math

¿Cuál es la salida del siguiente código, que usa el módulo math?

import mathprint(math.sqrt(16))print(math.floor(-1.5), math.ceil(-1.5))
  1. 4.0, y -2 -1
  2. 4.0, y -1 -2
  3. 4, y -2 -1
  4. 4, y -1 -1
RespuestaA. 4.0, y -2 -1

math.sqrt devuelve un número de punto flotante (浮動小数点数) incluso cuando el resultado es exacto, así que la raíz cuadrada de 16 se muestra no como 4 sino como 4.0. math.floor redondea hacia abajo en la recta numérica, así que -1.5 resulta -2, y math.ceil redondea hacia arriba, así que -1.5 resulta -1. Tenga en cuenta que, con números negativos, int(-1.5), que redondea hacia 0, da -1, lo que no coincide con el resultado de floor. Si confunde floor y ceil, obtendrá el orden -1 -2, y si supone que el tipo de retorno de sqrt es int, obtendrá 4.

Pregunta 22 | os.path

¿Cuál es la combinación de valores que devuelven os.path.splitext('/tmp/z.txt') y os.path.basename('/tmp/z.txt')?

  1. ('/tmp/z', '.txt'), y 'z.txt'
  2. ('/tmp', 'z.txt'), y '/tmp'
  3. ('/tmp/z', 'txt'), y 'z.txt'
  4. ('/tmp/z.txt', ''), y 'z.txt'
RespuestaA. ('/tmp/z', '.txt'), y 'z.txt'

os.path.splitext separa solo la extensión (拡張子), dejando el punto unido al lado de la extensión, y devuelve una tupla de 2 elementos. Por eso resulta ('/tmp/z', '.txt'). No ocurre que se pierda el punto y quede 'txt'. os.path.basename devuelve el nombre del final de la ruta, así que es 'z.txt'. Lo que devuelve el lado del directorio, '/tmp', es en cambio os.path.dirname, y cuando quiere una tupla con ambos juntos, se usa os.path.split. Conviene recordar la distinción de que splitext divide por la extensión, no por un separador de directorios.

Pregunta 23 | Coincidencias con re

¿Cuál es la salida del siguiente código, que usa el módulo re?

import reprint(re.match("b", "abc"))print(re.search("b", "abc").group())print(re.findall(r"\d+", "a1b22c333"))
  1. b, y None, y ['1', '22', '333']
  2. None, y b, y ['1', '22', '333']
  3. b, y b, y ['1', '22', '333']
  4. None, y b, y ['a1', 'b22', 'c333']
RespuestaB. None, y b, y ['1', '22', '333']

re.match solo busca coincidencias desde el principio de la cadena, así que, como el principio de 'abc' es 'a', no coincide con 'b' y devuelve None. re.search busca incluso en medio de la cadena, así que coincide con 'b', y .group() extrae la cadena coincidente b. Esta diferencia es el punto que más se pregunta sobre re. re.findall devuelve una lista que reúne todas las partes que coinciden con el patrón (パターン), y como \d+ significa dígitos consecutivos, resulta ['1', '22', '333']. Los caracteres separadores no se incluyen. Si no hay coincidencias, devuelve una lista vacía.

Pregunta 24 | datetime

¿Cuál es la salida del siguiente código, que usa datetime?

import datetimed = datetime.date(2026, 8, 22)print(d)print(d.strftime("%Y/%m/%d"))print(d + datetime.timedelta(days=10))
  1. 2026/08/22, y 2026/08/22, y 2026/09/01
  2. 2026-08-22, y 2026/08/22, y 2026-09-01
  3. 2026-08-22, y 2026/08/22, y 10 days, 0:00:00
  4. 2026-08-22, y 2026/08/22, y 2026-08-32
RespuestaB. 2026-08-22, y 2026/08/22, y 2026-09-01

Al imprimir directamente un objeto date, se muestra en formato ISO, con guiones, como 2026-08-22. strftime es lo que se usa para cambiar el formato, y si se le pasa '%Y/%m/%d', devuelve la cadena separada por barras 2026/08/22. Sumar timedelta(days=10) a una fecha no devuelve un timedelta, sino una nueva fecha que apunta a 10 días después, así que al imprimirla se obtiene 2026-09-01. El cálculo que cruza el límite de un mes lo resuelve datetime, así que no resulta una fecha inexistente como 2026-08-32, que es lo que obtendría si simplemente sumara 10 al número del día. Si piensa que print(d) usa el mismo formato que strftime, obtendría una salida separada por barras, pero la visualización predeterminada usa guiones. Cuando resta una fecha de otra, lo que se devuelve no es una fecha sino un timedelta, y su visualización toma una forma como 10 days, 0:00:00.

Pregunta 25 | Counter

¿Cuál es la salida del siguiente código, que usa collections.Counter?

from collections import Counterc = Counter("abracadabra")print(c.most_common(2))print(c["a"], c["z"])
  1. [('a', 5), ('b', 2)], y 5, y un KeyError
  2. [('a', 5), ('r', 2)], y 5 0
  3. [('a', 5), ('b', 2)], y 5 0
  4. [('a', 5), ('b', 2), ('r', 2)], y 5 0
RespuestaC. [('a', 5), ('b', 2)], y 5 0

'abracadabra' contiene a 5 veces, b y r 2 veces cada una, y c y d una vez cada una. most_common(2) devuelve solo los 2 primeros por número de apariciones, como una lista de tuplas de elemento y recuento, así que resulta [('a', 5), ('b', 2)]. Entre b y r, que empatan en número de apariciones, va primero la que apareció antes, así que se elige b. Counter es un tipo de diccionario (辞書), pero consultar un elemento que no está presente no produce KeyError; devuelve 0, lo que es una diferencia respecto a un diccionario ordinario. Gracias a este diseño, no hace falta comprobar si existe antes de contar.

Pregunta 26 | namedtuple

¿Cuál es la salida del siguiente código, que usa un tipo creado con namedtuple?

from collections import namedtuplePt = namedtuple("Pt", "x y")p = Pt(1, 2)print(p)print(p.x, p[0], isinstance(p, tuple))
  1. (1, 2), y 1 1 True
  2. Pt(x=1, y=2), y 1 1 False
  3. Pt(x=1, y=2), y 1 1 True
  4. Pt(1, 2), y 1 1 False
RespuestaC. Pt(x=1, y=2), y 1 1 True

Un tipo creado con namedtuple se muestra con la forma Pt(x=1, y=2), con el nombre del tipo y los nombres de los campos. El valor se puede leer tanto por nombre como por índice, así que p.x y p[0] son ambos 1. Además, como namedtuple es un subtipo de tuple, isinstance(p, tuple) es True. Es decir, es un tipo de tupla (タプル), y sus elementos no se pueden reescribir después. Si quiere guardar un valor que se pueda cambiar, use _replace, que crea una nueva instancia con un valor sustituido, o defina una clase ordinaria.

Pregunta 27 | De un solo uso

¿Cuál es la salida del siguiente código, que pasa dos veces el mismo objeto map a list()?

m = map(str, [1, 2, 3])print(list(m))print(list(m))
  1. La línea 1 es ['1', '2', '3'] y la línea 2 es None
  2. La línea 1 es ['1', '2', '3'] y la línea 2 es []
  3. La línea 1 es [1, 2, 3] y la línea 2 es []
  4. Tanto la línea 1 como la línea 2 son ['1', '2', '3']
RespuestaB. La línea 1 es ['1', '2', '3'] y la línea 2 es []

Lo que devuelve map no es una lista sino un iterador (イテレータ); cada llamada a next() entrega el siguiente valor, y una vez agotado, se acabó. Como la primera llamada a list() saca los 3 valores, a la segunda llamada a list() no le queda nada y resulta una lista vacía. No se produce un error ni se obtiene None; simplemente queda vacía en silencio, lo que hace que sea fácil pasarlo por alto. map(str, ...) convierte cada elemento en una cadena, así que el primer resultado no son números sino ['1', '2', '3']. Lo mismo ocurre con filter, zip, enumerate y los generadores, así que, si piensa recorrerlo más de una vez, fíjelo en una lista con list() justo después de recibirlo.

Pregunta 28 | filter y all

¿Cuál es la salida del siguiente código, que usa filter, all y any?

print(list(filter(None, [0, 1, "", 2, None, "a"])))print(all([]), any([]))
  1. [1, 2, 'a'], y False False
  2. [0, '', None], y True False
  3. [1, 2, 'a'], y True False
  4. [1, 2, 'a'], y False True
RespuestaC. [1, 2, 'a'], y True False

Si se pasa None como primer argumento de filter, se filtra según el valor de verdad (真偽) del propio elemento. 0, la cadena vacía y None se evalúan como falsos, así que se descartan, y lo que queda es [1, 2, 'a']. No ocurre que queden los descartados. all está especificado para devolver True siempre que no haya ni un solo elemento falso, así que, para una lista vacía, sin ningún elemento, es True. any devuelve True si hay al menos un elemento verdadero, así que, para una vacía, es False. El punto de que estas dos dan respuestas opuestas cuando se les pasa una secuencia vacía se pregunta con frecuencia.

Pregunta 29 | Un generador

¿En qué orden se imprimen las líneas en el siguiente código, que llama a una función que contiene yield?

def gen():    print("start")    yield 1    print("mid")    yield 2g = gen()print("created")print(next(g))print(next(g))
  1. En el orden start mid created 1 2
  2. En el orden created start mid 1 2
  3. En el orden created start 1 mid 2
  4. En el orden start created 1 mid 2
RespuestaC. En el orden created start 1 mid 2

Llamar a una función que contiene yield no ejecuta ni una sola línea de su cuerpo. Lo que se devuelve es solo un objeto generador (ジェネレータ), así que primero se imprime created. Solo con el primer next(g) empieza por fin a moverse desde arriba, imprime start, luego se detiene en yield 1 y devuelve 1. El siguiente next(g) se reanuda desde donde se detuvo, imprime mid y luego se detiene en yield 2, devolviendo 2. Esta ejecución diferida es la diferencia decisiva respecto a una función ordinaria, y llamar a next(g) una vez más termina la función, lo que produce StopIteration.

Pregunta 30 | is y ==

¿Cuál es la salida del siguiente código, que pone uno junto al otro el operador is y el operador ==?

a = [1, 2]b = [1, 2]c = aprint(a == b, a is b)print(a == c, a is c)
  1. True False, y True False
  2. True True, y True True
  3. False False, y True True
  4. True False, y True True
RespuestaD. True False, y True True

== es el operador que pregunta si los valores son iguales, así que, como a y b tienen el mismo contenido, da True. is es el operador que pregunta si se trata del mismo objeto (オブジェクト), y como a y b son dos listas creadas por separado, da False. En cambio, c = a no crea una lista nueva; solo da otro nombre al mismo objeto, así que a is c es True. Es decir, si se modifica la lista a través de c, el cambio también se ve a través de a. None es un objeto especial que existe una sola vez, así que la forma idiomática es escribir x is None, pero cuando quiera comparar valores, use siempre ==.

Práctica: responda las preguntas de esta página

Es una herramienta de práctica con preguntas en orden aleatorio (funciona si JavaScript está activado). Aun sin ella, puede leer todas las preguntas y explicaciones de arriba.

* Las explicaciones son información con fines de estudio. El contenido y el sistema de los exámenes cambian cada año, así que consulta siempre los anuncios oficiales de la entidad organizadora.

Esta página es una traducción del texto original en japonés. Si hay diferencias entre la traducción y el original, prevalece la versión en japonés. Esta traducción se ha hecho con IA y puede contener errores. Informar de un error de traducción Ver el original en japonés