Escolar Documentos
Profissional Documentos
Cultura Documentos
Introdução à Computação e
Programação Usando
Pitão
Introdução à Computação e
Programação Usando
Pitão
com Aplicação à Modelagem Computacional
e Entendimento de Dados
terceira edição
John V. Guttag
Imprensa do MIT
Cambridge, Massachusetts
Londres, Inglaterra
Machine Translated by Google
Todos os direitos reservados. Nenhuma parte deste livro pode ser reproduzida de qualquer forma por
qualquer meio eletrônico ou mecânico (incluindo fotocópia, gravação ou armazenamento e recuperação de
informações) sem permissão por escrito do editor.
Este livro foi ambientado no Minion Pro pela New Best-set Typesetters Ltd.
10 9 8 7 6 5 4 3 2 1
d_r0
Machine Translated by Google
Olga
Davi
Andreia
Michael
Marca
addie
perfurar
Machine Translated by Google
CONTEÚDO
PREFÁCIO
AGRADECIMENTOS
1: COMEÇANDO
2: INTRODUÇÃO AO PYTHON
7: MÓDULOS E ARQUIVOS
8: TESTE E DEBUGAÇÃO
9: EXCEÇÕES E AFIRMAÇÕES
10: AULAS E PROGRAMAÇÃO ORIENTADA A OBJETOS
25: CONJUNTO
26: MÉTODOS DE CLASSIFICAÇÃO
Lista de Figuras
Capítulo 1
Figura 1-1 Fluxograma de jantar
Capítulo 2
Figura 2-1 Janela de inicialização do Anaconda
Figura 2-2 Janela do Spyder
Figura 2-3 Operadores nos tipos int e float
Figura 2-4 Vinculação de variáveis a objetos
Figura 2-5 Fluxograma para declaração condicional
Figura 2-6 Fluxograma para iteração
Figura 2-7 Quadrando um número inteiro, da maneira mais difícil
Capítulo 11
Capítulo 12
Capítulo 13
Machine Translated by Google
Figura 13-13 Saldo restante e custo líquido para diferentes tipos de hipotecas
Capítulo 14
Capítulo 15
Figura 15-5 Usando uma árvore de decisão para resolver um problema da mochila
Capítulo 16
Capítulo 17
Capítulo 23
Figura 23-1 Um exemplo de Pandas DataFrame vinculado à variável
wwc
Capítulo 24
Figura 24-1 Dois conjuntos de
nomes Figura 24-2 Associando um vetor de características a
cada nome Figura 24-3 Pares de vetores de características/
rótulos para presidentes Figura 24-4 Nome, características e rótulos
para diversos animais Figura 24-5 Visualizando
métricas de distância Figura 24-6
Distância de Minkowski Figura
24-7 Classe Animal Figura 24-8 Construir tabela de distâncias entre
pares de animais Figura 24-9 Distâncias entre três
animais Figura 24-10 Distâncias entre quatro
animais Figura 24-11 Distâncias usando um método diferente
representação
de recursos Capítulo 25 Figura 25-1 Altura, peso
e cor da camisa Figura 25-2
Exemplo de classe Figura
25-3 Grupo de classes Figura 25-4
Agrupamento de K-means Figura 25-5 Encontrando o
melhor agrupamento de k-médios
Figura 25-6 Um teste de k-means Figura 25-7
Exemplos de duas distribuições Figura 25-8 Linhas impressas por uma chamada para
Figura 25-9 Gerando pontos a partir de três distribuições Figura
25-10 Pontos de três gaussianos sobrepostos Figura 25-11
Dentição de mamífero em dentalFormulas.csv Figura 25-12
Leitura e processamento do arquivo CSV
Figura 25-13 Atributos de escala
Figura 25-14 Início do CSV arquivo classificando mamíferos por dieta
Machine Translated by Google
Capítulo 26
os dados em treinamento e
conjuntos de teste
Figura 26-20 Leia os dados do Titanic e crie uma lista de exemplos 207
PREFÁCIO
Este livro é baseado em cursos que são oferecidos no MIT desde 2006 e como
“Massive Online Open Courses” (MOOCs) através do edX e MITx desde 2012.
A primeira edição do livro foi baseada em um único curso de um semestre. No
entanto, com o tempo, não resisti em adicionar mais material do que caberia em
um semestre. A edição atual é adequada para uma sequência introdutória de
ciência da computação de dois ou três trimestres.
Noções básicas de
programação, linguagem de programação
Python 3, técnicas de resolução de problemas
computacionais e complexidade computacional.
e-programação-em-python-fall-2016/
e
https://ocw.mit.edu/courses/electrical-engineering-and computer-science/6-0002-
introduction-to-computational thinking-and-data-science-fall-2016/.
1 Essa foi a suposta resposta de Euclides, por volta de 300 aC, ao pedido
do rei Ptolomeu de uma maneira mais fácil de aprender matemática.
Machine Translated by Google
AGRADECIMENTOS
Tenho uma dívida especial de gratidão com Julie Sussman, PPA, que
editou as duas primeiras edições deste livro, e Lisa Ruffolo, que editou a
edição atual. Tanto Julie quanto Lisa foram colaboradoras que leram o livro
com olhos de estudante e me disseram o que precisava ser feito, o que
deveria ser feito e o que poderia ser feito se eu tivesse tempo e energia para
fazê-lo. Eles me enterraram em “sugestões” boas demais para serem
ignoradas.
Finalmente, agradeço à minha esposa, Olga, por me incentivar a terminar
e por me dispensar de várias tarefas domésticas para que eu pudesse
trabalhar no livro.
Machine Translated by Google
COMEÇANDO
Um computador faz duas coisas, e apenas duas coisas: ele executa cálculos e
lembra os resultados desses cálculos. Mas faz essas duas coisas extremamente
bem. O computador típico que fica em uma mesa ou em uma mochila realiza cerca
de 100 bilhões de cálculos por segundo. É difícil imaginar o quão rápido isso é.
Pense em segurar uma bola um metro acima do chão e soltá-la. No momento em
que chega ao chão, seu computador pode ter executado mais de um bilhão de
instruções. Quanto à memória, um pequeno computador pode ter centenas de
gigabytes de armazenamento. Quão grande é isso? Se um byte (o número de bits,
normalmente oito, necessários para representar um caractere) pesasse um grama
(o que não acontece), 100 gigabytes pesariam 100.000 toneladas métricas. Para
efeito de comparação, esse é aproximadamente o peso combinado de 16.000
elefantes africanos.2 Durante a maior parte da história da humanidade, a computação
foi limitada pela rapidez com que o cérebro
humano podia calcular e pela qualidade com que a mão humana registrava os
resultados computacionais. Isso significava que apenas os menores problemas
poderiam ser atacados computacionalmente. Mesmo com a velocidade dos
computadores modernos, alguns problemas ainda estão além dos modelos
computacionais modernos (por exemplo, entender completamente a mudança
climática), mas cada vez mais problemas estão se mostrando passíveis de solução
computacional. Esperamos que, ao terminar este livro, você se sinta à vontade para
aplicar o pensamento computacional na solução de muitos dos problemas que
encontrar durante seus estudos, trabalho e até mesmo na vida cotidiana.
x” e “é possível viajar de trem de Paris a Roma”. Estas são declarações de fato. Infelizmente, eles
não nos dizem nada sobre como encontrar uma raiz quadrada ou como pegar trens de Paris a
Roma.
Caso contrário, crie uma nova estimativa calculando a média de g e x/g, ou seja, (g +
x/g)/2.
4. Usando esse novo palpite, que novamente chamamos de g, repita o processo até que g*g
esteja próximo o suficiente de x.
Observe que a descrição do método é uma sequência de etapas simples, juntamente com um
fluxo de controle que especifica quando executar cada etapa. Tal descrição é chamada de
algoritmo. 5 O algoritmo que usamos para aproximar a raiz quadrada é um exemplo de adivinhação
Machine Translated by Google
verifique o algoritmo. Baseia-se no fato de que é fácil verificar se um palpite é ou não bom
o suficiente.
Mais formalmente, um algoritmo é uma lista finita de instruções que descrevem um
conjunto de cálculos que, quando executados em um conjunto de entradas, prosseguirão
por uma sequência de estados bem definidos e, eventualmente, produzirão uma saída.
2. Mexa.
A receita inclui alguns testes para decidir quando o processo está completo, bem como
instruções sobre a ordem de execução das instruções, às vezes pulando para uma
instrução específica com base em um teste.
diga a ele para fazer - nada mais, nada menos. Isso é bom porque significa que
você pode fazer o computador fazer todo tipo de coisas divertidas e úteis. É
uma coisa ruim porque quando não faz o que você quer, você geralmente não
tem ninguém para culpar além de si mesmo.
Existem centenas de linguagens de programação no mundo.
Não existe melhor linguagem. Diferentes idiomas são melhores ou piores para
diferentes tipos de aplicativos. O MATLAB, por exemplo, é uma boa linguagem
para manipulação de vetores e matrizes. C é uma boa linguagem para escrever
programas que controlam redes de dados. PHP é uma boa linguagem para
construir websites. E o Python é uma excelente linguagem de uso geral.
linguagens são projetadas para que cada programa legal tenha exatamente um
significado.
Embora os erros de sintaxe sejam o tipo de erro mais comum (especialmente para
aqueles que estão aprendendo uma nova linguagem de programação), eles são o tipo
de erro menos perigoso. Toda linguagem de programação séria detecta todos os erros
sintáticos e não permite que os usuários executem um programa com um único erro
sintático. Além disso, na maioria dos casos, o sistema de linguagem fornece uma
indicação suficientemente clara da localização do erro para que o programador seja
capaz de corrigi-lo sem pensar muito.
Ele pode ser executado até a conclusão e produzir uma resposta que pode ou não
estar correta.
Machine Translated by Google
Exercício com os dedos: os computadores podem ser irritantemente literais. Se você não
disser a eles exatamente o que deseja que eles façam, é provável que eles façam a coisa
errada. Tente escrever um algoritmo para dirigir entre dois destinos. Escreva da maneira que
você faria para uma pessoa e imagine o que aconteceria se essa pessoa fosse tão estúpida
quanto um computador e executasse o algoritmo exatamente como está escrito. (Para uma
ilustração divertida disso, dê uma olhada no vídeo https://www.youtube.com/watch?v=FN2RM-
CHkuI&t=24s.)
conhecimento declarativo
conhecimento imperativo
computação
de algoritmo
computador de programa
fluxo de controle do
contador do programa
fluxograma
linguagem de programação
Tese de Church-Turing
Machine Translated by Google
problema de parada
Literais de completude de
Turing
operadores infixos
sintaxe
semântica estática
semântica
2 Nem tudo é mais caro do que costumava ser. Em 1960, um pouco de memória de
computador custava cerca de US$ 0,64. Hoje custa cerca de $ 0,000000004.
6 Pode ser difícil para alguns de vocês acreditar, mas antigamente as pessoas não
carregavam telefones que funcionavam como recursos
computacionais. Na verdade, as pessoas carregavam pequenos dispositivos que só
podiam ser usados para cálculos aritméticos.
INTRODUÇÃO AO PYTHON
Neste livro, usamos Python. No entanto, este livro não é sobre Python. Certamente
ajudará você a aprender Python, e isso é bom. O que é muito mais importante, no entanto,
é que você aprenderá algo sobre como escrever programas que resolvem problemas.
Você pode transferir essa habilidade para qualquer linguagem de programação.
Python é uma linguagem de programação de propósito geral que você pode usar
efetivamente para construir quase qualquer tipo de programa que não precise de acesso
direto ao hardware do computador. Python não é ideal para programas que possuem altas
restrições de confiabilidade (devido à sua fraca verificação semântica estática) ou que são
construídos e mantidos por muitas pessoas ou por um longo período de tempo (novamente
devido à fraca verificação semântica estática).
Python é uma linguagem viva. Desde a sua introdução por Guido von Rossum em
1990, passou por muitas mudanças. Durante a primeira década de sua vida, Python foi
uma linguagem pouco conhecida e pouco usada.
Isso mudou com a chegada do Python 2.0 em 2000. Além de incorporar melhorias
importantes à própria linguagem, marcou uma mudança no caminho evolutivo da
linguagem. muitos grupos
Machine Translated by Google
Este seria um bom momento para instalar o Anaconda (ou algum outro
IDE) em seu computador, para que você possa executar os exemplos no
Machine Translated by Google
e siga as instruções.
Assim que a instalação estiver concluída, inicie o aplicativo Anaconda
Navigator. Uma janela contendo uma coleção de ferramentas Python aparecerá. A
janela se parecerá com a Figura 2-1. 10 Por
enquanto, a única ferramenta que usaremos é o Spyder. Quando você iniciar o
Spyder (clicando no botão Iniciar , entre todas as coisas), uma janela semelhante
à Figura 2-2 será aberta.
Observe que dois valores foram passados para impressão na terceira instrução.
A função print pega um número variável de argumentos separados por vírgulas
e os imprime, separados por um caractere de espaço, na ordem em que
aparecem.
Machine Translated by Google
int é usado para representar números inteiros. Literais do tipo int são
escritos da maneira que normalmente denotamos inteiros (por exemplo,
-3 ou 5 ou 10002). float é usado para representar números reais. Os
literais do tipo float sempre incluem um ponto decimal (por exemplo, 3,0 ou
3,17 ou -28,72). (Também é possível escrever literais do tipo float
usando notação científica. Por exemplo, o literal 1.6E3 representa 1.6*103 ,
ou seja, é o mesmo que 1600.0.) Você pode se perguntar por que esse
tipo não é chamado de real. Dentro do computador, os valores do tipo float
são armazenados como números de ponto flutuante. Essa representação,
que é usada por todas as linguagens de programação modernas, tem muitas vantagens.
No entanto, em algumas situações, faz com que a aritmética de
ponto flutuante se comporte de maneiras ligeiramente diferentes da
aritmética em números reais. Discutimos isso na Seção 3.3. bool é
usado para representar os valores booleanos True e False.
None é um tipo com um único valor. Falaremos mais sobre None na Seção
4.
3
Fora[1]: 3
3+2
Fora [2]: 5
3,0+2,0
Fora[3]: 5.0
3!=2
Out[4]: Verdadeiro
O tipo de função interna do Python pode ser usado para descobrir o tipo
de um objeto:
tipo(3)
Fora[5]: int
tipo(3.0)
Fora[6]: flutuar
pi = 3
raio = 11 área =
pi * (raio**2) raio = 14
x, y = 2, 3
Isso é conveniente, pois permite que você use atribuição múltipla para trocar as
ligações de duas variáveis.
Por exemplo, o código
x, y = 2, 3
x, y = y, x print('x
=', x) print('y =', y)
vai imprimir
x=3
y=2
se x%2 == 0:
print('Par') senão:
print('Ímpar')
print('Feito com condicional')
x = (111111111111111111111111111111111 +
222222222222333222222222 +
3333333333333333333333333333333)
é interpretado como uma única linha por causa dos parênteses. Muitos
programadores Python preferem usar continuações de linha implícitas a usar
uma barra invertida. Mais comumente, os programadores quebram linhas
longas em vírgulas ou operadores.
Voltando aos condicionais, quando o bloco verdadeiro ou o bloco falso
de um condicional contém outro condicional, diz-se que as instruções
condicionais estão aninhadas. O código a seguir contém condicionais
aninhadas em ambas as ramificações da instrução if de nível superior .
se x%2 == 0:
se x%3 == 0:
print('Divisivel por 2 e 3')
Machine Translated by Google
outro:
print('Divisível por 2 e não por 3')
elif x%3 == 0:
print('Divisível por 3 e não por 2')
print('z é o menor')
Você pode atacar este exercício de várias maneiras. Há oito casos separados
a serem considerados: todos são ímpares (um caso), exatamente dois deles são
ímpares (três casos), exatamente um deles é ímpar (três casos) ou nenhum deles
é ímpar (um caso). Portanto, uma solução simples envolveria uma sequência de
oito instruções if , cada uma com uma única instrução print :
Isso faz o trabalho, mas é bastante complicado. Não são apenas 16 linhas
de código, mas as variáveis são repetidamente testadas quanto à estranheza.
O código a seguir é mais elegante e mais eficiente:
resposta = min(x, y, z) se x%2 !=
0:
resposta = x
imprimir(resposta)
imprime o máximo de x, e, e z.
Os condicionais nos permitem escrever programas que são mais interessantes do
que os programas de linha reta, mas a classe de programas de ramificação ainda é
bastante limitada. Uma maneira de pensar sobre o poder de uma classe de programas
é em termos de quanto tempo eles podem levar para serem executados. Suponha que
cada linha de código leve uma unidade de tempo para ser executada. Se uma linha reta
Machine Translated by Google
programa tiver n linhas de código, levará n unidades de tempo para ser executado. E
quanto a um programa de ramificação com n linhas de código? Pode levar menos de n
unidades de tempo para ser executado, mas não pode demorar mais, pois cada linha de
código é executada no máximo uma vez.
Diz-se que um programa para o qual o tempo máximo de execução é limitado pela
duração do programa é executado em tempo constante. Isso não significa que cada
vez que o programa é executado, ele executa o mesmo número de etapas. Isso significa
que existe uma constante, k, de modo que o programa não levará mais do que k passos
para ser executado. Isso implica que o tempo de execução não cresce com o tamanho
da entrada do programa.
Objetos do tipo str são usados para representar caracteres.16 Literais do tipo str podem
ser escritos usando aspas simples ou duplas, por exemplo, 'abc' ou "abc". O literal '123'
denota uma string de três caracteres, não o número 123.
'a'
3*4
3*'a'
3+4
'um'+'um'
new_id
'a'*'a'
Cada uma dessas linhas gera uma mensagem de erro. A primeira linha produz a
mensagem
Como new_id não é um literal de nenhum tipo, o interpretador o trata como um nome.
No entanto, como esse nome não está vinculado a nenhum objeto, tentar usá-lo
causa um erro de tempo de execução. O código 'a'*'a' produz a mensagem de erro
TypeError: não é possível multiplicar a sequência por não int do tipo 'str'
Essa verificação de tipo existe é uma coisa boa. Ele transforma erros
descuidados (e às vezes sutis) em erros que interrompem a execução, em vez de
erros que levam os programas a se comportarem de maneiras misteriosas. A
verificação de tipo em Python não é tão forte quanto em algumas outras linguagens
de programação (por exemplo, Java), mas é melhor em Python 3 do que em Python
2. Por exemplo, está claro o que < deve significar quando é usado para comparar
dois strings ou dois números. Mas qual deve ser o valor de '4' < 3 ? De forma bastante
arbitrária, os projetistas do Python 2 decidiram que deveria ser False, porque todos
os valores numéricos deveriam ser menores que todos os valores do tipo str. Os
projetistas do Python 3 e da maioria das outras linguagens modernas decidiram que,
como essas expressões não têm um significado óbvio, elas deveriam gerar uma
mensagem de erro.
Strings são um dos vários tipos de sequência em Python. Eles compartilham as
seguintes operações com todos os tipos de sequência.
Machine Translated by Google
que imprime
produz a mesma saída que a instrução print anterior, mais detalhada. Se você
quiser incluir uma chave na string denotada por uma string f, use duas chaves.
Por exemplo, print(f'{{{3*5}}}') imprime {15}.
A expressão dentro de uma string f pode conter modificadores que controlam
a aparência da string de saída.17 Esses modificadores são separados da
expressão que indica o valor a ser modificado por dois pontos. Por exemplo, a
string f f'{3.14159:.2f}' é avaliada como a string '3.14' porque o modificador .2f
instrui o Python a truncar a representação de string de um número de ponto
flutuante para dois dígitos após o ponto decimal. E a declaração
Machine Translated by Google
2.4.1 Entrada
Python 3 tem uma função, entrada, que pode ser usada para obter entrada
diretamente de um usuário. A função de entrada recebe uma string como argumento
e a exibe como um prompt no shell. A função então espera que o usuário digite algo
e pressione a tecla Enter . A linha digitada pelo usuário é tratada como uma string e
se torna o valor retornado pela função.
seria exibido. Observe que a instrução print introduz um espaço antes do “?”. Ele faz
isso porque quando print recebe vários argumentos, ele coloca um espaço entre os
valores associados aos argumentos. O espaço pode ser evitado executando + name
+ '?') ou print(f'Are you really
print('Você é mesmo '
{name}?'), cada um dos quais produz uma única string e passa essa string como o único argumento a
ser impresso.
Agora considere o código
porque input sempre retorna um objeto do tipo str, mesmo que o usuário tenha
digitado algo que se pareça com um número inteiro. Por exemplo, se o usuário
tivesse inserido 3, n seria vinculado ao str '3' e não ao int 3. Portanto, o valor da
expressão n*4 seria '3333' em vez de 12. A boa notícia é que sempre que uma
string é um literal válido de algum tipo, uma conversão de tipo pode ser aplicada a
ela.
Exercício de dedo: Escreva um código que peça ao usuário para inserir seu
aniversário no formato mm/dd/aaaa e, em seguida, imprima uma string no formato
'Você nasceu no ano aaaa'.
Você deve estar se perguntando como consegui digitar a string 'ÿ ÿÿ ÿÿÿÿÿÿ
ÿÿÿÿÿÿ?'. Eu não. Como a maior parte da web usa UTF-8, consegui cortar
a string de uma página da web e colá-la diretamente em meu programa.
Existem maneiras de inserir caracteres Unicode diretamente de um teclado,
mas, a menos que você tenha um teclado especial, todas elas são bastante
complicadas.
#… print(to_print)
Na quarta vez que o teste é alcançado, ele é avaliado como Falso e o fluxo de
controle prossegue para a instrução de impressão após o loop. Para quais valores de x
esse programa terminará? Há três casos a serem considerados: x == 0, x > 0 e x < 0.
Suponha que x < 0. Algo muito ruim acontece. O controle entrará no loop e cada
iteração moverá num_iterations para mais longe de x do que para mais perto dele. O
programa, portanto, continuará executando o loop indefinidamente (ou até que algo ruim
ocorra, por exemplo, um erro de estouro). Como poderíamos remover essa falha no
Machine Translated by Google
programa? Alterar o teste para num_iterations < abs(x) quase funciona. O loop
termina, mas imprime um valor negativo. Se a instrução de atribuição dentro do
loop também for alterada, para ans = ans + abs(x), o código funcionará corretamente.
enquanto verdadeiro:
se x%11 == 0 e x%12 == 0:
quebrar
x=x+1
print(x, 'é divisível por 11 e 12')
estampas
Se uma instrução break for executada dentro de um loop aninhado (um loop
dentro de outro loop), a quebra encerrará o loop interno.
Os loops while que usamos até agora são altamente estilizados, muitas vezes
iterando sobre uma sequência de inteiros. Python fornece uma linguagem
Machine Translated by Google
mecanismo, o loop for , que pode ser usado para simplificar programas contendo
esse tipo de iteração.
A forma geral de uma instrução for é (lembre-se de que as palavras em
itálico são descrições do que pode aparecer, não código real):
para variável em sequência: bloco de
código
imprimirá 91. A expressão (77, 11, 3) é uma tupla. Discutimos as tuplas em detalhes
na Seção 5. Por enquanto, pense apenas em uma tupla como uma sequência de
valores.
A sequência de valores vinculados à variável é mais comumente gerada
usando a função interna que retorna uma sérierecebe de intervalos inteiros. A função
três argumentos inteiros:
faixa start, stop e step. Produz a progressão start, start + step,
start + 2*step, etc. Se step for positivo, o último elemento é o maior inteiro tal que
(start + i*step) é estritamente menor que stop. Se step for negativo, o último
elemento é o menor inteiro tal que (start + i*step) é maior que stop. Por exemplo, a
expressão range(5, 40, 10) produz a sequência 5, 15, 25, 35, e a expressão
range(40, 5, -10) produz a sequência 40, 30, 20, 10.
x=4
para i no intervalo(x): print(i)
imprime
0
1
2
3
índice = 0
last_index = 1 while
index <= last_index: i = index print(i) i =
0 print(i) index
= index + 1
impressões? Apenas 0, porque os argumentos para a função faixana linha com for são
avaliados logo antes da primeira iteração do loop e não são reavaliados nas iterações
subsequentes.
Agora, vamos ver com que frequência as coisas são avaliadas quando aninhamos loops.
Considerar
x=4
para j no intervalo (x): para
i no intervalo (x): x = 2
Quantas vezes cada um dos dois loops é executado? Já vimos que o range(x) que
controla o loop externo é avaliado na primeira vez que é atingido e não reavaliado a
cada iteração, portanto, são quatro iterações do loop externo. Isso implica que o loop
for interno é alcançado quatro vezes. A primeira vez que é atingida, a variável x = 4,
então haverá quatro iterações. No entanto, nas próximas três vezes que for atingido,
x = 2, haverá duas iterações de cada vez.
x=3
for j in range(x):
print('Iteração do loop externo') for i in range(x):
print(' x = 2
Iteração do loop interno')
ele imprime
A instrução for pode ser usada em conjunto com o operador in para iterar
convenientemente os caracteres de uma string. Por exemplo,
total = 0 para
c em '12345678': total = total
+ int(c) print(total)
linguagem de baixo
nível linguagem de alto
nível linguagem
interpretada linguagem
compilada código-fonte
Código da máquina
Ambiente
de desenvolvimento integrado Python (IDE)
anaconda
Spyder
shell do console
IPython
programa (script)
comando (declaração)
objeto
tipo
objeto escalar
objeto não escalar
literal
Machine Translated by Google
bool de ponto
flutuante
Nenhum
valor da
expressão do
operador
variável de
prompt do shell
palavra
reservada de
atribuição vinculativa
programa de ramificação
condicional
expressão composta
tempo constante
complexidade computacional
strings de expressão
condicional sobrecarregado
de operador
verificação
indexação corte
Machine Translated by Google
formatada
Unicode
pseudocódigo de
iteração
(looping)
durante a simulação de loop manual
quebrar
for loop
tupla
faixa
no operador
10 Como o Anaconda é atualizado com frequência, quando você ler isto, a aparência
da janela pode ter mudado.
14 Se você acredita que o valor real de ÿ não é 3, você está certo. Nós até demonstramos
esse fato na Seção 18.4.
15 “O que há em um nome? Aquilo que chamamos de rosa por qualquer outro nome
cheiraria tão doce.”
porque o intérprete tenta encontrar o valor ao qual ans está vinculado antes de estar
vinculado a qualquer coisa. Agora, restaure a inicialização de ans, substitua a
instrução ans = ans + 1 por e tente encontrar a raiz cúbica de 8. Depois que você
anos = anos, cansar de esperar, digite “control c” (mantenha pressionada a tecla Ctrl
e a tecla c simultaneamente) . Isso o levará de volta ao prompt do usuário no shell.
abs(x) - ans**3)
Machine Translated by Google
no início do loop e tente executá-lo novamente. desta vez ele vai imprimir
Veja o tamanho de um número inteiro que você precisa inserir antes que haja uma
pausa perceptível antes que o resultado seja impresso.
Vejamos outro exemplo de enumeração exaustiva: testar se um
inteiro é um número primo e retornar o menor divisor se não for. Um
número primo é um inteiro maior que 1 que é divisível apenas por si
mesmo e por 1. Por exemplo, 2, 3, 5 e 111.119 são primos e 4, 6, 8 e
62.710.561 não são primos.
A maneira mais simples de descobrir se um inteiro, x, maior que 3
é primo, é dividir x por cada inteiro entre 2 e, x-1. Se o resto de
qualquer uma dessas divisões for 0, x não é primo, caso contrário, x
é primo. O código na Figura 3-2 implementa essa abordagem. Ele
primeiro pede ao usuário para inserir um inteiro, converte a string
retornada em um int e atribui esse inteiro à variável x. Em seguida,
ele configura as condições iniciais para uma enumeração exaustiva
inicializando o palpite como 2 e a variável menor_divisor como None
— indicando que, até prova em contrário, o código assume que x é primo.
A enumeração exaustiva é feita dentro de um loop for . O loop
termina quando todos os divisores inteiros possíveis de x foram
tentados ou quando descobriu um inteiro que é um divisor de x.
Depois de sair do loop, o código verifica o valor de small_divisor e
imprime o texto apropriado. O truque de inicializar uma variável antes
de entrar em um loop e verificar se esse valor foi alterado na saída é
comum.
Exercício de dedo: Altere o código na Figura 3-2 para que ele retorne o
maior em vez do menor divisor. Dica: se y*z = x e y é o menor divisor de x,
z é o maior divisor de x.
Imagine que alguém lhe peça para escrever um programa que imprima a raiz
quadrada de qualquer número não negativo. O que você deveria fazer?
Você provavelmente deve começar dizendo que precisa de uma declaração
de problema melhor. Por exemplo, o que o programa deve fazer se for solicitado
a encontrar a raiz quadrada de 2? A raiz quadrada de 2 não é um número
racional. Isso significa que não há como representar com precisão seu valor
como uma string finita de dígitos (ou como um ponto flutuante), portanto, o
problema declarado inicialmente não pode ser resolvido.
O que um programa pode fazer é encontrar uma aproximação para a raiz
quadrada - ou seja, uma resposta que seja próxima o suficiente da raiz quadrada
real para ser útil. Voltaremos a essa questão em detalhes consideráveis mais
adiante neste livro. Mas, por enquanto, vamos pensar em “próximo o suficiente”
como uma resposta que está dentro de alguma constante, chame-a de epsilon, do real
responder.
Mais uma vez, estamos usando enumeração exaustiva. Observe que esse
método para encontrar a raiz quadrada não tem nada em comum com a maneira
de encontrar raízes quadradas usando um lápis que você pode ter aprendido no
ensino médio. Muitas vezes, a melhor maneira de
Machine Translated by Google
O que você acha que acontecerá se definirmos x = 0,25? Encontrará uma raiz próxima
de 0,5? Não. Infelizmente, ele irá relatar
Quando executamos nosso código após essa alteração, ele informa que
Agora, vamos pensar em quanto tempo o programa levará para ser executado. O
número de iterações depende de quão perto a resposta está do nosso ponto de partida, 0,
e do tamanho das etapas. Grosso modo, o programa executará o loop while no máximo
vezes x/passo .
Vamos tentar o código em algo maior, por exemplo, x = 123456. Ele será executado
por um bom tempo e depois será impresso
O que você acha que aconteceu? Certamente existe um número de ponto flutuante
que aproxima a raiz quadrada de 123456 com precisão de 0,01.
Machine Translated by Google
é, para qualquer par de números distintos, n1 e n2, ou n1 < n2 ou n1 > n2. Então, podemos
pensar na raiz quadrada de x como estando em algum lugar na linha
0_____________________________________________
_____________max
e comece a pesquisar esse intervalo. Como não sabemos necessariamente por onde
começar a pesquisar, vamos começar pelo meio.
0__________________________suposição______________
____________max
Se essa não for a resposta certa (e não será na maioria das vezes), pergunte se é
muito grande ou muito pequeno. Se for muito grande, sabemos que a resposta deve estar à
esquerda. Se for muito pequeno, sabemos que a resposta deve estar à direita. Em seguida,
repetimos o processo no intervalo menor. A Figura 3-5 contém uma implementação e um
teste desse algoritmo.
numGuesses = 13
5,00030517578125 está próximo da raiz quadrada de 25
Observe que ele encontra uma resposta diferente do nosso algoritmo anterior.
Isso está perfeitamente bem, pois ainda atende à especificação do problema.
Mais importante, observe que a cada iteração do loop, o tamanho do espaço
a ser pesquisado é cortado pela metade. Por esta razão, o algoritmo é chamado
de busca de bisseção. A pesquisa de bisseção é uma grande melhoria em
relação ao nosso algoritmo anterior, que reduzia o espaço de pesquisa em apenas
uma pequena quantidade a cada iteração.
Vamos tentar x = 123456 novamente. Desta vez, o programa leva apenas 30
tentativas para encontrar uma resposta aceitável. Que tal x = 123456789 ? Leva
apenas 45 palpites.
Não há nada de especial em usar esse algoritmo para encontrar raízes
quadradas. Por exemplo, alterando alguns 2s para 3s, podemos usá-lo para
aproximar a raiz cúbica de um número não negativo. No Capítulo 4, apresentamos
um mecanismo de linguagem que nos permite generalizar esse código para
encontrar qualquer raiz.
A pesquisa de bisseção é uma técnica amplamente útil para muitas coisas
além de encontrar raízes. Por exemplo, o código na Figura 3-6 usa a busca de
bisseção para encontrar uma aproximação para o log base 2 de x (ou seja, um
número, ans, tal que 2**ans está próximo de x). Ele é estruturado exatamente
como o código usado para encontrar uma aproximação para uma raiz quadrada.
Ele primeiro encontra um intervalo contendo uma resposta adequada e, em
seguida, usa a pesquisa de bisseção para explorar esse intervalo com eficiência.
Machine Translated by Google
Exercício com os dedos: o que teria de ser alterado para fazer o código
da Figura 3-5 funcionar para encontrar uma aproximação da raiz cúbica de
números negativos e positivos? Dica: pense em mudar para baixo para
garantir que a resposta esteja dentro da região que está sendo pesquisada.
Na maioria das vezes, números do tipo float fornecem uma aproximação razoavelmente
boa para números reais. Mas “a maior parte do tempo” não é o tempo todo e, quando
não o fazem, pode levar a consequências surpreendentes. Por exemplo, tente
executar o código
x = 0,0 para
i no intervalo (10): x = x + 0,1
se x == 1,0:
print(x, '= 1.0') else:
Talvez você, como a maioria das pessoas, ache estranho que imprima,
Talvez porque a maioria das pessoas tenha dez dedos, gostamos de usar
decimais para representar números. Por outro lado, todos os sistemas de
computador modernos representam números em binário. Isso não ocorre porque
os computadores nascem com dois dedos. É porque é fácil construir switches
de hardware, ou seja, dispositivos que podem estar em apenas um dos dois
estados, ligado ou desligado. O fato de os computadores usarem uma
representação binária e as pessoas uma representação decimal pode levar a
uma dissonância cognitiva ocasional.
Nas linguagens de programação modernas, os números não inteiros são
implementados usando uma representação chamada ponto flutuante. Por
enquanto, vamos supor que a representação interna seja em decimal.
Representaríamos um número como um par de inteiros - os dígitos significativos
do número e um expoente. Por exemplo, o número 1,949 seria representado
como o par (1949, -3), que representa o produto 1949*10-3 .
0,625.
0,1? Um número infinito de dígitos! Não existem inteiros sig e tais que sig 2 -exp seja igual a 0,1. Portanto, não
exp *
importa quantos bits o Python (ou qualquer outra linguagem) use para representar números de ponto
flutuante, ele pode representar apenas uma aproximação de 0,1. Na maioria das implementações do Python,
existem 53 bits de precisão disponíveis para números de ponto flutuante, portanto, os dígitos significativos
armazenados para o número decimal 0,1 serão
11001100110011001100110011001100110011001100110011001
se x == 1,0:
print(x, '= 1.0') else:
imprimir
Agora vemos que o teste x == 1,0 produz o resultado Falso porque o valor ao qual x está vinculado não
é exatamente 1,0. Isso explica por que a cláusula else foi executada. Mas por que ele decidiu que x era menor
que 1,0 quando a representação de ponto flutuante de 0,1 é ligeiramente maior que 0,1? Porque durante
alguma iteração do loop, o Python ficou sem dígitos significativos e fez alguns arredondamentos, que por acaso
foram para baixo. O que é impresso se adicionarmos ao final da cláusula else o código print x == 10.0*0.1? Ele
imprime Falso. Não é o que nossos professores do ensino fundamental nos ensinaram, mas somar 0,1 dez
vezes não produz o mesmo valor que multiplicar 0,1 por 10.
3.4 Newton–Raphson23
2
prazo. Alguns exemplos são 3 (grau 0), 2,5x + 12 (grau 1) e 3x (grau 2).
Para encontrar a raiz quadrada de um número, digamos k, precisamos encontrar um valor x tal que x ÿ k =
2
0. A primeira derivada desse polinômio é simplesmente 2x. Portanto, sabemos que podemos melhorar o palpite
atual escolhendo como nosso próximo palpite ÿ ( palpite2 ÿ )/2 * palpite. A Figura 3-7 contém um código que ilustra
como usar esse método para encontrar rapidamente uma aproximação para a raiz quadrada.
Machine Translated by Google
função de decremento
adivinhar e verificar
enumeração exaustiva
Aproximação
Ordenação Total
Bisseção Pesquisa
aproximação
sucessiva
trocar
dígitos
expoente
Machine Translated by Google
arredondamento
de precisão
Coeficiente polinomial
de Newton-
Raphson
grau
raiz
22 Se você nunca viu tal coisa, eles ainda podem ser encontrados em
bibliotecas de tijolo e argamassa.
24 Newton criou uma variante desse algoritmo por volta de 1669. Joseph Raphson
publicou uma variante diferente mais ou menos na mesma época que
Newton. Notavelmente, a variante de Raphson ainda é amplamente usada hoje.
(Talvez ainda mais notável, Stradivari começou a fazer violinos no mesmo
ano, e alguns de seus violinos também estão em uso hoje.)
Machine Translated by Google
Já usamos várias funções internas, por exemplo, max e abs na Figura 4-1. A
capacidade dos programadores de definir e usar suas próprias funções, como se
fossem integradas, é um salto qualitativo em conveniência.
outro:
retornar y
def é uma palavra reservada que informa ao Python que uma função está prestes
a ser definida. O nome da função (max_val neste exemplo) é simplesmente um
nome usado para se referir à função. A convenção PEP 8 é que os nomes das
funções devem estar em letras minúsculas com palavras separadas por
sublinhados para melhorar a legibilidade.
A sequência de nomes entre parênteses após o nome da função (x,y neste
exemplo) são os parâmetros formais da função. Quando a função é usada, os
parâmetros formais são vinculados (como em uma instrução de atribuição) aos
parâmetros reais (frequentemente referidos como argumentos) da invocação
da função (também referida como uma chamada de função). Por exemplo, a
invocação
max_val(3, 4)
liga x a 3 e y a 4.
Machine Translated by Google
A Figura 4-3 contém uma função que possui três parâmetros formais e
retorna um valor, chame-a de resultado, de modo que abs(resultado**potência
– x) >= epsilon.
A Figura 4-4 contém código que pode ser usado para testar se find_root
funciona como pretendido. A função de teste test_find_root tem
aproximadamente o mesmo comprimento que o próprio find_root . Para
programadores inexperientes, escrever funções de teste muitas vezes parece
ser um desperdício de esforço. Programadores experientes sabem, no entanto,
que um investimento na escrita de código de teste costuma render grandes
dividendos. Certamente é melhor do que sentar em um teclado e digitar casos
de teste no shell repetidamente durante a depuração (o processo de descobrir
por que um programa não funciona e corrigi-lo). Observe que, como estamos
invocando test_find_root com três tuplas (isto é, sequências de valores) de
comprimento três, uma chamada verifica 27 combinações de parâmetros.
Finalmente, como test_find_root verifica se find_root está retornando uma
resposta apropriada e relata o resultado, ele salva o programador da tarefa
tediosa e propensa a erros de inspecionar visualmente cada saída e verificar
se está correto. Voltamos ao assunto dos testes no Capítulo 8.
Machine Translated by Google
Exercício com os dedos: Use a função find_root na Figura 4-3 para imprimir a
soma das aproximações para a raiz quadrada de 25, a raiz cúbica de -8 e a
quarta raiz de 16. Use 0,001 como epsilon.
Exercício de dedo: Escreva uma função is_in que aceite duas strings como
argumentos e retorne True se uma string ocorrer em qualquer lugar da outra, e
False caso contrário. Dica: você pode querer usar o operador interno str in.
print(primeiro_nome, sobrenome)
A função print_name assume que first_name e last_name são strings e que reverse é um
booleano. Se reverse == True, imprime last_name, first_name;caso contrário, ele imprime
first_name last_name.
Cada um dos seguintes é uma invocação equivalente de print_name:
print(primeiro_nome, sobrenome)
vai imprimir
Olga Puchmayerova
Puchmajerova, Olga
Puchmajerova, Olga
Machine Translated by Google
Exercício de dedo: Escreva uma função mult que aceite um ou dois ints
como argumentos. Se chamada com dois argumentos, a função imprime o
produto dos dois argumentos. Se chamado com um argumento, imprime esse
argumento.
min(6,4)
min(3,4,1,6)
são legais (e avaliam o que você acha que eles fazem). O Python torna mais
fácil para os programadores definirem suas próprias funções que aceitam um
número variável de argumentos. O operador de descompactação * permite
que uma função aceite um número variável de argumentos posicionais. Por
exemplo,
def média(*args):
# Assume pelo menos um argumento e todos os argumentos são
números
# Retorna a média dos argumentos tot = 0 for a in args:
tot += a
return tot/len(args)
Machine Translated by Google
imprime 1,5 -1,0. Observe que o nome após o * na lista de argumentos não precisa ser
args. Pode ser qualquer nome. Para média, poderia ter sido mais descritivo escrever def
mean(*numbers).
4.1.4 Escopo
Vejamos outro pequeno exemplo:
x=3y
=2z=
f(x) #valor de x usado como parâmetro atual print('z =', z) print('x =',
x) print('y =', y)
x=4
z=4
x=3a
=2
de atribuição vincula x a 3.
Machine Translated by Google
deff():
imprimir(x)
def g():
imprimir(x) x
=1
x = 3 f()
x=
3 g()
4.2 Especificações
De alguma forma, você precisa deixar todo mundo saber o que todo mundo está
fazendo, sem gerar tanto trabalho que ninguém esteja disposto a participar. É aqui
que entra a abstração. Você pode escrever 25 especificações, cada uma dizendo qual
material os alunos devem aprender em cada aula, mas sem dar nenhum detalhe sobre
como esse material deve ser ensinado. O que você obteve pode não ser
pedagogicamente maravilhoso, mas pelo menos pode fazer sentido.
abs(x, /)
Retorna o valor absoluto do argumento.
Isso nos diz que abs é uma função que mapeia um único argumento para
seu valor absoluto. (O / na lista de argumentos significa que o argumento
deve ser posicional.) Se você inserir help(), uma sessão de ajuda interativa
será iniciada e o interpretador apresentará o prompt help> na janela do
console. Uma vantagem do modo interativo é que você pode obter ajuda
sobre as construções do Python que não são objetos. Por exemplo,
Machine Translated by Google
ajuda> se
A declaração "se"
******************
if_stmt ::= "if" expressão ":" suite ("elif" expressão ":" suite)*
["else" ":" suite]
Ele seleciona exatamente uma das suítes avaliando as expressões uma a uma até
que uma seja
considerada verdadeira (consulte a seção Operações booleanas para a definição de
verdadeiro e
falso); então essa suíte é executada
Exercício de dedo: Usando o algoritmo da Figura 3-6, escreva uma função que
satisfaça a especificação
Em Python, as funções são objetos de primeira classe. Isso significa que eles
podem ser tratados como objetos de qualquer outro tipo, por exemplo, int ou
lista. Eles têm tipos, por exemplo, a expressão type(abs) tem o valor <type
'built in_function_or_method'>; eles podem aparecer em expressões, por
exemplo, como o lado direito de uma instrução de atribuição ou como um
argumento para uma função; eles podem ser retornados por funções; etc.
Machine Translated by Google
Dar-se ao trabalho de definir uma função para fazer algo tão simples
quanto elevar um número ao quadrado parece um pouco bobo. Felizmente, o
Python oferece suporte à criação de funções anônimas (ou seja, funções que
não estão vinculadas a um nome), usando a palavra reservada lambda. A
forma geral de uma expressão lambda é
Machine Translated by Google
Por exemplo, a expressão lambda lambda x, y: x*y retorna uma função que
retorna o produto de seus dois argumentos. As expressões lambda são
frequentemente usadas como argumentos para funções de ordem superior. Por
exemplo, poderíamos substituir a chamada acima para bisection_solve por
Exercício de dedo: Escreva uma expressão lambda que tenha dois parâmetros
numéricos. Se o segundo argumento for igual a zero, ele deve retornar None. Caso
contrário, deve retornar o valor da divisão do primeiro argumento pelo segundo
argumento. Dica: use uma expressão condicional.
Como as funções são objetos de primeira classe, elas podem ser criadas e
retornadas dentro de funções. Por exemplo, dada a definição da função
def create_eval_ans():
power = input('Digite um inteiro positivo: ') return lambda ans:
ans**int(power)
o código
eval_ans = create_eval_ans()
print(bisection_solve(99, eval_ans, 0,01, baixo, alto))
Exercício de dedo: Use find para implementar uma função que satisfaça a especificação
definição de função
parâmetro formal
parâmetro real
invocação
abstração lambda do
ponto de execução
função de teste
depurando
argumento posicional
palavra-chave argumento
espaço de nomes
escopo
variável local
Machine Translated by Google
quadro de pilha de
mesa de símbolo
cliente de especificação de
pilha de escopo
estático (lexical)
(LIFO)
suposição
garantir a
abstração da
decomposição
função de
ajuda docstring
objeto de primeira
método de expressão
lambda
notação de ponto
26 Na prática, você provavelmente usaria a função interna em vez de definir sua máximo,
própria função.
27 Como veremos mais adiante, essa noção de função é muito mais geral
do que os matemáticos chamam de função. Foi popularizado pela primeira vez pela
linguagem de programação Fortran 2 no final dos anos 1950.
28 Mais adiante no livro, discutimos dois outros mecanismos para sair de uma função, raise e
yield.
Machine Translated by Google
5
TIPOS ESTRUTURADOS E MUTABILIDADE
Os programas que examinamos até agora lidam com três tipos de objetos: int, float e
str. Os tipos numéricos int e float são tipos escalares. Ou seja, objetos desses tipos
não possuem estrutura interna acessível. Em contraste, str pode ser pensado como
um tipo estruturado ou não escalar. Podemos usar a indexação para extrair caracteres
individuais de uma string e fatiar para extrair substrings.
5.1 Tuplas
t1 = () t2 =
(1, 'dois', 3) print(t1) print(t2)
()
(1, 'dois', 3)
Olhando para este exemplo, você pode pensar que a tupla contendo o valor único
1 seria escrita (1). Mas, para citar o RH
Haldeman citando Richard Nixon, “seria errado”. 33 Como os parênteses são usados
para agrupar expressões, (1) é apenas uma maneira detalhada de escrever o inteiro 1.
Para denotar a tupla singleton contendo esse valor, escrevemos (1,). Quase todo
mundo que usa Python, em um momento ou outro, acidentalmente omitiu essa vírgula
irritante.
A repetição pode ser usada em tuplas. Por exemplo, a expressão 3* ('a', 2) resulta em ('a', 2, 'a',
2, 'a', 2).
Assim como as strings, as tuplas podem ser concatenadas, indexadas e divididas.
Considerar
A segunda instrução print imprime o valor gerado pela concatenação dos valores
vinculados a t1 e t2, que é uma tupla com cinco elementos. Ele produz a saída
Uma instrução for pode ser usada para iterar sobre os elementos de
uma tupla. E o operador in pode ser usado para testar se uma tupla contém
um valor específico. Por exemplo, o seguinte código
def intersect(t1, t2):
"""Assume que t1 e t2 são tuplas
Retorna uma tupla contendo elementos que estão em t1 e t2""" result = () for
e in t1:
se e em t2:
resultado += (e,)
resultado de retorno
print(intersect((1, 'a', 2), ('b', 2, 'a')))
max_val = eu
retorno min_val, max_val
Machine Translated by Google
cria um iterador que retornará os elementos da tupla um por vez. Python tem muitos tipos
iteráveis integrados, incluindo strings, listas e dicionários.
Muitas funções integradas úteis operam em iteráveis. Entre os mais úteis estão sum,
min e max. A função sum pode ser aplicada a iteráveis de números. Retorna a soma dos
elementos. O
Machine Translated by Google
as funções max e min podem ser aplicadas a iteráveis para os quais existe uma
ordenação bem definida dos elementos.
Exercício com os dedos: Escreva uma expressão que resulte na média de uma
tupla de números. Use a função soma.
Como uma tupla, uma lista é uma sequência ordenada de valores, onde cada valor é
identificado por um índice. A sintaxe para expressar literais do tipo lista é semelhante
àquela usada para tuplas; a diferença é que usamos colchetes em vez de parênteses.
A lista vazia é escrita como [], e as listas de singleton são escritas sem aquela (oh,
tão fácil de esquecer) vírgula antes do colchete de fechamento.
Como as listas são iteráveis, podemos usar uma instrução for para iterar
os elementos da lista. Assim, por exemplo, o código
produz a saída,
eu fiz tudo
4
Amor
Também podemos indexar em listas e dividir listas, assim como podemos para tuplas.
Por exemplo, o código
L1 = [1, 2, 3]
L2 = L1[-1::-1] para i no
intervalo(len(L1)): print(L1[i]*L2[i])
estampas
3
4
3
Machine Translated by Google
As declarações de atribuição
Univs = [Techs, Ivys]
Univs1 = [['MIT', 'Caltech'], ['Harvard', 'Yale', 'Brown']]
produzir a saída
Univs = [['MIT', 'Caltech'], ['Harvard', 'Yale', 'Brown']]
Univs1 = [['MIT', 'Caltech'], ['Harvard', 'Yale', 'Brown']]
Verdadeiro
Figura 5-2 Duas listas que parecem ter o mesmo valor, mas não
Que Univs e Univs1 estão vinculados a objetos diferentes pode ser verificado usando a função
integrada do Python id, que retorna um identificador inteiro exclusivo para um objeto. Essa função
nos permite testar a igualdade do objeto comparando seu id. Uma maneira mais simples de testar
a igualdade de objeto é usar o operador is . Quando executamos o código
ele imprime
Verdadeiro
Falso
Falso
Id da Univs = 4946827936
ID de Univs1 = 4946612464
(Não espere ver os mesmos identificadores exclusivos se você executar este código. A
semântica do Python não diz nada sobre qual identificador está associado a cada objeto; ela apenas
exige que dois objetos não tenham o mesmo identificador.)
Machine Translated by Google
Observe que na Figura 5-2 os elementos de Univs não são cópias das listas
às quais Techs e Ivys estão vinculados, mas sim as próprias listas. Os elementos
de Univs1 são listas que contêm os mesmos elementos que as listas em Univs,
mas não são as mesmas listas. Podemos ver isso executando o código
que imprime
IDs de Univs[0] e Univs[1] 4447807688 4456134664
IDs de Univs1[0] e Univs1[1] 4447805768 4447806728
O método append para listas tem um efeito colateral. Em vez de criar uma nova
lista, ele altera a lista existente, Techs, adicionando um novo elemento, a string
'RPI' neste exemplo, ao final dela. A Figura 5-3 descreve o estado da computação
após a execução do acréscimo .
L2[i].append(i) print('L1
=', L1, 'mas', 'L2 =', L2)
Imprime L1 = [[0, 1], [0, 1]] mas L2 = [[0], [1]]. Por que?
Porque a primeira instrução de atribuição cria uma lista com dois elementos,
cada um dos quais é o mesmo objeto, enquanto a segunda instrução de
atribuição cria uma lista com dois objetos diferentes, cada um dos quais é
inicialmente igual a uma lista vazia.
append_val(3)
append_val(4)
Você pode pensar que a segunda chamada para append_val imprimiria a lista [4]
porque teria anexado 4 à lista vazia. Na verdade, ele imprimirá [3, 4]. Isso ocorre
porque, na hora da definição da função, é criado um novo objeto do tipo lista , com
valor inicial da lista vazia. Cada vez que append_val é chamado sem fornecer um valor
para o parâmetro formal list_1, o objeto criado na definição da função é vinculado a
list_1, modificado e impresso. Portanto, a segunda chamada para append_val muda e,
em seguida, imprime uma lista que já foi alterada pela primeira chamada para essa
função.
L1 = [1,2,3]
L2 = [4,5,6]
L3 = L1 + L2
imprimir('L3 =', L3)
L1.extend(L2)
print('L1 =', L1)
L1.append(L2)
print('L1 =', L1)
vai imprimir
L3 = [1, 2, 3, 4, 5, 6]
L1 = [1, 2, 3, 4, 5, 6]
L1 = [1, 2, 3, 4, 5, 6, [4, 5, 6]]
Observe que o operador + não tem efeito colateral. Ele cria uma nova lista e a retorna. Em contraste,
estenda e anexe cada mutação L1.
A Figura 5-4 descreve brevemente alguns dos métodos associados com
listas. Observe que todos eles, exceto count e index, modificam a lista.
Machine Translated by Google
5.3.1 Clonagem
Geralmente é prudente evitar a mutação de uma lista sobre a qual se está
iterando. Considere o código
L1.remove(e1)
L1 = [1,2,3,4]
L2 = [1,2,5,6]
Remove_dups(L1, L2) print('L1 =',
L1)
não sofre mutação dentro do loop, mas pode ter consequências surpreendentes
se a lista sofrer mutação. Nesse caso, o contador oculto começa em 0,
descobre que L1[0] está em L2 e o remove - reduzindo o comprimento de L1
para 3. O contador é então incrementado para 1 e o código procede para
verificar se o valor de L1[1] está em L2. Observe que este não é o valor original
de L1[1] (ou seja, 2), mas sim o valor atual de L1[1] (ou seja, 3). Como você
pode ver, é possível descobrir o que acontece quando a lista é modificada
dentro do loop. No entanto, não é fácil. E o que acontece provavelmente não
é intencional, como neste exemplo.
Uma maneira de evitar esse tipo de problema é usar o slicing para
clonar35 (ou seja, fazer uma cópia) da lista e escrever para e1 em L1[:].
Observe que a escrita
new_L1 = L1 para
e1 em new_L1:
não resolveria o problema. Não criaria uma cópia de L1, mas simplesmente
introduziria um novo nome para a lista existente.
Fatiar não é a única maneira de clonar listas em Python. A expressão
L.copy() tem o mesmo valor que L[:]. Tanto o fatiamento quanto a cópia
executam o que é conhecido como cópia superficial. Uma cópia rasa cria
uma nova lista e insere os objetos (não cópias dos objetos) da lista a ser
copiada na nova lista. O código
L = [2]
L1 = [L]
L2 = L1[:]
L2 = cópia.deepcopy(L1)
L.append(3)
print(f'L1 = {L1}, L2 = {L2}')
imprime L1 = [[2, 3]] L2 = [[2, 3]] porque L1 e L2 contêm o objeto que foi
vinculado a L na primeira instrução de atribuição.
Se a lista a ser copiada contém objetos mutáveis que você também usa
deseja copiar, importe a função do módulo de biblioteca cópia de
O valor de L3 será [[[2]]] porque copy.deepcopy cria um novo objeto não apenas
para a lista [L1], mas também para a lista L1. Ou seja, faz cópias até o final —
na maioria das vezes. Por que “na maioria das vezes?” O código
L1 = [2]
L1.apêndice(L1)
cria uma lista que contém a si mesma. Uma tentativa de fazer cópias até o fundo
nunca terminaria. Para evitar esse problema, copy.deepcopy faz exatamente
uma cópia de cada objeto e, em seguida, usa essa cópia para cada instância do
objeto. Isso é importante mesmo quando as listas não contêm a si mesmas. Por
exemplo,
L1 = [2]
L2 = [L1, L1]
L3 = copiar. deepcopy(L2)
L3[0].apêndice(3)
imprimir(L3)
imprime [[2, 3], [2, 3]] porque copy.deepcopy faz uma cópia de L1 e a usa nas
duas vezes em que L1 ocorre em L2.
para e em iterável:
if test(e):
new_list.append(expr(e)) return new_list
Por exemplo, [e**2 for e in range(6)] resulta em [0, 1, 4, 9, 16, 25], [e**2 for e in range(8) if e%2 == 0] é avaliado
como [0, 4, 16, 36], e [x**2 for x in [2, 'a', 3, 4.0] se type(x) == int] é avaliado como [4, 9] .
L = [(x, y) para x
no intervalo(6) se x%2 == 0 para y no intervalo(6)
se y%3 == 0]
[(0, 0), (0, 3), (2, 0), (2, 3), (4, 0), (4, 3)]
Claro, podemos produzir a mesma lista sem compreensão de lista, mas o código
é consideravelmente menos compacto:
L = []
para x no intervalo(6): se x%2
== 0: para y no
intervalo(6): se y%3 == 0:
L.append((x, y))
Machine Translated by Google
Imprime [[(0, 0), (2, 0), (4, 0)], [(0, 3), (2, 3), (4, 3)]].
É preciso prática para se familiarizar com as compreensões de lista
aninhadas, mas elas podem ser bastante úteis. Vamos usar as compreensões
de lista aninhada para gerar uma lista de todos os números primos menores que
100. A ideia básica é usar uma compreensão para gerar uma lista de todos os
números candidatos (ou seja, de 2 a 99), uma segunda compreensão para gerar
uma lista dos restos da divisão de um primo candidato por cada divisor potencial,
e a função interna all para testar se algum desses restos é 0.
retorna primos
Exercício de dedo: Escreva uma lista de compreensão que gere todos os não
primos entre 2 e 100.
função (ou seja, uma função que tem apenas um parâmetro) e o segundo
argumento é qualquer coleção ordenada de valores adequados como argumentos
para o primeiro argumento. É freqüentemente usado no lugar de uma
compreensão de lista. Por exemplo, list(map(str, range(10))) é equivalente a
[str(e) for e in range(10)]. A função é freqüentemente
O mapa usada com um loop for . Quando usado em um for se comporta
loop, como a função em que faixaretorna um valor
mapa para cada iteração do loop. Esses valores são gerados aplicando o
primeiro argumento a cada elemento do segundo argumento. Por exemplo, o
código
estampas
4
36
16
De forma mais geral, o primeiro argumento para map pode ser uma função
de n argumentos, caso em que deve ser seguido por n coleções ordenadas
subsequentes (cada uma com o mesmo tamanho). Por exemplo, o código
estampas
1
28
9
Vimos quatro tipos de sequência iteráveis: str, tupla, intervalo e lista. Eles são semelhantes
porque objetos desses tipos podem ser operados conforme descrito na Figura 5-6. Algumas
de suas outras semelhanças e diferenças estão resumidas na Figura 5-7.
Os programadores Python tendem a usar listas com muito mais frequência do que tuplas.
Como as listas são mutáveis, elas podem ser construídas de forma incremental durante uma
computação. Por exemplo, o código a seguir cria incrementalmente uma lista contendo todos
os números pares em outra lista.
Machine Translated by Google
even_elems = [] para
e em L:
se e%2 == 0:
even_elems.append(e)
Um dos métodos integrados mais úteis é o split, que recebe duas strings
como argumentos. O segundo argumento especifica um separador que é
usado para dividir o primeiro argumento em uma sequência de substrings.
Por exemplo,
Machine Translated by Google
estampas
5.6 Conjuntos
Conjuntos são outro tipo de tipo de coleção. Eles são semelhantes à noção de um
conjunto em matemática, pois são coleções desordenadas de elementos únicos. Eles são
denotados usando o que os programadores chamam de chaves e os matemáticos
chamam de chaves de conjunto, por exemplo,
baseball_teams.add('Yankees')
football_teams.update(['Patriots', 'Jets']) print(baseball_teams)
print(football_teams)
estampas
Machine Translated by Google
(A ordem na qual os elementos aparecem não é definida pelo idioma, portanto, você pode obter
uma saída diferente se executar este exemplo.)
Os elementos podem ser removidos de um conjunto usando o método remove , que gera
um erro se o elemento não estiver no conjunto, ou o método discard , que não gera um erro se o
elemento não estiver no conjunto.
A participação em um conjunto pode ser testada usando o operador in . Por exemplo, 'Rockies' em
baseball_teams retorna True. A união dos métodos binários , a interseção, a diferença e o issubset têm
seus significados matemáticos usuais. Por exemplo,
print(baseball_teams.union({1, 2}))
print(baseball_teams.intersection(football_teams))
print(baseball_teams.difference(football_teams)) print({'Padres',
'Yankees'}.issubset(baseball_teams))
estampas
Uma das coisas boas sobre conjuntos é que existem operadores infixos convenientes para muitos
dos métodos, incluindo | para união, & para interseção, - para diferença, <= para subconjunto e >= para
superconjunto. O uso desses operadores facilita a leitura do código. Compare, por exemplo,
ao código apresentado anteriormente, que usa notação de ponto para imprimir os mesmos valores.
Nem todos os tipos de objetos podem ser elementos de conjuntos. Todos os objetos em um
conjunto devem ser passíveis de hash. Um objeto é hashável se tiver
Machine Translated by Google
Um método __hash__ que mapeia o objeto do tipo para um int e o valor retornado
por __hash__ não muda durante o tempo de vida do objeto e
Um __eq__ método que é usado para compará-lo quanto à igualdade com outros
objetos.
Todos os objetos dos tipos imutáveis escalares do Python são passíveis de hash, e
nenhum objeto dos tipos mutáveis integrados do Python é passível de hash. Um objeto de
um tipo imutável não escalar (por exemplo, uma tupla) é hashável se todos os seus
elementos forem hasháveis.
5.7 Dicionários
Objetos do tipo dict (abreviação de dicionário) são como listas, exceto pelo fato de que
os indexamos usando chaves em vez de números inteiros. Qualquer objeto hashable pode
ser usado como uma chave. Pense em um dicionário como um conjunto de pares chave/valor.
Literais do tipo dict são colocados entre chaves e cada elemento é escrito como uma chave
seguida por dois pontos seguidos por um valor. Por exemplo, o código,
vai imprimir
As entradas em um dict não podem ser acessadas usando um índice. É por isso que
months_numbers[1] refere-se inequivocamente à entrada com a chave 1 em vez da
segunda entrada. Se uma chave é definida em um dicionário pode ser testado usando o
operador in .
Machine Translated by Google
FtoE['madeira'] = 'madeira'
print(translate('Eu bebo vinho tinto.', dicts, 'Francês para Inglês'))
vai imprimir
Eu madeira de vinho tinto.
Machine Translated by Google
estampas
estampas
estampas
Geralmente é conveniente usar tuplas como chaves. Imagine, por exemplo, usar
uma tupla da forma (flight_number, day) para representar voos de companhias aéreas.
Seria então fácil usar essas tuplas como chaves em um dicionário implementando um
mapeamento de voos para horários de chegada. Uma lista não pode ser usada como
chave, porque objetos do tipo lista não são passíveis de hash.
Como vimos, existem muitos métodos úteis associados aos dicionários, incluindo
alguns para remover elementos. Nós não
Machine Translated by Google
Agora, vamos tentar algo mais ambicioso. Uma cifra é um algoritmo que mapeia
um texto simples (um texto que pode ser facilmente lido por um ser humano) para
um texto criptografado. As cifras mais simples são cifras de substituição que
substituem cada caractere no texto simples por uma string única. O mapeamento
dos caracteres originais para a string que os substitui é chamado de chave (por
analogia com o tipo de chave usada para abrir uma fechadura, não o tipo de chave
usada nos dicionários Python). Em Python, os dicionários fornecem uma maneira
conveniente de implementar mapeamentos que podem ser usados para codificar e
decodificar texto.
Uma cifra de livro é uma cifra para a qual a chave é derivada de um livro. Por
exemplo, pode mapear cada caractere no texto simples para o índice numérico da
primeira ocorrência desse caractere no livro (ou em uma página do livro). A suposição
é que o remetente e o destinatário da mensagem codificada concordaram
previamente sobre o livro, mas um adversário que intercepta a mensagem codificada
não sabe qual livro foi usado para codificá-la.
Se texto simples fosse “não é não” e o livro começasse com “Era uma vez, em
uma casa em uma terra distante”, a chamada gen_code_keys(livro, texto_simples)
retornaria
{'n': '1', 'o': '7', ' ': '4', 'i': '13', 's': '26'}
Observe, a propósito, que o é mapeado para sete em vez de zero porque o e O são
caracteres diferentes. Se o livro fosse o texto de Don
Machine Translated by Google
{'n': '1', 'o': '13', ' ': '2', 'i': '6', 's': '57'}
Como os caracteres no texto simples podem ser substituídos por vários caracteres
no texto cifrado, usamos * para separar os caracteres no texto cifrado. O
operador .join é usado para transformar a lista de strings em uma única string.
1*13*2*6*57*2*1*13
{'1': 'n', '13': 'o', '2': ' ', '6': 'i', '57': 's'}
Machine Translated by Google
Se um caractere ocorre no texto simples, mas não no livro, algo ruim acontece. O
dicionário code_keys irá mapear cada um desses caracteres para -1, e decode_keys irá
mapear -1 para qualquer que seja o último caractere do livro.
Exercício com os dedos: Resolva o problema descrito no parágrafo anterior. Dica: uma
maneira simples de fazer isso é criar um novo livro anexando algo ao livro original.
22*13*33*137*59*11*23*11*1*57*6*13*1*2*6*57*2*6*1*22*13*33*1
37*59*11*23*11*1*57*6*173*7*11
lista
de iteradores de tipo de
objeto iterável de
atribuição
múltipla de tupla
função de id de
efeito colateral de
igualdade de objeto
cópia rasa
de
clonagem de aliasing
Machine Translated by Google
compreensão
do dicionário
de
tipo hashável
ver objeto
34 Lembre-se de que, por enquanto, você deve pensar em um método simplesmente como uma
função que é invocada usando a notação de ponto.
Você pode ter ouvido falar de recursão e provavelmente pensa nela como uma
técnica de programação bastante sutil. Essa é uma lenda urbana encantadora
espalhada por cientistas da computação para fazer as pessoas pensarem que
somos mais inteligentes do que realmente somos. A recursão é uma ideia
importante, mas não é tão sutil e é mais do que uma técnica de programação.
Como método descritivo, a recursão é amplamente utilizada, mesmo por
pessoas que nunca sonhariam em escrever um programa. Considere parte do
código legal dos Estados Unidos que define a noção de cidadania por “direito de
primogenitura”. Grosso modo, a definição é a seguinte
valor com o qual foi chamado. Eventualmente, a recursão termina com a chamada
fact_rec(1).
fêmeas(0) = 1
fêmeas(1) = 1 fêmeas(n
+ 2) = fêmeas(n+1) + fêmeas(n)
Ele tem dois casos básicos, não apenas um. Em geral, podemos ter
quantos casos básicos quisermos.
No caso recursivo, há duas chamadas recursivas, não apenas uma.
Novamente, pode haver quantos quisermos.
6.2 Palíndromos
Machine Translated by Google
Dividir para conquistar é uma ideia antiga. Júlio César praticava o que os
romanos chamavam de divide et impera (dividir para reinar). Os britânicos
praticaram brilhantemente para controlar o subcontinente indiano.
Benjamin Franklin estava bem ciente da experiência britânica no uso dessa técnica,
o que o levou a dizer na assinatura dos Estados Unidos
Declaração de Independência: “Devemos todos ser enforcados juntos, ou
seguramente todos seremos enforcados separadamente.”
Nesse caso, resolvemos o problema dividindo o problema original em uma
versão mais simples do mesmo problema (verificando se uma string mais curta é um
palíndromo) e algo simples que sabemos fazer (comparando caracteres únicos) e,
em seguida, combinando os soluções com o operador lógico e. A Figura 6-5 contém
algum código que pode ser usado para visualizar como isso funciona.
Machine Translated by Google
Executando o código
print('Tente dogGod')
print(is_palindrome('dogGod')) print('Try
doGood')
print(is_palindrome('doGood'))
estampas
Tente dogGod
is_pal chamado com doggod is_pal
chamado com oggo is_pal chamado
com gg is_pal chamado com
recursão
caso base
funções auxiliares
avaliação de curto-circuito
MÓDULOS E ARQUIVOS
7.1 Módulos
import circle pi = 3
print(pi)
print(circle.pi)
print(circle.area(3))
print(circle.circumference(3))
print(circle.sphere_surface(3))
vai imprimir
3
3.14159
28.27431
18.849539999999998
113.09724
da importação do círculo *
print(pi)
print(circle.pi)
print(cal.LocaleTextCalendar(locale='fr_FR').formatmonth(204 9, 3))
print(cal.LocaleTextCalendar(locale='pl_PL').formatmonth(204 9, 3))
outro:
thanksgiving = mês[4][cal.THURSDAY] return ação de
graças print('In 2011', 'US
Thanksgiving was on November',
find_thanksgiving(2011))
def shopping_days(ano):
"""ano um número >= 1941
retorna o número de dias entre o Dia de Ação de Graças dos EUA
e
Natal no ano"""
Exercício de dedo: Desde 1958, o Dia de Ação de Graças canadense ocorre na segunda
segunda-feira de outubro. Escreva uma função que receba um ano (>1957) como parâmetro
e retorne o número de dias entre o Dia de Ação de Graças canadense e o Natal.
7.3 Arquivos
Todo sistema de computador usa arquivos para salvar coisas de uma computação para
outra. O Python fornece muitos recursos para criar e acessar arquivos. Aqui ilustramos
alguns dos básicos.
Cada sistema operacional (por exemplo, Windows e macOS) vem com seu próprio
sistema de arquivos para criar e acessar arquivos. O Python alcança a independência do
sistema operacional acessando arquivos por meio de algo chamado de identificador de
arquivo. O código
instrui o sistema operacional a criar um arquivo com o nome kids e retornar um identificador
de arquivo para esse arquivo. O argumento 'w' para abrir indica que o arquivo deve ser
aberto para escrita. O código a seguir abre um arquivo, usa o método write para escrever
duas linhas. (Em uma string Python, o caractere de escape “\” é usado para indicar que o
próximo caractere deve ser tratado de maneira especial. Neste exemplo, a string '\n' indica
um caractere de nova linha.) Por fim, o código fecha o arquivo. Lembre-se de fechar um
arquivo quando o programa terminar de usá-lo. Caso contrário, existe o risco de algumas
ou todas as gravações não serem salvas.
Você pode garantir que não se esqueça de fechar um arquivo abrindo-o usando
uma instrução with . Código do formulário
abre um arquivo, vincula um nome local a ele que pode ser usado no code_block
e, em seguida, fecha o arquivo quando o code_block é encerrado.
O código a seguir abre um arquivo para leitura (usando o argumento 'r') e
imprime seu conteúdo. Como o Python trata um arquivo como uma sequência de
linhas, podemos usar uma instrução for para iterar sobre o conteúdo do arquivo.
A linha extra entre David e Andrea existe porque print inicia uma nova linha toda
vez que encontra o '\n' no final de cada linha no arquivo. Poderíamos ter evitado
imprimir a linha extra escrevendo print(line[:-1]).
O código
name_handle.write('Andrea')
name_handle.close()
name_handle = open('kids', 'r') for line in
name_handle: print(line)
módulo
declaração de
importação nomes
identificador de arquivo
escrevendo e lendo
de arquivos
anexando a arquivos
TESTE E DEBUGAÇÃO
Odiamos trazer isso à tona, mas o Dr. Pangloss49 estava errado. Não vivemos no
“melhor dos mundos possíveis”. Há alguns lugares onde chove muito pouco e
outros onde chove muito. Alguns lugares são muito frios, outros muito quentes,
alguns muito quentes no verão e muito frios no inverno. Às vezes, o mercado de
ações cai — muito.
Às vezes, os trapaceiros vencem (consulte Houston Astros). E, irritantemente,
nossos programas nem sempre funcionam corretamente na primeira vez que os
executamos.
Livros foram escritos sobre como lidar com esse último problema, e há muito
a ser aprendido com a leitura desses livros.
No entanto, com o objetivo de fornecer algumas dicas que podem ajudá-lo a
resolver o próximo problema a tempo, este capítulo fornece uma discussão
altamente condensada do tópico. Embora todos os exemplos de programação
estejam em Python, os princípios gerais se aplicam ao funcionamento de qualquer
sistema complexo.
O teste é o processo de execução de um programa para tentar verificar se ele
funciona como pretendido. A depuração é o processo de tentar corrigir um
programa que você já sabe que não funciona conforme o esperado.
Teste e depuração não são processos nos quais você deve começar a pensar
depois que um programa foi criado. Bons programadores projetam seus programas
de forma a torná-los mais fáceis de testar e depurar. A chave para fazer isso é
dividir o programa em componentes separados que podem ser implementados,
testados e depurados independentemente de outros componentes. Neste ponto
do livro, discutimos apenas um mecanismo para modularizar programas, a função.
Portanto, por enquanto, todos os nossos exemplos serão baseados em funções.
Quando chegarmos a outros mecanismos, em particular as classes, voltaremos a
alguns dos tópicos abordados neste capítulo.
Machine Translated by Google
8.1 Teste
O objetivo do teste é mostrar que existem bugs, não mostrar que um programa
está livre de bugs. Para citar Edsger Dijkstra, “o teste de programa pode ser
usado para mostrar a presença de bugs, mas nunca para mostrar sua ausência!”
50 Ou, como Albert Einstein supostamente disse: “Nenhuma quantidade de
experimentação pode provar que estou certo; um único experimento pode
provar que estou errado.”
Porque isto é assim? Mesmo o mais simples dos programas tem bilhões de
entradas possíveis. Considere, por exemplo, um programa que pretende atender
à especificação
as entradas possíveis são todas as combinações de pares de números inteiros. Uma maneira
de particionar esse conjunto é nesses nove subconjuntos:
Para a maioria dos programas, encontrar um bom particionamento das entradas é muito
mais fácil falar do que fazer. Normalmente, as pessoas confiam em heurísticas baseadas na
exploração de diferentes caminhos por meio de alguma combinação de código e especificações.
A heurística baseada na exploração de caminhos através do código se enquadra em uma
classe chamada teste de caixa de vidro (ou caixa branca) .
Heurísticas baseadas na exploração de caminhos através da especificação se enquadram em
uma classe chamada teste de caixa preta.
Essa independência reduz a probabilidade de gerar suítes de teste que exibem erros
correlacionados com erros no código. Suponha, por exemplo, que o autor de um programa
tenha feito a suposição implícita, mas inválida, de que uma função nunca seria chamada com
um número negativo. Se a mesma pessoa construísse o conjunto de testes para o programa,
provavelmente repetiria o erro e não testaria a função com um argumento negativo.
Machine Translated by Google
Como dissemos anteriormente, uma boa maneira de gerar dados de teste de caixa preta é
para explorar caminhos através de uma especificação. Considere, a especificação
épsilon > 0
Retorna o resultado tal que
x-epsilon <= resultado*resultado <= x+epsilon"""
def is_prime(x):
"""Assume que x é um int não negativo
Retorna True se x for primo; Falso caso contrário"""
se x <= 2:
retorna falso
para i no intervalo(2, x): se x%i
== 0:
retorna falso
retornar Verdadeiro
Machine Translated by Google
def abs(x):
"""Assume que x é um int
Retorna x se x>=0 e –x caso contrário"""
se x < -1:
retornar -x
outro:
retornar x
Apesar das limitações do teste de caixa de vidro, geralmente vale a pena seguir
algumas regras práticas:
Machine Translated by Google
• O loop não foi inserido (por exemplo, se o loop estiver iterando sobre os
elementos de uma lista, verifique se ele foi testado na lista vazia).
encontre casos onde o loop sai porque len(L) é maior que zero e casos
onde sai porque L[i] == e.
Para funções recursivas, inclua casos de teste que façam com que a função retorne
sem chamadas recursivas, exatamente uma chamada recursiva e mais de uma
chamada recursiva.
Configure o ambiente necessário para invocar o programa (ou unidades) para testar.
8.2 Depuração
raça em programas. Se o seu programa tem vários bugs, é porque você cometeu
vários erros.
Os bugs de tempo de execução podem ser categorizados em duas dimensões:
>>> bobo(2)
Digite o elemento: a
Digite o elemento: b
Machine Translated by Google
A boa notícia é que ele falha até mesmo neste teste simples, então você não precisa
digitar milhões de caracteres. A má notícia é que você não tem ideia de por que falhou.
Nesse caso, o código é pequeno o suficiente para que você provavelmente consiga
olhar para ele e encontrar o bug (ou bugs). No entanto, vamos fingir que é muito grande
para fazer isso e começar a reduzir sistematicamente o espaço de busca.
Freqüentemente, a melhor maneira de fazer isso é conduzir uma pesquisa de bisseção.
Encontre algum ponto na metade do código e elabore um experimento que permitirá que
você decida se há um problema antes desse ponto que possa estar relacionado ao sintoma.
(Claro, pode haver problemas depois desse ponto também, mas geralmente é melhor
procurar um problema de cada vez.) Ao escolher esse ponto, procure um local onde alguns
valores intermediários facilmente examinados forneçam informações úteis. Se um valor
intermediário não for o que você esperava, provavelmente há um problema que ocorreu
antes desse ponto no código. Se todos os valores intermediários parecerem corretos, o bug
provavelmente está em algum lugar posterior no código. Esse processo pode ser repetido
até que você restrinja a região em que um problema está localizado a algumas linhas de
código ou a algumas unidades, se estiver testando um sistema grande.
def bobo(n):
"""Assume que n é um int > 0
Obtém n entradas do usuário
Imprime 'Sim' se a sequência de entradas forma um palíndromo;
result.append(elem)
print(result) if
is_pal(result): print('Yes')
outro:
print('Não')
Vamos tentar isso e ver se result tem o valor correto após o loop for .
Sim, mas infelizmente o programa ainda imprime Sim. Agora, temos
motivos para acreditar que um segundo bug está abaixo da instrução
print . Então, vamos ver is_pal. Insira a linha
print(temperatura, x)
difícil começar. 57 Mas ele nunca depurou um software. Esta subseção contém algumas
dicas pragmáticas sobre o que fazer quando a depuração fica difícil.
um nome errado, por exemplo, digitou uma letra minúscula quando deveria
ter digitado uma maiúscula?
• Falha ao reinicializar uma variável?
Pare de se perguntar por que o programa não está fazendo o que você
deseja. Em vez disso, pergunte a si mesmo por que ele está fazendo o que
está fazendo. Essa deve ser uma pergunta mais fácil de responder e
provavelmente será um bom primeiro passo para descobrir como consertar o programa.
Sempre certifique-se de que você pode voltar para onde você está.
Nada é mais frustrante do que perceber que uma longa série de mudanças o deixou
mais longe do objetivo do que quando começou e não ter como voltar ao ponto de
partida. O espaço em disco geralmente é abundante. Use-o para armazenar versões
antigas do seu programa.
Finalmente, se houver muitos erros inexplicáveis, você pode considerar se
encontrar e corrigir um bug de cada vez é a abordagem correta. Talvez seja melhor
você pensar em uma maneira melhor
Machine Translated by Google
para organizar seu programa ou talvez um algoritmo mais simples que será mais fácil de implementar
corretamente.
testando
depuração
conjunto de testes
unidade teste de
canhoto de teste
bug
aberto bug
oculto bug
persistente bug
intermitente programação
confirmação
Machine Translated by Google
pesquisa de bisseção
51 Ou, por falar nisso, aqueles que dão notas a conjuntos de problemas muito grandes
cursos de programação.
55 O software inadequado foi um fator que contribuiu para a perda de 346 vidas em dois
acidentes aéreos comerciais entre outubro de 2018 e março de 2019.
57 Ele também teria dito a JFK: “Não compre um único voto a mais do que o necessário.
Eu serei amaldiçoado se vou pagar por um deslizamento de terra.”
Machine Translated by Google
EXCEÇÕES E AFIRMAÇÕES
Até agora, tratamos as exceções como eventos terminais. Quando uma exceção é
levantada, o programa termina (crashes pode ser uma palavra mais apropriada
neste caso), e voltamos ao nosso código e tentamos descobrir o que deu errado.
Quando surge uma exceção que causa o encerramento do programa, dizemos que
uma exceção não tratada foi gerada.
tente
bloco de código
exceto (lista de nomes de exceção): bloco de código
outro:
bloco de código
Se você sabe que uma linha de código pode gerar uma exceção quando
executada, você deve lidar com a exceção. Em um programa bem escrito, exceções
não tratadas devem ser a exceção.
Considere o código
Na maioria das vezes, esse código funcionará bem, mas falhará se num_failures for
zero. A tentativa de dividir por zero fará com que o sistema de tempo de execução
do Python gere uma exceção ZeroDivisionError e a instrução de impressão nunca
será alcançada.
É melhor escrever algo ao longo das linhas de
Machine Translated by Google
try:
success_failure_ratio = num_successes/num_failures print('A taxa de sucesso/falha
é', success_failure_ratio) exceto ZeroDivisionError:
def soma_dígitos:
"""Assume que s é uma string
Retorna a soma dos dígitos decimais em s
Por exemplo, se s é 'a2b3c' ele retorna 5"""
nesse caso, o bloco except será inserido se qualquer uma das exceções
listadas for levantada dentro do bloco try .
Como alternativa, podemos escrever um bloco except separado para
cada tipo de exceção, o que permite ao programa escolher uma ação com
base na qual a exceção foi levantada. Se o programador escrever
Machine Translated by Google
exceto:
vinculado ao argumento (uma string neste caso) associado a ValueError quando ele
foi levantado. Quando o código
enquanto verdadeiro:
val = input('Digite um número inteiro: ') try: val =
int(val)
print('O quadrado do
número digitado é',
val**2)
break #para sair do loop while exceto ValueError:
print(val, 'não é um número
inteiro')
retornando
except ValueError: print(val,
'não é um número inteiro')
Melhor ainda, essa função pode ser generalizada para solicitar qualquer tipo de
entrada:
Com exceções, o programador ainda precisa incluir o código que lida com a
exceção. No entanto, se o programador esquecer de incluir esse código e a
exceção for levantada, o programa será interrompido imediatamente. Isto é uma
coisa boa. Ele alerta o usuário do programa que algo problemático aconteceu.
(E, como discutimos no Capítulo 8, bugs evidentes são muito melhores do que
bugs encobertos.) Além disso, dá a alguém que está depurando o programa uma
indicação clara de onde as coisas deram errado.
Machine Translated by Google
Não pense em exceções apenas como erros. Eles são um mecanismo de fluxo
de controle conveniente que pode ser usado para simplificar programas.
Em muitas linguagens de programação, a abordagem padrão para lidar
com erros é fazer com que as funções retornem um valor (geralmente algo
análogo ao None do Python) indicando que algo está errado. Cada chamada
de função deve verificar se esse valor foi retornado. Em Python, é mais comum
que uma função gere uma exceção quando não pode produzir um resultado
consistente com a especificação da função.
levantar exceçãoNome(argumentos)
def find_an_even(L):
"""Assume que L é uma lista de inteiros
Retorna o primeiro número par em L
Aumenta ValueError se L não contiver um par
número"""
9.3 Afirmações
ou
afirmar expressão booleana, argumento
Asserções são uma ferramenta útil de programação defensiva. Eles podem ser
usados para confirmar que os argumentos para uma função são de tipos apropriados.
Eles também são uma ferramenta de depuração útil. Eles podem ser usados, por
exemplo, para confirmar que os valores intermediários têm os valores esperados ou
que uma função retorna um valor aceitável.
exceções
levantando uma exceção
exceção não tratada
exceção tratada
construção try-except
catch (uma exceção)
funções polimórficas objetos
de primeira classe
declaração de aumento
afirmações
Machine Translated by Google
10
AULAS E ORIENTADAS A OBJETOS
PROGRAMAÇÃO
Agora voltamos nossa atenção para nosso último tópico importante relacionado
à programação em Python: usar classes para organizar programas em torno de
abstrações de dados.
As classes podem ser usadas de várias maneiras diferentes. Neste livro,
enfatizamos seu uso no contexto da programação orientada a objetos. A
chave para a programação orientada a objetos é pensar nos objetos como
coleções de dados e métodos que operam nesses dados.