Os argumentos posicionais (位置引数) são associados em ordem, da esquerda para a direita, então a recebe 1 e b recebe 3. b tem o valor padrão (デフォルト値) 2, mas, como um valor foi passado, esse valor tem prioridade. Os 4 e 5 que sobraram sem correspondência são reunidos em args, marcado com um asterisco, como uma tupla (タプル), resultando em (4, 5). Nenhum argumento nomeado foi passado, então kwargs é o dicionário vazio {}, e não None. Se você achar que o valor padrão 2 permanece em b, obtém (1, 2, (3, 4, 5), {}), mas um argumento posicional tem prioridade sobre um valor padrão. O que entra em args é uma tupla, não uma lista, então também não é exibido com colchetes.
Questão 2 | Especificando por nome
Na chamada a seguir, um nome que não está na definição foi passado como argumento nomeado. O que é exibido?
Apenas 1 é dado como argumento posicional, então a recebe 1 e, como b não é dado, permanece com seu valor padrão 2. Como não existe na definição um parâmetro formal (仮引数) chamado x, o argumento nomeado (キーワード引数) x=9 vai para kwargs, marcado com dois asteriscos, como {'x': 9}. Não sobra nenhum argumento posicional, então args é a tupla vazia (). Se você achar que x=9 vai para b, obtém (1, 9, (), {}), mas um argumento nomeado só vai para o parâmetro formal cujo nome corresponde. Se você achar que o 9 vai para args, obtém (1, 2, (9,), {}), mas um valor passado pelo nome nunca acaba indo para as sobras dos argumentos posicionais. Confundir qual asterisco recebe um dicionário e qual recebe uma tupla poderia fazer parecer que {'x': 9} foi para args e que kwargs ficou vazio.
Questão 3 | Uma lista vazia como padrão
A função a seguir, que tem uma lista vazia como valor padrão, foi chamada 3 vezes. Quais são as 3 linhas exibidas?
De cima para baixo, são exibidos None / None / None
De cima para baixo, são exibidos [1] / [1, 2] / [1, 2, 3]
De cima para baixo, são exibidos [1] / [2] / [3]
De cima para baixo, são exibidos [1] / [1, 2] / [2, 3]
RespostaB. De cima para baixo, são exibidos [1] / [1, 2] / [1, 2, 3]
O valor padrão (デフォルト値) [] é criado uma única vez, quando a linha def é executada, e essa mesma lista fica guardada no objeto função (関数オブジェクト). Não é criada uma nova lista vazia a cada chamada, então os resultados de append se acumulam para a chamada seguinte, e ela cresce como [1], [1, 2], [1, 2, 3]. Se você achar que uma nova lista é criada a cada chamada, obtém [1] / [2] / [3], mas esse é o comportamento quando se usa lst=None como padrão e se cria uma nova lista no corpo da função. Se você escrever o corpo por engano como return lst.append(x), None, o valor de retorno de append, é impresso 3 vezes seguidas. Além disso, se você escrever não em 3 linhas separadas, mas em uma só linha como print(add(1), add(2), add(3)), as três referem-se à mesma lista, então [1, 2, 3], o estado depois de tudo ter sido avaliado, é impresso 3 vezes seguidas.
Questão 4 | Compartilhamento do valor padrão
O atributo do objeto função foi exibido depois de 3 chamadas. O que é exibido?
Um objeto função guarda seus valores padrão em um atributo chamado __defaults__, como uma tupla. Entre os parâmetros formais, só lst tem valor padrão, então ela vira uma tupla de um elemento, exibida com uma vírgula no final. Seu conteúdo é a própria lista criada uma única vez na linha def e, como os resultados dos appends das 3 chamadas permanecem nela tal como estão, ela fica ([1, 2, 3],). Antes de qualquer chamada seria ([],), mas o efeito das chamadas aparecer aqui é justamente o perigo de um argumento padrão mutável (ミュータブルなデフォルト引数). O próprio __defaults__ é uma tupla, então não é exibido com colchetes, e a lista dentro dele também não se transforma em tupla.
Questão 5 | Avaliação do valor padrão
Uma variável foi escrita como valor padrão e, depois, essa variável recebeu outro valor. Quais são as 2 linhas exibidas?
i = 5def h(x=i): return xi = 10print(h())print(h(i))
5, seguido de 5
5, seguido de 10
10, seguido de 10
10, seguido de 5
RespostaB. 5, seguido de 10
A expressão de um valor padrão (デフォルト値) não é avaliada toda vez que a função é chamada; ela é avaliada uma única vez, no momento em que a linha def é executada. Nesse momento i vale 5, então é 5 que fica guardado no objeto função. Mesmo que i receba 10 depois, o valor padrão já guardado não muda, então h() retorna 5. Já h(i) lê i no momento da chamada e o passa, então retorna o valor desse momento, 10. Se você achar que o valor padrão é avaliado a cada chamada, as duas linhas ficam 10; se achar que um valor passado como argumento também substitui o valor padrão, as duas linhas ficam 5. A armadilha do argumento padrão mutável surge exatamente dessa mesma regra.
Questão 6 | A ordem dos argumentos
O código a seguir foi salvo em um arquivo e executado com python3. O que acontece?
def g(a=1, b): return a + bprint(g(2, 3))
Ocorre um SyntaxError no ponto da definição, e nem uma linha é executada
4 é exibido, e a execução segue normalmente até o fim
Ocorre um TypeError no ponto da chamada, e a execução para ali
5 é exibido, e a execução segue normalmente até o fim
RespostaA. Ocorre um SyntaxError no ponto da definição, e nem uma linha é executada
Não é possível colocar um parâmetro formal sem valor padrão depois de um que tem valor padrão. Essa regra é verificada na etapa de análise sintática, então executar o arquivo resulta primeiro em SyntaxError, com a mensagem parameter without a default follows parameter with a default (no 3.11 e anteriores: non-default argument follows default argument). Como a análise sintática é feita no arquivo inteiro antes da execução, nem a linha do print é executada. É uma etapa de detecção totalmente diferente de um TypeError causado por um erro na forma de chamar algo. g(2, 3) em si corresponde ao número de parâmetros formais, então, se a definição estivesse correta, seria de esperar que 5 fosse exibido, mas a execução nunca chega lá. A forma correta é colocar depois o parâmetro com valor padrão, como em def g(b, a=1).
Questão 7 | O valor de retorno de uma função
Duas funções que diferem em ter ou não um return foram chamadas como a seguir. O que é exibido?
one termina sem nunca passar por um return, então seu valor de retorno (戻り値) é None. Não é uma tupla vazia nem uma string vazia que é retornada. two coloca 1 e 2 separados por vírgula depois de return, mas isso não é retornar dois valores; é retornar uma única tupla, (1, 2). Por isso type(two()).__name__ é tuple. Se você achar que vários valores de retorno são reunidos em uma lista, obtém list, mas uma expressão com itens separados por vírgulas cria uma tupla. Tropeçar ao tentar atribuir e usar o valor de retorno de uma função que apenas exibe algo costuma ser causado por esse None.
Questão 8 | Desempacotamento na chamada
O código a seguir coloca um asterisco antes de uma lista para chamar uma função. O que é exibido?
Quando você coloca um asterisco antes de uma lista no lado da chamada, o conteúdo dela é distribuído um a um como argumentos posicionais (位置引数). Assim, a recebe 1 e b recebe 2, e o 3 que sobrou é reunido em args, marcado com um asterisco, como uma tupla, resultando em (3,). Como é uma tupla de um elemento, uma vírgula é colocada no final. Nenhum argumento nomeado foi passado, então kwargs é o dicionário vazio {}, e não None. Se você esquecer o asterisco e escrever f(vals), a própria lista vai para o primeiro parâmetro formal, resultando em ([1, 2, 3], 2, (), {}), o que muda o resultado sem gerar erro. O que entra em args é uma tupla, então também não é exibido com colchetes.
Questão 9 | Como as anotações são tratadas
A função a seguir foi chamada com um valor de tipo diferente do que está escrito em sua anotação. O que acontece?
文字列 é impresso na linha 1, e <class 'int'> é impresso na linha 2
Ocorre um TypeError na linha 1, informando que não se pode passar um tipo diferente da anotação
文字列 é impresso na linha 1, e int é impresso na linha 2
Ocorre um ValueError na linha 1, informando que não é possível converter para int
RespostaA. 文字列 é impresso na linha 1, e <class 'int'> é impresso na linha 2
Uma anotação (アノテーション) é apenas registrada no dicionário __annotations__ do objeto função; o tipo não é verificado em tempo de execução. Mesmo que a esteja anotado como int, você pode passar uma string, e mesmo que b esteja anotado como str, você pode passar um inteiro. Mesmo que o valor de retorno esteja anotado como bool, o que é realmente retornado é a string recebida, tal como está. Por isso, na linha 1 é impresso 文字列, tal como está. O que está guardado em __annotations__['a'] na linha 2 é o objeto de tipo int, então imprimi-lo o exibe na forma <class 'int'>. Não são impressos apenas os 3 caracteres int. Se você realmente quiser verificar tipos, precisa usar uma ferramenta separada de verificação de tipos ou fazer a verificação você mesmo no corpo da função.
Questão 10 | Como corrigir o valor padrão
O código a seguir foi reescrito com o valor padrão definido como None. Quais são as 3 linhas exibidas?
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 cima para baixo, são exibidos [1] / [1, 2] / [9, 3]
De cima para baixo, são exibidos [1] / [2] / [9, 3]
De cima para baixo, são exibidos [1] / [2] / [3, 9]
De cima para baixo, são exibidos [1] / [2] / [3]
RespostaB. De cima para baixo, são exibidos [1] / [2] / [9, 3]
O padrão consagrado é definir o valor padrão (デフォルト値) como None e então verificar, no início do corpo, se ele é None, criando uma nova lista vazia nesse caso. Como None é um valor que não pode ser alterado, não surge nenhum problema de compartilhamento, mesmo sendo avaliado uma única vez. Como uma nova lista vazia é criada a cada vez na primeira e na segunda chamadas, que omitem lst, elas ficam [1] e [2], sem restar nenhum elemento da chamada anterior. Na terceira chamada, quem chama passa [9], então lst is None fica falso, e 3 é acrescentado ao final da lista passada, resultando em [9, 3]. Se ficasse como lst=[], antes da reescrita, os elementos da chamada anterior permaneceriam quando o argumento fosse omitido na segunda chamada, resultando em [1, 2]. Se, ignorando a lista passada por quem chama, você sempre criar uma lista nova, obtém [1] / [2] / [3], e a vantagem desse padrão — usar o que foi dado, quando algo é dado — se perde. Se, em vez de acrescentar ao final, você inserir no início com insert(0, x), a terceira chamada fica [3, 9].
Questão 11 | Ordem de busca de nomes
Qual é a ordem correta em que a regra LEGB do Python busca o nome de uma variável?
Função em execução, nível superior do módulo, função externa, embutidos, nesta ordem
Função em execução, função externa, nível superior do módulo, embutidos, nesta ordem
Nível superior do módulo, embutidos, função em execução, função externa, nesta ordem
Embutidos, nível superior do módulo, função externa, função em execução, nesta ordem
RespostaB. Função em execução, função externa, nível superior do módulo, embutidos, nesta ordem
LEGB vem de Local, Enclosing, Global, Built-in e representa uma busca do mais interno para fora. Primeiro procura dentro da função em execução, depois dentro da função que a envolve (外側の関数), depois no nível superior do módulo (モジュール最上位) e, por fim, nos embutidos (組み込み) como len e print. A busca termina no momento em que algo é encontrado, então, se o mesmo nome for usado no lado interno, o nome externo fica oculto. Buscar de fora para dentro significaria que uma variável local criada dentro de uma função perderia para uma variável de mesmo nome no nível do módulo, o que não corresponde ao comportamento real. O erro de inverter a ordem de Global e Enclosing também é comum, mas a função externa é mais interna do que o nível superior do módulo.
Questão 12 | Uma atribuição local
O código a seguir atribui, de dentro de uma função, ao mesmo nome usado fora dela. Quais são as 2 linhas exibidas?
x = 'global'def f(): x = 'local' print(x)f()print(x)
local, seguido de local
local, seguido de global
global, seguido de global
global, seguido de local
RespostaB. local, seguido de global
Atribuir a um nome dentro de uma função cria esse nome como uma nova variável local (ローカル変数), e a variável de mesmo nome no nível do módulo não é afetada em nada. O print(x) dentro da função lê o x local, então local é impresso, mas o print(x) depois que a função retorna lê o x do nível do módulo, então global é impresso. Se você achar que a atribuição dentro da função alcança o lado de fora, as duas linhas ficam local, mas isso exigiria uma declaração global x no início da função. Ao contrário, se você achar que a atribuição dentro da função é ignorada e o valor externo é lido, as duas linhas ficam global, mas uma variável local é de fato criada assim que se faz uma atribuição. Essa assimetria — a leitura alcança o lado de fora, mas a escrita não — é a regra básica de escopo (スコープ) do Python.
Questão 13 | nonlocal
No código a seguir, a função interna tenta reescrever a variável externa. O que é exibido?
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
RespostaC. 2 1
Como no inner dentro de outer está escrito nonlocal n, a atribuição a n alcança o n da função que o envolve, outer. O n que outer retorna depois de chamar inner é 2. Como o inner dentro de outer2 não tem essa declaração, n = 2 apenas cria uma nova variável local (ローカル変数) de inner e para por aí, então o n de outer2 continua 1. Por isso a saída é 2 1. Se você achar que as duas alcançam, obtém 2 2, e se achar que nenhuma alcança, obtém 1 1, mas o que faz a diferença é aquela única linha de nonlocal. Observe também que nonlocal não pode ser usado a menos que já exista uma variável de mesmo nome em uma função externa, e também não pode apontar para uma variável do nível superior do módulo (モジュール最上位). Quando quiser reescrever uma variável do nível superior, use global.
Questão 14 | Erro de variável não vinculada
Um nome que recebe atribuição em uma linha posterior foi lido em uma linha anterior. O que acontece quando o código a seguir é executado?
z = 100def bad(): print(z) z = 1bad()
Ocorre um UnboundLocalError, nada é impresso, e a execução para
100 é impresso, e a execução segue normalmente até o fim
None é impresso, e a execução segue normalmente até o fim
1 é impresso, e a execução segue normalmente até o fim
RespostaA. Ocorre um UnboundLocalError, nada é impresso, e a execução para
Quando o Python compila o corpo de uma função, ele decide que todo nome que recebe uma atribuição em qualquer lugar dessa função é uma variável local (ローカル変数), em toda a função. Essa determinação é feita para a função como um todo, e a ordem das linhas não importa. Nessa função bad, há uma atribuição z = 1 em uma linha posterior, então z é tratado como variável local dessa função. Assim, no ponto de print(z), ainda não foi colocado nenhum valor nela, e ocorre um UnboundLocalError. O texto da mensagem dessa exceção pode variar conforme a versão do Python, mas o nome do tipo, UnboundLocalError, não muda. O z = 100 do nível superior não é consultado. Se você achar que, por estar em uma linha anterior à atribuição, o valor externo é lido, obteria 100, mas não é assim que o Python se comporta. Uma variável local sem atribuição também não vira None.
Questão 15 | A declaração global
O código a seguir coloca lado a lado uma função com declaração global e uma sem ela. O que é exibido?
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
RespostaB. 20 30 20
f tem global n escrito, então n = 20 reescreve o n do nível superior do módulo (モジュール最上位). A própria f retorna 20, e o n do nível superior também passa a ser 20. g não tem essa declaração, então n = 30 apenas cria uma variável local em g e não afeta o n do nível superior. g retorna seu próprio 30 local. O n lido pelo print final é o do nível superior e, como f já o reescreveu, vale 20. Se você achar que até a atribuição de g alcança o lado de fora, obtém 20 30 30, e se achar que global não tem efeito, obtém 20 30 10. Uma declaração global (global宣言) é uma instrução que diz "envie a atribuição a este nome para o nível superior"; observe que a declaração não é necessária se você apenas lê o valor.
Questão 16 | O efeito de __all__
O mymod.py a seguir foi preparado, e outro arquivo escreveu from mymod import *. Quais nomes são trazidos para o lado que importa?
RespostaD. Apenas pub é trazido, e other e hello não
Uma importação com asterisco (星印による取り込み) normalmente traz todos os nomes que não começam com sublinhado, mas, se o lado do módulo tiver uma lista de strings chamada __all__, ela se restringe apenas aos nomes listados ali. Neste exemplo, __all__ lista apenas pub, então nem other nem hello são trazidos. other não começa com sublinhado, mas, uma vez que __all__ existe, a lista dele tem prioridade. Ser uma função também não dá tratamento especial, então hello igualmente não é trazido. __all__ só tem efeito sobre a importação com asterisco; uma importação que indica um nome específico, como from mymod import hello, ou o modo de fazer import mymod e depois mymod.hello(), não é bloqueada.
Questão 17 | O valor de __name__
Para o mymod2.py a seguir, qual é a combinação de valores de __name__ quando ele é executado diretamente com python3 mymod2.py e quando outro arquivo faz import mymod2?
A execução direta dá mymod2, e o import dá __main__
Tanto a execução direta quanto o import dão __main__
A execução direta dá __main__, e o import dá mymod2
Tanto a execução direta quanto o import dão mymod2
RespostaC. A execução direta dá __main__, e o import dá mymod2
Quando você executa um arquivo diretamente com python3, '__main__' é colocado em __name__. Quando ele é importado de outro arquivo, em vez disso é colocado o nome do arquivo sem a extensão, 'mymod2'. Por isso, na execução direta são impressas as 2 linhas mymod2: __main__ e 直接実行, e na importação é impressa apenas a 1 linha mymod2: mymod2. if __name__ == '__main__': é a expressão idiomática consagrada que usa essa diferença para separar o "processamento que só deve ser executado quando este arquivo é executado diretamente". Inverter a combinação significaria que o código destinado a verificar o funcionamento também seria executado na importação, o que anula o propósito dessa expressão idiomática. Se os dois casos dessem o mesmo valor, essa separação nem sequer poderia ser feita.
Questão 18 | sys.argv
Quando o args.py a seguir é executado com o comando python3 args.py foo bar, quais são as 2 linhas exibidas?
sys.argv é uma lista com os valores passados pela linha de comando (コマンドライン), e o próprio nome do script é colocado no início dela. Neste exemplo, ela vira uma lista de 3 elementos, algo como ['args.py', 'foo', 'bar'], então seu len é 3. sys.argv[1:], cortado do índice 1 em diante, vira ['foo', 'bar'], sem o nome do script. Se você achar que o número de argumentos passados vira o comprimento tal como está, obtém 2, mas isso deixa de contar o nome do script no início. Achar que sys.argv[1:] vira ['args.py', 'foo'] acontece quando você erra a posição inicial da fatia. Se você executar sem passar nenhum argumento, o comprimento é 1, e sys.argv[1:] é uma lista vazia.
Questão 19 | Recarregando um módulo
Qual é o comportamento correto quando o mesmo módulo é importado duas vezes dentro de um único programa?
Na segunda vez, o corpo do módulo é lido de novo desde o início e executado mais uma vez
Na segunda vez, é criado um objeto separado, e nenhum estado é compartilhado
Na segunda vez, o cache é usado, e o corpo do módulo não é executado de novo
Na segunda vez, ocorre um ImportError, já que o mesmo nome não pode ser usado mais de uma vez
RespostaC. Na segunda vez, o cache é usado, e o corpo do módulo não é executado de novo
Um módulo que já foi importado uma vez fica em cache (キャッシュ) em um dicionário chamado sys.modules. O segundo import apenas vincula o nome ao mesmo objeto módulo já registrado ali, então não acontece nem a releitura do arquivo nem a reexecução do corpo. Você pode confirmar isso colocando um print no nível superior do módulo e vendo que ele é exibido só uma vez, mesmo importando duas vezes. Mesmo que você o importe com outro alias, ele aponta para o mesmo objeto. Assim, se você reescrever uma variável de algum módulo, o valor reescrito também fica visível de qualquer outro lugar que importe esse módulo. Importar o mesmo nome duas vezes não causa erro e, se quiser recarregar o corpo durante o desenvolvimento, você precisa usar explicitamente um recurso do importlib.
Questão 20 | Verificando se há um ambiente virtual
Qual é uma forma adequada de determinar se o Python em execução no momento está dentro de um ambiente virtual criado com venv?
Se sys.prefix não estiver definido, pode-se concluir que está dentro de um ambiente virtual
Se sys.prefix e sys.base_prefix coincidirem, pode-se concluir que está dentro de um ambiente virtual
Se sys.base_prefix estiver definido, pode-se concluir que está dentro de um ambiente virtual
Se sys.prefix e sys.base_prefix forem diferentes, pode-se concluir que está dentro de um ambiente virtual
RespostaD. Se sys.prefix e sys.base_prefix forem diferentes, pode-se concluir que está dentro de um ambiente virtual
sys.prefix aponta para onde o Python em execução no momento está instalado, e sys.base_prefix aponta para onde está o Python do qual ele se originou. Fora de um ambiente virtual (仮想環境), os dois têm o mesmo valor, mas dentro de um ambiente criado com python3 -m venv, sys.prefix passa a apontar para o diretório do ambiente criado, enquanto sys.base_prefix continua apontando para o Python original, então os dois acabam diferentes. A existência dessa diferença é a pista para a determinação. Dizer que coincidir significa estar dentro de um ambiente virtual é a condição inversa. sys.base_prefix está sempre definido, mesmo fora de um ambiente virtual, então não se pode concluir pelo fato de estar definido ou não. sys.prefix também está sempre definido. Os caminhos concretos e a versão do pip variam conforme o ambiente, então essa relação é a única coisa que vale a pena memorizar.
Questão 21 | O valor de retorno de write
O código a seguir captura o valor de retorno de um método de escrita. O que é exibido?
with open('/tmp/sample.txt', 'w') as f: n = f.write("one\ntwo\nthree\n")print(n)
None é exibido
3 é exibido
14 é exibido
11 é exibido
RespostaC. 14 é exibido
write retorna, como valor de retorno (戻り値), o número de caracteres que escreveu. Em "one\ntwo\nthree\n", one tem 3 caracteres, two tem 3 caracteres e three tem 5 caracteres, totalizando 11, e somando as 3 quebras de linha (改行) chega-se a um total de 14 caracteres. Esquecer de contar as quebras de linha dá 11. Se você achar que é retornado o número de linhas escritas, obtém 3, mas o que write conta é estritamente o número de caracteres. Escolher None supondo que nada é retornado também é um erro comum, fácil de confundir com métodos como append ou sort, que retornam None, mas write retorna uma contagem. Além disso, write não acrescenta quebra de linha automaticamente, então, quando quiser separar linhas, você precisa escrevê-la você mesmo dentro da string.
Questão 22 | readlines
Um arquivo com 3 linhas escritas foi lido com readlines. O que é exibido?
with open('/tmp/sample.txt', 'w') as f: f.write("one\ntwo\nthree\n")with open('/tmp/sample.txt') as f: print(f.readlines())
['one\ntwo\nthree\n'] é exibido
'one\ntwo\nthree\n' é exibido
['one\n', 'two\n', 'three\n'] é exibido
['one', 'two', 'three'] é exibido
RespostaC. ['one\n', 'two\n', 'three\n'] é exibido
readlines retorna todas as linhas como uma lista, mas o caractere de quebra de linha (改行) no final de cada elemento é mantido tal como está. Por isso fica ['one\n', 'two\n', 'three\n']. Se você achar que as quebras de linha são removidas, obtém ['one', 'two', 'three'], mas para obter isso você precisa aplicar rstrip a cada elemento. Quem retorna tudo como uma única string, sem dividir, é read; readlines retorna uma lista. Se você achar que ele trata o arquivo inteiro como um só elemento, embrulhado sem dividir nas quebras de linha, obtém ['one\ntwo\nthree\n'], mas readlines cria um novo elemento a cada quebra de linha. Quando você escreve for line in f e tira as linhas uma de cada vez, a line entregue a você também ainda tem a quebra de linha.
Questão 23 | Uma segunda leitura
O mesmo objeto arquivo foi lido duas vezes seguidas. O que é exibido?
with open('/tmp/sample.txt') as f: a = f.read() b = f.read()print(len(a), b == '')
14 False é exibido
14 True é exibido
0 False é exibido
0 True é exibido
RespostaB. 14 True é exibido
Um objeto arquivo (ファイルオブジェクト) lembra sua posição atual. O primeiro read lê tudo, do início ao fim, então a fica com 14 caracteres, incluindo as quebras de linha, e a posição vai para o fim. O segundo read tenta ler a partir do fim, então não sobra nada e ele retorna uma string vazia. Por isso len(a) é 14, e b == '' é True. Se você achar que o mesmo conteúdo é retornado na segunda vez, b == '' seria False, mas, para ler o mesmo conteúdo de novo, você precisa voltar a posição para o início com f.seek(0). len(a) ser 0 aconteceria se o arquivo estivesse vazio ou se a posição já estivesse no fim. É uma situação fácil de confundir com uma falha de leitura quando nada é exibido, mas a causa é a posição estar no fim.
Questão 24 | with e close
closed foi verificado tanto dentro quanto fora de um bloco with. Quais são as 2 linhas exibidas?
with open('/tmp/sample.txt') as f: print(f.closed)print(f.closed)
False, seguido de False
False, seguido de True
True, seguido de False
True, seguido de True
RespostaB. False, seguido de True
Dentro de um bloco with, o arquivo ainda está aberto, então closed é False. Ele é fechado automaticamente ao sair do bloco, então closed depois disso passa a ser True. Por isso é exibido False, seguido de True. O propósito de usar with (with文) é garantir que esse processo de fechamento seja executado mesmo que você se esqueça de escrevê-lo, e o arquivo é fechado mesmo que ocorra uma exceção dentro do bloco e você saia no meio. Você também pode abrir vários arquivos juntos em uma única instrução with, separando-os por vírgulas, e cada um deles é fechado da mesma forma. Tentar ler de um objeto arquivo depois que ele foi fechado resulta em ValueError, então não é possível lê-lo de novo fora do bloco with.
Questão 25 | Modo de escrita
O mesmo arquivo foi aberto 3 vezes, recebeu escritas e depois foi lido. O que é exibido?
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())
O conteúdo do arquivo é AAABBB
O conteúdo do arquivo é AAABBBC
O conteúdo do arquivo é C
O conteúdo do arquivo é CAABBB
RespostaC. O conteúdo do arquivo é C
Abrir com 'w' trunca o arquivo para comprimento 0 nesse momento (切り詰め), haja ou não conteúdo existente. Acompanhando em ordem, o primeiro 'w' escreve AAA, e o 'a' seguinte é um acréscimo (追記), então fica AAABBB. Mas, no momento em que ele é aberto pela terceira vez com 'w', AAABBB é apagado e C é escrito nele, então o que resta no final é apenas C. Se você achar que as 3 aberturas foram com 'a', obtém AAABBBC, e se achar que a última escrita é ignorada, obtém AAABBB. Sobrescrever a partir do início sem truncar é o comportamento de 'r+', caso em que apenas o caractere inicial seria substituído, como em CAABBB. O truncamento acontece no momento da abertura, não no momento da escrita, então abrir com 'w' e não escrever nada deixa o arquivo vazio. Se a intenção é acrescentar, use 'a'; se é refazer o arquivo, use 'w' — expresse sua intenção pelo modo.
Questão 26 | Uma operação depois de fechar
O que acontece se você tentar ler de um objeto arquivo depois que ele foi fechado?
f = open('/tmp/sample.txt')f.close()print(f.read())
Ocorre um ValueError, informando que é uma operação em um arquivo fechado
Uma string vazia é exibida, e a execução termina normalmente
O arquivo é reaberto automaticamente, e todo o seu conteúdo é exibido
Ocorre um OSError, informando que o arquivo não pode ser reaberto
RespostaA. Ocorre um ValueError, informando que é uma operação em um arquivo fechado
Chamar read em um objeto arquivo depois que ele foi fechado resulta em ValueError, com a mensagem I/O operation on closed file. Como o próprio objeto lembra que foi fechado, ele não é reaberto automaticamente só porque é necessário. Ele também não retorna uma string vazia no sentido de "não há nada para ler" — esse é o comportamento de read depois que o arquivo já foi lido até o fim. Algo como FileNotFoundError, para quando um arquivo não é encontrado, é parente de OSError, mas a exceção (例外) por operar em um arquivo fechado é outra, e é ValueError. Usar uma instrução with evita, por estrutura, a própria situação de tentar ler depois de sair do bloco.
Questão 27 | Somente leitura
O que acontece se você tentar escrever em um arquivo que foi aberto com o modo omitido?
with open('/tmp/sample.txt', 'w') as f: f.write('data')with open('/tmp/sample.txt') as f: f.write('more')
Ocorre um io.UnsupportedOperation, informando que não é possível escrever nele
Ocorre um ValueError, informando que a especificação do modo está incorreta
more é acrescentado ao final do conteúdo, e a execução termina normalmente
O conteúdo é substituído por more, e a execução termina normalmente
RespostaA. Ocorre um io.UnsupportedOperation, informando que não é possível escrever nele
Omitir o segundo argumento de open faz com que ele seja somente leitura, 'r'. Chamar write em um objeto arquivo aberto como somente leitura resulta em io.UnsupportedOperation, acompanhado da mensagem not writable. Como esse código é sintaticamente válido, você só percebe o erro de modo (モード) quando realmente o executa. Se quiser sobrescrever, use 'w'; se quiser acrescentar ao final, use 'a'; e, se quiser tanto ler quanto escrever, reabra com 'r+'. Ao contrário, chamar read em um arquivo aberto com 'w' ou 'a' resulta no mesmo io.UnsupportedOperation, desta vez com not readable. Você pode pensar no modo como uma declaração, no momento em que aquele objeto arquivo é criado, do que é permitido fazer com ele.
Questão 28 | seek e tell
O código a seguir lê apenas os 5 primeiros caracteres e depois verifica a posição. O que é exibido?
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
RespostaB. 'one\nt' 5 0
Passar um número para read faz com que ele leia apenas essa quantidade de caracteres. Os 5 primeiros caracteres são os 3 caracteres de one, o 1 caractere de quebra de linha e o t de two, resultando em 'one\nt'. A posição avança na medida do que foi lido, então o tell logo em seguida retorna 5. O seek(0) seguinte volta a posição para o início, então o tell final é 0. Se você achar que read não move a posição, tell retorna 0 nas duas vezes, e se achar que seek não tem efeito, tell retorna 5 nas duas vezes. Se você confundir o argumento de read como sendo um número de linhas em vez de um número de caracteres, com a intenção de ler até a linha 5, escolheria por engano o arquivo inteiro, 'one\ntwo\nthree\n', e 14. Usar seek(0) permite ler de novo desde o início com o mesmo objeto arquivo.
Questão 29 | JSON e tuplas
O código a seguir converte em JSON um dicionário que contém uma tupla e o lê de volta. Quais são as 2 linhas exibidas?
RespostaD. {"t": [1, 2]}, seguido de {'t': [1, 2]} list
O JSON só tem arrays (配列); não existe um tipo como a tupla. Por isso, no momento do dumps, a tupla é convertida em um array, e a saída é {"t": [1, 2]}. Quando é lido de volta, o array também é restaurado como lista, então o tipo de back['t'] é list; ele não volta a ser tupla. A informação de que originalmente era uma tupla não fica guardada em lugar nenhum, então o tipo muda na ida e volta. Se você quiser mantê-lo como tupla, precisa aplicar tuple() você mesmo ao valor depois de lê-lo de volta. Parênteses nunca aparecem na saída JSON, e uma tupla também não é convertida em string. Pelo mesmo motivo, usar um inteiro como chave de dicionário faz com que ele seja convertido em string pelo dumps, e ele também não volta a ser inteiro ao ser lido de volta.
Questão 30 | As chaves do JSON
O que acontece se você tentar converter em JSON um dicionário com chave inteira e um conjunto?
A linha 1 mostra {1: "a"}, e a linha 2 mostra [1, 2]
Ocorre um TypeError na linha 1, e a linha 2 não é executada
A linha 1 mostra {"1": "a"}, e ocorre um TypeError na linha 2
A linha 1 mostra {"1": "a"}, e a linha 2 mostra [1, 2]
RespostaC. A linha 1 mostra {"1": "a"}, e ocorre um TypeError na linha 2
As chaves de um objeto JSON precisam ser strings, então chamar dumps em um dicionário com chave inteira converte a chave em string, resultando na saída {"1": "a"}. Isso é concluído normalmente; nenhuma exceção é gerada aqui. Observe, porém, que, ao ser lida de volta, a chave continua sendo string e não volta a ser inteiro. O conjunto (集合) da segunda linha não tem tipo correspondente no JSON, então tentar chamar dumps nele resulta em TypeError, com a mensagem Object of type set is not JSON serializable. Se você achar que o conjunto é escrito como um array, obtém [1, 2], mas, se quiser essa conversão, precisa aplicar list() a ele você mesmo antes de passá-lo. Para valores que contêm texto em japonês, eles são escapados na saída por padrão, e passar falso para ensure_ascii faz com que sejam exibidos como os próprios caracteres.
Prática: responda às perguntas desta página
Esta é uma ferramenta de prática com perguntas em ordem aleatória (funciona quando o JavaScript está ativado). Mesmo sem ela, você pode ler todas as perguntas e explicações acima.
* As explicações são informações para fins de estudo. O conteúdo e o sistema dos exames mudam de ano para ano, por isso confira sempre os comunicados oficiais da entidade organizadora.
Esta página é uma tradução do texto original em japonês. Em caso de diferença entre a tradução e o original, prevalece a versão em japonês. Esta tradução foi feita com IA e pode conter erros.Informar um erro de traduçãoVer o original em japonês