Los argumentos posicionales (位置引数) se asignan en orden desde la izquierda, así que a recibe 1 y b recibe 3. b tiene un valor por defecto (デフォルト値) de 2, pero como se le pasó un valor, ese valor tiene prioridad. Los valores 4 y 5, que no tienen correspondencia, se agrupan en args, marcado con un solo asterisco, como una tupla, lo que da (4, 5). No se pasó ningún argumento por palabra clave, así que kwargs es el diccionario vacío {}, no None. Si piensa que b conserva el valor por defecto 2, obtendría (1, 2, (3, 4, 5), {}), pero un argumento posicional tiene prioridad sobre el valor por defecto. Lo que entra en args es una tupla, no una lista, así que tampoco se muestra entre corchetes.
Pregunta 2 | Especificación por palabra clave
En la siguiente llamada se pasó por palabra clave un nombre que no está en la definición. ¿Qué se muestra?
Como solo se da 1 como argumento posicional, a recibe 1, y como b no se da, se queda con su valor por defecto 2. Como en la definición no hay ningún parámetro formal (仮引数) llamado x, el argumento por palabra clave (キーワード引数) x=9 entra en kwargs, marcado con doble asterisco, como {'x': 9}. No sobra ningún argumento posicional, así que args es la tupla vacía (). Si piensa que x=9 entra en b, obtendría (1, 9, (), {}), pero un argumento por palabra clave solo entra en el parámetro formal cuyo nombre coincide. Si piensa que el 9 entra en args, obtendría (1, 2, (9,), {}), pero un valor pasado por nombre nunca pasa a formar parte de los argumentos posicionales sobrantes. Si confunde qué asterisco recibe un diccionario y cuál recibe una tupla, podría parecerle que {'x': 9} entró en args y que kwargs quedó vacío.
Pregunta 3 | Una lista vacía como valor por defecto
Se llamó 3 veces a la siguiente función, que tiene una lista vacía como valor por defecto. ¿Cuáles son las 3 líneas que se muestran?
De arriba abajo se muestra [1] / [1, 2] / [1, 2, 3]
De arriba abajo se muestra [1] / [2] / [3]
De arriba abajo se muestra [1] / [1, 2] / [2, 3]
RespuestaB. De arriba abajo se muestra [1] / [1, 2] / [1, 2, 3]
El valor por defecto (デフォルト値) [] se crea una sola vez, cuando se ejecuta la línea def, y esa misma lista se guarda en el objeto función. No se crea una nueva lista vacía en cada llamada, así que los resultados de append se conservan para la siguiente llamada, y la lista crece como [1], [1, 2], [1, 2, 3]. Si piensa que se crea una lista nueva en cada llamada, obtendría [1] / [2] / [3], pero ese es el comportamiento cuando se usa lst=None como valor por defecto y se crea una lista nueva en el cuerpo. Si por error escribe el cuerpo como return lst.append(x), se imprime 3 veces seguidas None, el valor de retorno de append. Además, si en lugar de repartirlo en 3 líneas lo escribe en una sola línea como print(add(1), add(2), add(3)), las tres hacen referencia a la misma lista, así que se imprime 3 veces seguidas [1, 2, 3], el estado después de terminar todas las evaluaciones.
Pregunta 4 | Valor por defecto compartido
Tras 3 llamadas se mostró un atributo del objeto función. ¿Qué se muestra?
Un objeto función guarda sus valores por defecto (デフォルト値) en un atributo llamado __defaults__, en forma de tupla. De los parámetros formales, solo lst tiene valor por defecto, así que es una tupla de un elemento y se muestra con una coma final. Su contenido es precisamente la lista creada una sola vez en la línea def, y como en ella quedan tal cual los resultados de los append de las 3 llamadas, resulta ([1, 2, 3],). Antes de cualquier llamada sería ([],), pero que el efecto de las llamadas aparezca aquí es lo peligroso de un argumento por defecto mutable. __defaults__ en sí es una tupla, así que no se muestra entre corchetes, y la lista que contiene tampoco se convierte en tupla.
Pregunta 5 | Evaluación del valor por defecto
Se escribió una variable como valor por defecto y después se reasignó esa variable. ¿Cuáles son las 2 líneas que se muestran?
i = 5def h(x=i): return xi = 10print(h())print(h(i))
5 y luego 5
5 y luego 10
10 y luego 10
10 y luego 5
RespuestaB. 5 y luego 10
La expresión del valor por defecto (デフォルト値) no se evalúa cada vez que se llama a la función; se evalúa una sola vez, en el momento en que se ejecuta la línea def. En ese momento i vale 5, así que lo que se guarda en el objeto función es 5. Aunque después i se reasigne a 10, el valor por defecto ya guardado no cambia, así que h() devuelve 5. En cambio, h(i) lee i en el momento de la llamada y lo pasa, así que devuelve el valor de ese momento, 10. Si piensa que el valor por defecto se evalúa en cada llamada, ambas líneas serían 10, y si piensa que un valor pasado como argumento también reemplaza el valor por defecto, ambas líneas serían 5. La trampa del argumento por defecto mutable surge exactamente de esta misma regla.
Pregunta 6 | El orden de los argumentos
El siguiente código se guardó en un archivo y se ejecutó con python3. ¿Qué ocurre?
def g(a=1, b): return a + bprint(g(2, 3))
Se produce un SyntaxError en el momento de la definición y no se ejecuta ni una sola línea
Se muestra 4 y se ejecuta normalmente hasta el final
Se produce un TypeError en el momento de la llamada y la ejecución se detiene ahí
Se muestra 5 y se ejecuta normalmente hasta el final
RespuestaA. Se produce un SyntaxError en el momento de la definición y no se ejecuta ni una sola línea
No se puede colocar un parámetro formal (仮引数) sin valor por defecto después de uno que tiene valor por defecto (デフォルト値). Esta regla se comprueba en la etapa de sintaxis, así que al ejecutar el archivo se produce primero un SyntaxError, con el mensaje parameter without a default follows parameter with a default (en 3.11 y anteriores: non-default argument follows default argument). Como el análisis sintáctico se realiza sobre todo el archivo antes de la ejecución, no se ejecuta ni siquiera la línea de print. Es una etapa de detección completamente distinta de la de un TypeError causado por un error en la forma de llamar. g(2, 3) en sí coincide con el número de parámetros formales, así que, si la definición fuera correcta, se esperaría que se mostrara 5, pero nunca se llega a ese punto. Para escribirlo correctamente, hay que colocar después el que tiene valor por defecto, como en def g(b, a=1).
Pregunta 7 | El valor de retorno de una función
Se llamó de la siguiente manera a dos funciones que se diferencian en si tienen o no return. ¿Qué se muestra?
one termina sin pasar nunca por un return, así que su valor de retorno (戻り値) es None. No devuelve una tupla vacía ni una cadena vacía. two escribe 1 y 2 separados por una coma después de return, pero esto no devuelve dos valores, sino una sola tupla, (1, 2). Por tanto, type(two()).__name__ es tuple. Si piensa que varios valores de retorno se agrupan en una lista, obtendría list, pero una expresión con valores separados por comas crea una tupla. Cuando alguien tropieza al intentar asignar y usar el valor de retorno de una función que solo muestra algo, la causa suele ser este None.
Pregunta 8 | Desempaquetado en la llamada
El siguiente código llama a una función poniendo un asterisco delante de una lista. ¿Qué se muestra?
Cuando se pone un solo asterisco delante de una lista en el lado de la llamada, su contenido se reparte uno por uno como argumentos posicionales (位置引数). Así, a recibe 1 y b recibe 2, y el 3 sobrante se agrupa en args, marcado con un solo asterisco, como una tupla, lo que da (3,). Como es una tupla de un elemento, lleva una coma final. No se pasó ningún argumento por palabra clave, así que kwargs es el diccionario vacío {}, no None. Si olvida el asterisco y escribe f(vals), la lista misma entra en el primer parámetro formal y se obtiene ([1, 2, 3], 2, (), {}), con lo que el resultado cambia sin que se produzca ningún error. Lo que entra en args es una tupla, así que tampoco se muestra entre corchetes.
Pregunta 9 | Cómo se tratan las anotaciones
Se llamó a la siguiente función con un valor de un tipo distinto del escrito en su anotación. ¿Qué ocurre?
En la línea 1 se imprime 文字列 y en la línea 2 se imprime <class 'int'>
Se produce un TypeError en la línea 1, que informa de que no se puede pasar un tipo distinto del de la anotación
En la línea 1 se imprime 文字列 y en la línea 2 se imprime int
Se produce un ValueError en la línea 1, que informa de que no se puede convertir a int
RespuestaA. En la línea 1 se imprime 文字列 y en la línea 2 se imprime <class 'int'>
Una anotación (アノテーション) simplemente se registra en el diccionario __annotations__ del objeto función; el tipo no se comprueba en tiempo de ejecución. Aunque a esté anotado como int, se puede pasar una cadena, y aunque b esté anotado como str, se puede pasar un entero. Aunque el valor de retorno esté anotado como bool, lo que realmente se devuelve es la cadena recibida, tal cual. Por eso, en la línea 1 se imprime 文字列 tal cual. Lo que se guarda en __annotations__['a'] en la línea 2 es el objeto de tipo int, así que al imprimirlo se muestra con la forma <class 'int'>. No se imprimen solo los 3 caracteres int. Si realmente quiere comprobar tipos, necesita usar una herramienta de comprobación de tipos aparte o comprobarlo usted mismo en el cuerpo.
Pregunta 10 | Cómo corregir el valor por defecto
El siguiente código se reescribió con None como valor por defecto. ¿Cuáles son las 3 líneas que se muestran?
def add2(x, lst=None): if lst is None: lst = [] lst.append(x) return lstprint(add2(1))print(add2(2))print(add2(3, [9]))
De arriba abajo se muestra [1] / [1, 2] / [9, 3]
De arriba abajo se muestra [1] / [2] / [9, 3]
De arriba abajo se muestra [1] / [2] / [3, 9]
De arriba abajo se muestra [1] / [2] / [3]
RespuestaB. De arriba abajo se muestra [1] / [2] / [9, 3]
El patrón estándar es poner None como valor por defecto (デフォルト値) y luego comprobar al principio del cuerpo si es None y, en ese caso, crear una nueva lista vacía. Como None es un valor que no se puede modificar, no surge ningún problema de compartición aunque se evalúe una sola vez. En la primera y la segunda llamada, que omiten lst, se crea cada vez una lista vacía nueva, así que resultan [1] y [2], sin que quede ningún elemento de la llamada anterior. En la tercera llamada, quien llama pasa [9], así que lst is None es falso, y 3 se añade al final de la lista recibida, lo que da [9, 3]. Si se dejara como lst=[] antes de la reescritura, al omitirlo en la segunda llamada quedarían los elementos de la llamada anterior y resultaría [1, 2]. Si se ignora la lista que pasa quien llama y siempre se crea una lista nueva, se obtiene [1] / [2] / [3], y se pierde la ventaja de este patrón: usar lo que se recibe cuando se recibe. Si en lugar de añadir al final se inserta al principio con insert(0, x), la tercera llamada resulta [3, 9].
Pregunta 11 | Orden de búsqueda de nombres
¿Cuál es el orden correcto en que la regla LEGB de Python busca un nombre de variable?
En el orden: función en ejecución, nivel superior del módulo, función envolvente, integrados
En el orden: función en ejecución, función envolvente, nivel superior del módulo, integrados
En el orden: nivel superior del módulo, integrados, función en ejecución, función envolvente
En el orden: integrados, nivel superior del módulo, función envolvente, función en ejecución
RespuestaB. En el orden: función en ejecución, función envolvente, nivel superior del módulo, integrados
LEGB significa Local, Enclosing, Global, Built-in, y representa una búsqueda (探索) desde lo más interno hacia afuera. Primero busca dentro de la función en ejecución, luego dentro de la función que la envuelve, después en el nivel superior del módulo y, por último, en los integrados como len y print. La búsqueda termina en cuanto encuentra algo, así que, si se usa el mismo nombre en el interior, el nombre exterior queda oculto. Buscar de afuera hacia adentro significaría que una variable local creada dentro de una función perdería frente a una variable de módulo del mismo nombre, lo que no coincide con el comportamiento real. También es frecuente el error de intercambiar el orden de Global y Enclosing, pero la función envolvente está más hacia adentro que el nivel superior del módulo.
Pregunta 12 | Una asignación local
El siguiente código asigna, desde dentro de una función, al mismo nombre que el de afuera. ¿Cuáles son las 2 líneas que se muestran?
x = 'global'def f(): x = 'local' print(x)f()print(x)
local y luego local
local y luego global
global y luego global
global y luego local
RespuestaB. local y luego global
Asignar a un nombre dentro de una función crea ese nombre como una nueva variable local (ローカル変数), y la variable de módulo del mismo nombre no se ve afectada en absoluto. print(x) dentro de la función lee la x local, así que se imprime local, pero print(x) después de que la función termina lee la x del módulo, así que se imprime global. Si piensa que la asignación dentro de la función llega al exterior, ambas líneas serían local, pero para eso haría falta una declaración global x al principio de la función. Al contrario, si piensa que la asignación dentro de la función se ignora y se lee el valor exterior, ambas líneas serían global, pero en cuanto se hace una asignación se crea efectivamente una variable local. Esta asimetría —la lectura llega hacia afuera, pero la escritura no— es la regla básica del ámbito (スコープ) de Python.
Pregunta 13 | nonlocal
En el siguiente código, la función interna intenta reescribir la variable externa. ¿Qué se muestra?
def outer(): n = 1 def inner(): nonlocal n n = 2 inner() return ndef outer2(): n = 1 def inner(): n = 2 inner() return nprint(outer(), outer2())
1 2
2 2
2 1
1 1
RespuestaC. 2 1
Como el inner de outer tiene escrito nonlocal n, la asignación a n llega a la n de la función envolvente outer. La n que devuelve outer después de llamar a inner es 2. Como el inner de outer2 no tiene esa declaración, n = 2 solo crea una nueva variable local (ローカル変数) de inner y ahí termina, así que la n de outer2 se queda en 1. Por tanto, la salida es 2 1. Si piensa que la asignación llega en ambos casos, obtendría 2 2, y si piensa que no llega en ninguno, obtendría 1 1, pero lo que marca la diferencia es esa única línea de nonlocal. Tenga en cuenta también que nonlocal no se puede usar si no existe ya una variable del mismo nombre en una función envolvente, y tampoco puede apuntar a una variable del nivel superior del módulo. Cuando quiera reescribir una variable del nivel superior, use global.
Pregunta 14 | Error de variable sin asignar
Se leyó en una línea anterior un nombre que se asigna en una línea posterior. ¿Qué ocurre al ejecutar el siguiente código?
z = 100def bad(): print(z) z = 1bad()
Se produce un UnboundLocalError, no se imprime nada y se detiene
Se imprime 100 y se ejecuta normalmente hasta el final
Se imprime None y se ejecuta normalmente hasta el final
Se imprime 1 y se ejecuta normalmente hasta el final
RespuestaA. Se produce un UnboundLocalError, no se imprime nada y se detiene
Cuando Python compila el cuerpo de una función, decide que cualquier nombre que tenga una asignación en cualquier parte de esa función es, en su totalidad, una variable local (ローカル変数). Esta determinación se hace para la función en su conjunto, y el orden de las líneas no importa. En esta función bad hay una asignación z = 1 en una línea posterior, así que z se trata como variable local de esta función. Entonces, en el momento de print(z), todavía no se le ha asignado ningún valor, y se produce un UnboundLocalError. El texto del mensaje de esta excepción puede variar según la versión de Python, pero el nombre del tipo, UnboundLocalError, no cambia. No se hace referencia a la z = 100 del nivel superior. Si piensa que, por estar en una línea anterior a la asignación, se lee el valor exterior, obtendría 100, pero Python no se comporta así. Una variable local sin asignar tampoco se convierte en None.
Pregunta 15 | La declaración global
El siguiente código pone una función con declaración global junto a otra sin ella. ¿Qué se muestra?
n = 10def f(): global n n = 20 return ndef g(): n = 30 return nprint(f(), g(), n)
20 30 30
20 30 20
20 30 10
10 30 10
RespuestaB. 20 30 20
f tiene escrito global n, así que n = 20 reescribe la n del nivel superior del módulo. f devuelve 20, y la n del nivel superior también pasa a ser 20. g no tiene esa declaración, así que n = 30 solo crea una variable local (ローカル変数) en g y no afecta a la n del nivel superior. g devuelve su propio 30 local. La n que lee el print final es la del nivel superior, y como f ya la reescribió, vale 20. Si piensa que incluso la asignación de g llega al exterior, obtendría 20 30 30, y si piensa que global no tiene efecto, obtendría 20 30 10. Una declaración global (global宣言) es una instrucción que dice "envía al nivel superior las asignaciones a este nombre"; tenga en cuenta que la declaración no es necesaria si solo va a leer el valor.
Pregunta 16 | El efecto de __all__
Se preparó el siguiente mymod.py y en otro archivo se escribió from mymod import *. ¿Qué nombres se importan en el lado que llama?
Una importación con asterisco normalmente importa todos los nombres que no empiezan con guion bajo, pero si el módulo (モジュール) tiene una lista de cadenas llamada __all__, se limita solo a los nombres que figuran en ella. En este ejemplo, __all__ solo incluye pub, así que no se importan ni other ni hello. other no empieza con guion bajo, pero una vez que existe __all__, su lista tiene prioridad. Ser una función tampoco le da un trato especial, así que hello tampoco se importa. __all__ solo tiene efecto en una importación con asterisco; no impide una importación que nombra un nombre concreto, como from mymod import hello, ni el método de hacer import mymod y luego mymod.hello().
Pregunta 17 | El valor de __name__
Para el siguiente mymod2.py, ¿cuál es la combinación de valores de __name__ cuando se ejecuta directamente con python3 mymod2.py y cuando otro archivo hace import mymod2?
La ejecución directa da mymod2 y la importación da __main__
Tanto la ejecución directa como la importación dan __main__
La ejecución directa da __main__ y la importación da mymod2
Tanto la ejecución directa como la importación dan mymod2
RespuestaC. La ejecución directa da __main__ y la importación da mymod2
Cuando se ejecuta un archivo directamente con python3, se pone '__main__' en __name__. Cuando se importa desde otro archivo, se pone en su lugar el nombre del archivo sin extensión, 'mymod2'. Por eso, al ejecutarlo directamente se imprimen las 2 líneas mymod2: __main__ y 直接実行 ("ejecución directa"), y al importarlo solo se imprime 1 línea, mymod2: mymod2. if __name__ == '__main__': es la expresión idiomática estándar que aprovecha esta diferencia para separar el "procesamiento que solo debe ejecutarse cuando este archivo se ejecuta directamente". Si la combinación estuviera al revés, el código destinado a comprobar el funcionamiento también se ejecutaría al importar, lo que anularía el propósito de esta expresión. Si ambos casos dieran el mismo valor, esta separación ni siquiera sería posible.
Pregunta 18 | sys.argv
Cuando el siguiente args.py se ejecuta con el comando python3 args.py foo bar, ¿cuáles son las 2 líneas que se muestran?
sys.argv es una lista que reúne los valores pasados desde la línea de comandos (コマンドライン引数), y el propio nombre del script ocupa la primera posición. En este ejemplo es una lista de 3 elementos, como ['args.py', 'foo', 'bar'], así que su len es 3. sys.argv[1:], cortada desde el índice 1 en adelante, resulta ['foo', 'bar'], sin el nombre del script. Si piensa que la cantidad de argumentos pasados es directamente la longitud, obtendría 2, pero eso pasa por alto que se cuenta el nombre del script al principio. Pensar que sys.argv[1:] resulta ['args.py', 'foo'] ocurre si se equivoca en la posición inicial de la rebanada. Si lo ejecuta sin pasar ningún argumento, la longitud es 1 y sys.argv[1:] es una lista vacía.
Pregunta 19 | Volver a cargar un módulo
¿Cuál es el comportamiento correcto cuando se importa dos veces el mismo módulo dentro de un mismo programa?
La segunda vez, el cuerpo del módulo se vuelve a leer desde el principio y se ejecuta otra vez
La segunda vez, se crea un objeto distinto y no se comparte ningún estado
La segunda vez, se usa la caché y el cuerpo del módulo no se vuelve a ejecutar
La segunda vez, se produce un ImportError, porque el mismo nombre no se puede usar más de una vez
RespuestaC. La segunda vez, se usa la caché y el cuerpo del módulo no se vuelve a ejecutar
Un módulo (モジュール) que ya se importó una vez se guarda en caché en un diccionario llamado sys.modules. La segunda importación solo vincula el nombre al mismo objeto módulo ya registrado allí, así que ni se vuelve a leer el archivo ni se vuelve a ejecutar el cuerpo. Puede comprobarlo poniendo un print en el nivel superior del módulo y viendo que se muestra una sola vez aunque lo importe dos veces. Aunque lo importe con otro alias, apunta al mismo objeto. Por eso, si reescribe una variable de algún módulo, el valor reescrito también es visible desde cualquier otro lugar que importe ese módulo. Importar dos veces el mismo nombre no provoca un error, y si durante el desarrollo quiere volver a cargar el cuerpo, debe usar explícitamente una función de importlib.
Pregunta 20 | Comprobar si se está en un entorno virtual
¿Cuál es una forma adecuada de determinar si el Python que se está ejecutando está dentro de un entorno virtual creado con venv?
Si sys.prefix no está definido, se puede juzgar que está dentro de un entorno virtual
Si sys.prefix y sys.base_prefix coinciden, se puede juzgar que está dentro de un entorno virtual
Si sys.base_prefix está definido, se puede juzgar que está dentro de un entorno virtual
Si sys.prefix y sys.base_prefix difieren, se puede juzgar que está dentro de un entorno virtual
RespuestaD. Si sys.prefix y sys.base_prefix difieren, se puede juzgar que está dentro de un entorno virtual
sys.prefix indica dónde está instalado el Python en ejecución, y sys.base_prefix indica dónde está el Python del que deriva. Fuera de un entorno virtual (仮想環境), ambos tienen el mismo valor, pero dentro de un entorno creado con python3 -m venv, sys.prefix pasa a apuntar al directorio del entorno creado, mientras que sys.base_prefix sigue apuntando al Python original, así que los dos acaban siendo distintos. La existencia de esta diferencia es la clave para determinarlo. Decir que, si coinciden, se está dentro de un entorno virtual es la condición inversa. sys.base_prefix siempre está definido, incluso fuera de un entorno virtual, así que no se puede juzgar por si está definido o no. sys.prefix también está siempre definido. Las rutas concretas y la versión de pip varían según el entorno, así que lo único que vale la pena memorizar es esta relación.
Pregunta 21 | El valor de retorno de write
El siguiente código captura el valor de retorno del método write. ¿Qué se muestra?
with open('/tmp/sample.txt', 'w') as f: n = f.write("one\ntwo\nthree\n")print(n)
Se muestra None
Se muestra 3
Se muestra 14
Se muestra 11
RespuestaC. Se muestra 14
write devuelve como valor de retorno (戻り値) el número de caracteres que escribió. En "one\ntwo\nthree\n", one tiene 3 caracteres, two tiene 3 y three tiene 5, en total 11, y a eso se suman los 3 saltos de línea, lo que da un total de 14 caracteres. Si olvida contar los saltos de línea, obtendría 11. Si piensa que se devuelve el número de líneas escritas, obtendría 3, pero lo que cuenta write es estrictamente el número de caracteres. Elegir None suponiendo que no devuelve nada también es un error frecuente, fácil de confundir con métodos como append o sort, que devuelven None, pero write devuelve una cantidad. Además, write no añade automáticamente un salto de línea, así que, si quiere separar líneas, debe escribirlo usted mismo en la cadena.
Pregunta 22 | readlines
Se leyó con readlines un archivo en el que se escribieron 3 líneas. ¿Qué se muestra?
with open('/tmp/sample.txt', 'w') as f: f.write("one\ntwo\nthree\n")with open('/tmp/sample.txt') as f: print(f.readlines())
Se muestra ['one\ntwo\nthree\n']
Se muestra 'one\ntwo\nthree\n'
Se muestra ['one\n', 'two\n', 'three\n']
Se muestra ['one', 'two', 'three']
RespuestaC. Se muestra ['one\n', 'two\n', 'three\n']
readlines devuelve todas las líneas como una lista, pero el carácter de salto de línea (改行) al final de cada elemento se conserva tal cual. Por eso resulta ['one\n', 'two\n', 'three\n']. Si piensa que se eliminan los saltos de línea, obtendría ['one', 'two', 'three'], pero para eso hay que aplicar rstrip a cada elemento. El que devuelve todo como una sola cadena, sin dividir, es read; readlines devuelve una lista. Si piensa que trata todo el archivo como un solo elemento, sin dividir por los saltos de línea, obtendría ['one\ntwo\nthree\n'], pero readlines crea un nuevo elemento en cada salto de línea. Cuando escribe for line in f y saca las líneas una por una, la línea que recibe también conserva el salto de línea.
Pregunta 23 | Una segunda lectura
Se leyó dos veces seguidas del mismo objeto archivo. ¿Qué se muestra?
with open('/tmp/sample.txt') as f: a = f.read() b = f.read()print(len(a), b == '')
Se muestra 14 False
Se muestra 14 True
Se muestra 0 False
Se muestra 0 True
RespuestaB. Se muestra 14 True
Un objeto archivo (ファイルオブジェクト) recuerda su posición actual. La primera read lee todo desde el principio hasta el final, así que a contiene 14 caracteres, incluidos los saltos de línea, y la posición pasa al final. La segunda read intenta leer desde el final, así que no queda nada y devuelve una cadena vacía. Por tanto, len(a) es 14 y b == '' es True. Si piensa que la segunda vez se devuelve el mismo contenido, b == '' sería False, pero para volver a leer el mismo contenido hay que devolver la posición al principio con f.seek(0). Que len(a) fuera 0 ocurriría si el archivo estuviera vacío o si la posición ya estuviera al final. Es una situación que se confunde fácilmente con un fallo de lectura cuando no se muestra nada, pero la causa es que la posición está al final.
Pregunta 24 | with y close
Se comprobó closed tanto dentro como fuera de un bloque with. ¿Cuáles son las 2 líneas que se muestran?
with open('/tmp/sample.txt') as f: print(f.closed)print(f.closed)
False y luego False
False y luego True
True y luego False
True y luego True
RespuestaB. False y luego True
Dentro de un bloque with, el archivo sigue abierto, así que closed es False. Se cierra automáticamente al salir del bloque, así que closed pasa después a ser True. Por tanto, se muestra False y luego True. El propósito de usar with (with文) es garantizar que este proceso de cierre se ejecute aunque olvide escribirlo, y el archivo se cierra incluso si se produce una excepción dentro del bloque y se sale a mitad de camino. También se pueden abrir varios archivos juntos en una sola sentencia with separándolos con comas, y cada uno se cierra de la misma manera. Intentar leer de un objeto archivo después de cerrarlo produce un ValueError, así que no se puede volver a leer fuera del bloque with.
Pregunta 25 | Modo de escritura
Se abrió 3 veces el mismo archivo para escribir en él y luego se leyó. ¿Qué se muestra?
p = '/tmp/sample.txt'with open(p, 'w') as f: f.write('AAA')with open(p, 'a') as f: f.write('BBB')with open(p, 'w') as f: f.write('C')with open(p) as f: print(f.read())
El contenido del archivo es AAABBB
El contenido del archivo es AAABBBC
El contenido del archivo es C
El contenido del archivo es CAABBB
RespuestaC. El contenido del archivo es C
Abrir con 'w' trunca el archivo a longitud 0 en ese momento, haya o no contenido previo. Siguiendo el orden, el primer 'w' escribe AAA, y el siguiente 'a' es de anexado (追記), así que resulta AAABBB. Pero en el momento en que se abre por tercera vez con 'w', AAABBB se borra y se escribe C, así que al final solo queda C. Si piensa que las 3 aperturas fueron con 'a', obtendría AAABBBC, y si piensa que la última escritura se ignora, obtendría AAABBB. Sobrescribir desde el principio sin truncar es el comportamiento de 'r+', y en ese caso solo se reemplazaría el carácter inicial, como en CAABBB. El truncado ocurre en el momento de abrir, no en el de escribir, así que abrir con 'w' y no escribir nada deja el archivo vacío. Si su intención es añadir, use 'a'; si es rehacerlo, use 'w': exprese su intención mediante el modo.
Pregunta 26 | Una operación después de cerrar
¿Qué ocurre si intenta leer de un objeto archivo después de cerrarlo?
f = open('/tmp/sample.txt')f.close()print(f.read())
Se produce un ValueError, que informa de que es una operación sobre un archivo cerrado
Se muestra una cadena vacía y termina normalmente
Se vuelve a abrir automáticamente y se muestra todo el contenido del archivo
Se produce un OSError, que informa de que el archivo no se puede volver a abrir
RespuestaA. Se produce un ValueError, que informa de que es una operación sobre un archivo cerrado
Llamar a read sobre un objeto archivo después de cerrarlo produce un ValueError, con el mensaje I/O operation on closed file. Como el propio objeto recuerda que fue cerrado, no se vuelve a abrir automáticamente solo porque se necesite. Tampoco devuelve una cadena vacía en el sentido de "no hay nada que leer": ese es el comportamiento de read cuando ya se ha leído todo hasta el final. Algo como FileNotFoundError, para cuando no se encuentra un archivo, es de la familia de OSError, pero la excepción (例外) por operar sobre un archivo cerrado es otra, y es ValueError. Usar una sentencia with evita estructuralmente, desde el principio, la situación de intentar leer después de salir del bloque.
Pregunta 27 | Solo lectura
¿Qué ocurre si intenta escribir en un archivo que se abrió omitiendo el modo?
with open('/tmp/sample.txt', 'w') as f: f.write('data')with open('/tmp/sample.txt') as f: f.write('more')
Se produce un io.UnsupportedOperation, que informa de que no se puede escribir
Se produce un ValueError, que informa de que la especificación del modo es incorrecta
more se añade al final del contenido y termina normalmente
El contenido se reemplaza por more y termina normalmente
RespuestaA. Se produce un io.UnsupportedOperation, que informa de que no se puede escribir
Si se omite el segundo argumento de open, queda en modo de solo lectura (読み込み専用), 'r'. Llamar a write sobre un objeto archivo abierto en solo lectura produce un io.UnsupportedOperation, acompañado del mensaje not writable. Como es un código sintácticamente válido, solo se nota el error de modo cuando realmente se ejecuta. Si quiere sobrescribir, use 'w'; si quiere añadir al final, use 'a'; y si quiere leer y escribir, debe volver a abrirlo con 'r+'. A la inversa, llamar a read sobre un archivo abierto con 'w' o 'a' produce el mismo io.UnsupportedOperation, esta vez con not readable. Se puede pensar en el modo como el lugar donde se declara qué se permite hacer con ese objeto archivo.
Pregunta 28 | seek y tell
El siguiente código lee solo los 5 primeros caracteres y luego comprueba la posición. ¿Qué se muestra?
with open('/tmp/sample.txt') as f: head = f.read(5) pos = f.tell() f.seek(0) print(repr(head), pos, f.tell())
'one\ntwo\nthree\n' 14 0
'one\nt' 5 0
'one\nt' 5 5
'one\nt' 0 0
RespuestaB. 'one\nt' 5 0
Si se pasa un número a read, se lee solo esa cantidad de caracteres. Los 5 primeros caracteres son los 3 caracteres de one, 1 carácter de salto de línea y la t de two, lo que da 'one\nt'. La posición avanza lo que se ha leído, así que el tell inmediatamente posterior devuelve 5. El seek(0) siguiente devuelve la posición (位置) al principio, así que el tell final es 0. Si piensa que read no mueve la posición, obtendría que tell devuelve 0 ambas veces, y si piensa que seek no tiene efecto, obtendría que tell devuelve 5 ambas veces. Si confunde el argumento de read con un número de líneas en lugar de un número de caracteres, con la intención de leer hasta la línea 5, elegiría por error el archivo completo, 'one\ntwo\nthree\n', y 14. Usar seek(0) le permite volver a leer desde el principio con el mismo objeto archivo.
Pregunta 29 | JSON y las tuplas
El siguiente código convierte en JSON un diccionario que contiene una tupla y lo vuelve a leer. ¿Cuáles son las 2 líneas que se muestran?
RespuestaD. {"t": [1, 2]} y luego {'t': [1, 2]} list
JSON solo tiene arreglos; no existe un tipo tupla (タプル). Por eso, en el momento de dumps, la tupla se convierte en un arreglo y la salida es {"t": [1, 2]}. Al volver a leerlo, el arreglo se restaura también como lista, así que el tipo de back['t'] es list; no vuelve a ser una tupla. La información de que originalmente era una tupla no se conserva en ningún lugar, así que el tipo cambia en el viaje de ida y vuelta. Si quiere conservarlo como tupla, debe aplicar usted mismo tuple() al valor después de volver a leerlo. Los paréntesis nunca aparecen en la salida JSON, y una tupla tampoco se convierte en cadena. Por la misma razón, si usa un entero como clave de un diccionario, dumps lo convierte en cadena, y tampoco vuelve a ser entero al leerlo de nuevo.
Pregunta 30 | Las claves de JSON
¿Qué ocurre si intenta convertir en JSON un diccionario con una clave entera y un conjunto?
En la línea 1 sale {1: "a"} y en la línea 2 sale [1, 2]
Se produce un TypeError en la línea 1 y la línea 2 no se ejecuta
En la línea 1 sale {"1": "a"} y en la línea 2 se produce un TypeError
En la línea 1 sale {"1": "a"} y en la línea 2 sale [1, 2]
RespuestaC. En la línea 1 sale {"1": "a"} y en la línea 2 se produce un TypeError
Las claves de un objeto JSON deben ser cadenas, así que, al aplicar dumps a un diccionario con una clave entera, la clave se convierte en cadena y la salida es {"1": "a"}. Esto se completa normalmente; aquí no se produce ninguna excepción. Tenga en cuenta, sin embargo, que al volver a leerlo la clave sigue siendo una cadena y no vuelve a ser un entero. El conjunto (集合) de la línea 2 no tiene un tipo correspondiente en JSON, así que al intentar aplicarle dumps se produce un TypeError, con el mensaje Object of type set is not JSON serializable. Si piensa que un conjunto se escribe como un arreglo, obtendría [1, 2], pero si quiere esa conversión, debe aplicarle usted mismo list() antes de pasarlo. Los valores que contienen texto en japonés se escapan en la salida de forma predeterminada, y si se pasa un valor falso a ensure_ascii, se muestran tal cual con sus propios caracteres.
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ónVer el original en japonés