Escolar Documentos
Profissional Documentos
Cultura Documentos
Linguagem de Programação
Texto base
1
Introdução à disciplina e conceitos iniciais
Resumo
Nesta aula os objetivos são: (I) introduzir as diretrizes da disciplina, como conteúdo
programático, metodologia de ensino, critérios de avaliação e bibliografia; (II)
conceituar os termos “algoritmo”, “lógica de programação”, “linguagem de
programação”, “código-fonte” e “programa”; (III) simular o processo para resolução
de problemas computacionais sem o uso de computadores (computação desplugada).
1.1. Motivação
A vida das pessoas é constantemente alterada pela evolução das tecnologias,
inclusive pela tecnologia computacional. Diversos aspectos da vida são impactados pelo
progresso das descobertas relacionadas à área da Ciência da Computação. Você pode
imaginar o quão diferente seria sua rotina sem a existência de notebooks e smartphones?
Brookshear (2008, p. 17) menciona que, como uma disciplina, “[a Ciência da
Computação] busca construir uma base científica para diversos tópicos, tais como a
construção e a programação de computadores, o processamento de informações, as
soluções algorítmicas de problemas e o processo algorítmico propriamente dito.”.
Podemos resolver problemas com a construção de algoritmos e, para automatizá-los,
aplicaremos lógica de programação com instruções em linguagens de programação,
criando códigos-fonte que se tornarão programas executados por computadores.
1.3. Algoritmos
Conforme descrito por Ziviani (1999, p. 1) “[algoritmo é] uma sequência de
passos de ações executáveis para a obtenção de uma solução para um determinado tipo
de problema”. Podemos imaginar um algoritmo como um procedimento que visa
resolver um problema, formado por uma sequência ordenada e finita de passos passíveis
de serem executados, assim como conceituado por Pereira (2010).
O termo algoritmo não está diretamente relacionado aos computadores, muito
menos depende deles, pois podemos criar algoritmos que não serão, necessariamente,
executados em um computador. Imagine, por exemplo, uma receita de bolo:
1) Deixe disponível em sua bancada os ingredientes necessários: açúcar, farinha
de trigo, margarina, ovos, leite e fermento em pó;
2) Bata as claras dos ovos para formar claras em neve;
3) Misture a margarina com as gemas e o açúcar de modo a obter uma massa
homogênea;
4) Aos poucos coloque leite e farinha de trigo na massa, continue batendo;
5) Acrescente as claras em neve e o fermento na massa;
6) Despeje a massa em uma forma adequada, untada e enfarinhada;
7) Coloque a forma em um forno em temperatura média, por volta de 180 ºC,
por cerca de 40 minutos.
Essa receita de bolo é um algoritmo! Note como os algoritmos fazem parte do
dia a dia de qualquer pessoa. Existem algoritmos que são executados inconscientemente,
respirar é um deles. Quando você se desloca para o trabalho, executa um algoritmo, pois
segue uma sequência de passos logicamente ordenados para resolver um problema, que
podemos interpretar como atingir um objetivo. Desde trocar de roupa, passando por
embarcar em um transporte, até registrar sua entrada no local da empresa.
Assim como ficou evidente no algoritmo para construir um bolo, é possível criar
algoritmos com maior ou menor detalhamento. Você reparou que não foram definidas as
quantidades dos ingredientes? Isso pode ser um problema para algumas pessoas que
tentarão executar esse algoritmo e não tem conhecimento prévio. Já para outras pessoas
mais experientes, que apenas precisam lembrar dos passos gerais, pode ser suficiente.
Uma característica importantíssima sobre os algoritmos é que a construção pode
variar de acordo com quem os elabora. Portanto, nem todos os algoritmos que objetivam
resolver o mesmo problema terão a mesma sequência de passos, afinal dependerá,
dentre outros fatores, da experiência, vocabulário, concisão e lógica do autor. Em
contraste, a execução dos algoritmos, se forem bem formulados, independe de quem os
executará, bastando que os passos sejam rigorosamente seguidos.
Genericamente, podemos dizer que lógica é uma parte da filosofia que trata das
formas do pensamento em geral (dedução, indução, inferência, hipótese etc.) e das
🏹 VAMOS PRATICAR!
1) A partir do estado inicial, com os personagens na margem leste do rio, escreva uma
sequência de passos que leve-os para a margem oeste do rio, considerando tudo o que
foi descrito anteriormente. Lembre-se que queremos um algoritmo, então os passos
devem ser logicamente ordenados, não ambíguos e suficientemente simples para serem
entendidos e executados por outra pessoa. Faça da melhor forma que puder.
2) Reescreva seu algoritmo do exercício anterior, porém sem utilizar palavras. Adote
que poderá usar apenas os símbolos H, L, B, R, 0, 1 (representando os personagens
Homem, Lobo, Bode e Repolho e os lados leste e oeste, respectivamente) e → (para
indicar que, naquela instrução, os personagens à esquerda da seta irão juntos de barco
para o lado do lago indicado à direita da seta). Faça uma representação e atualize-a a
cada novo estado gerado pela execução de uma instrução do algoritmo.
Figura 1.2: Algoritmo e CS (nota sobre direitos autorais 1). Fonte: Elaborado pelo autor.
A segunda instrução solicita que Megan leia outro papel e guarde o valor na
parte C da lousa. Se a instrução indicasse que o valor lido deveria ser guardado em A, o
valor prévio em A seria sobrescrito, afinal não pode haver dois valores ocupando o
1
A figura utilizada para representar Megan no Computador Simplificado é de autoria de Fredy Sujono
(https://www.iconfinder.com/freud) e está autorizada para uso comercial mediante indicação de créditos.
mesmo espaço ao mesmo tempo. Após a execução desta instrução, o estado será
alterado conforme o ilustrado na Figura 1.4.
computador os dados são apagados e; (e) o diário é a memória auxiliar, que serve para
persistir os dados, ou seja, mantê-los registrados para que possam ser consultados
futuramente, como HDDs, SSDs, cartões de memória, pendrives etc.
Você pode estar se perguntando: “qual seria a equivalência do algoritmo em um
sistema computacional real?”. Resposta: ao código-fonte de um programa escrito em
uma linguagem de programação. Aprender essa linguagem será um de nossos objetivos!
📚 VAMOS LER!
Você sabia que existe um site dedicado a computação desplugada? Sim! Um site
dedicado a propagação desse método para ensinar e aprender conceitos de computação e
pensamento computacional sem o uso de computadores. Veja atividades e desafios
interessantes sobre diversos assuntos: como os números binários funcionam? Como
saber se um ano é bissexto? Como verificar se um número é par ou ímpar? É possível
adivinhar o número que alguém está pensando com pouquíssimas tentativas?
Para quem ficou interessado, o endereço é: https://csunplugged.org/en/
Bibliografia e referências
Texto base
2
Ambiente de desenvolvimento, tipos de dados,
operadores aritméticos e variáveis
Resumo
Nesta aula os objetivos são: (I) preparar o ambiente computacional necessário para a
disciplina; (II) acessar as duas ferramentas básicas utilizadas na disciplina para o
desenvolvimento de programas em Python (interpretador interativo e editor de código
fonte); (III) conceituar e utilizar tipos de dados primitivos, constantes e variáveis; (IV)
conhecer os operadores aritméticos; (V) manipular o sistema online Python Tutor.
2.1. Motivação
Para criarmos nossos programas, teremos que usar um ambiente que permita
escrever algoritmos em uma linguagem de programação e que possibilite a execução
automática dessas instruções. Por isso, nesta aula faremos o download, instalação e
execução do interpretador da linguagem Python e do IDLE, um software que possibilita
o processo básico para criação e execução de programas em Python. Introduziremos
conceitos básicos de Python para construir instruções simples e, por fim, manipularemos
um sistema online que possibilita a visualização passo a passo do que ocorre quando
💎
nossos códigos-fonte são executados, potencialmente facilitando a aprendizagem.
VOCÊ CONHECE?
Guido van Rossum é holandês, programador, matemático e criador da
linguagem Python! Contratado pelo Google entre 2005 a 2012 e, em
2013, começou a trabalhar na Dropbox. Se aposentou em 2019, mas
em 2020, com 64 anos, abandonou a aposentadoria para ingressar na
divisão de desenvolvimento da Microsoft.
Fonte da imagem: https://twitter.com/gvanrossum/photo
Figura 2.1: Site para download do Python. Fonte: Elaborado pelo autor.
2.2.2. IDLE
O IDLE1 (Ambiente Integrado de Desenvolvimento e Aprendizagem, tradução
nossa) é o ambiente em que programaremos e foi instalado juntamente com o
interpretador do Python no procedimento anterior. Este ambiente é programado na
própria linguagem Python e funciona igualmente em outros sistemas operacionais. Para
acessá-lo, procure-o no Menu Iniciar, como ilustrado na Figura 2.4.
1
No original: Integrated Development and Learning Environment.
Figura 2.5: Interpretador interativo do IDLE Python (Shell). Fonte: Elaborado pelo autor.
Figura 2.6: Execução de uma instrução inserida na Shell. Fonte: Elaborado pelo autor.
🏹 VAMOS PRATICAR!
1) Execute mais instruções na Shell, por exemplo substituindo 'Hello World!' pelo
seu nome. Repita com conteúdos diferentes entre os apóstrofos. Utilize os dois
primeiros atalhos para verificar se consegue regressar e avançar às instruções inseridas.
2) Refaça o exercício anterior, porém trocando apóstrofos por aspas. Depois elimine as
aspas. Ocorreu algum erro? Você descobrirá a razão mais detalhadamente nas próximas
aulas, por enquanto basta saber que todo texto deve estar entre apóstrofos ou aspas.
Figura 2.7: Menu para acesso ao editor de código-fonte. Fonte: Elaborado pelo autor.
Insira a Codificação 2.1 no editor, de modo a deixá-lo como na Figura 2.9. Não
se preocupe em entender o código completo, as instruções serão explicadas futuramente.
n1 = int(input('Número 1: '))
n2 = int(input('Número 2: '))
sub = n1 - n2
print('A subtração de', n1, '-', n2, 'é', sub)
Codificação 2.1: Programa para exibir a subtração de dois números dados pelo usuário.
Figura 2.9: Editor preenchido com um código-fonte. Fonte: Elaborado pelo autor.
Repare que a barra de título da janela consta como “untitled”, indicando que o
arquivo não foi salvo. Para executarmos o código-fonte e visualizar o programa
funcionando, precisamos salvar este arquivo. Para isso, acesse o menu “File > Save”,
defina o nome do arquivo e o local em que será salvo, conforme a Figura 2.10.
Figura 2.10: Salvamento do arquivo criado no editor. Fonte: Elaborado pelo autor.
Figura 2.11: Execução do código-fonte e programa gerado. Fonte: Elaborado pelo autor.
👁️🗨️
VOCÊ SABIA?
▪️ Existe material oficial sobre o IDLE, com detalhes e outros recursos interessantes do
ambiente. Interessado? Basta acessar o link: https://docs.python.org/3/library/idle.html
▪️ Além do IDLE existem outras ferramentas que podem ser usadas para criar
programas em Python, várias delas são citadas neste link:
https://python.org.br/ferramentas/
2.3.2. Operadores
Em Python, assim como em outras linguagens de programação, operadores são
símbolos pré-definidos que realizam uma operação sobre um ou mais operandos,
produzindo um valor como resultado. O tipo do valor resultante dependerá do operador
e dos operandos envolvidos. Quando um operador realiza uma operação entre 2
operandos, chamamos-o de binários, quando realiza uma operação com apenas 1
operando, chamamos-o de unários e, por fim, ternários quando envolvem 3 operandos.
+ 7 + 4 → 11
Soma o primeiro operando com o segundo.
(binário) 1 + 2.0 → 3.0
- 15 - 5 → 10
Subtrai o segundo operando do primeiro.
(binário) 5 - 15 → -10
- - (3) → -3
Inverte o sinal do operando à direita.
(unário) - (-7) → 7
Note que, exceto pela divisão real, quando todos os operandos envolvidos na
operação forem números inteiros o resultado será um número inteiro, porém, basta um
operando real para que o resultado da operação resulte em um número real.
🏹 VAMOS PRATICAR!
3) Resolva as operações a seguir usando apenas lápis, papel e calculadora, em seguida
confira os resultados inserindo as operações na Shell do Python.
a) 893 // 10 b) 893 / 10 c) 25.0 // 2 d) 25.0 / 2
e) 5678 % 1 f) 5678 % 10 g) 5678 % 100 h) 5678 % 1000
i) 5678 // 1 j) 5678 // 10 k) 5678 // 100 l) 5678 // 1000
m) 123 // 1000 n) 123 / 1000 o) 0 / 0 p) 0 ** 0
q) 1e3 + 5 r) 0x10 - 3.5 s) 9 ** 0.5 t) 81 ** 0.5
2.3.3. Variáveis
Uma variável é um espaço de memória associado a um identificador, ou seja, um
nome, e serve para guardar valores que o programa poderá acessar e modificar. Toda
variável possui um identificador único, de forma que possa ser referenciada pelo
programador sem ambiguidade em qualquer parte do programa.
Em Python, uma variável é criada no momento em que um valor é atribuído a
um identificador válido. A atribuição é feita colocando um identificador à esquerda de
um sinal de igual e um valor à direita deste mesmo operador, conforme a Figura 2.12.
Figura 2.12: Atribuição de um valor a uma variável. Fonte: Elaborado pelo autor.
O conteúdo de uma variável pode “variar”, ou seja, uma mesma variável pode
guardar valores diferentes em momentos diferentes de um programa. Lembre-se: uma
variável só guarda um valor por vez, portanto a cada nova atribuição o valor atual será
sobrescrito pelo novo. Execute a Codificação 2.2 na Shell e reflita sobre os resultados.
>>> a = True
>>> type(a)
>>> a = 123
>>> a = 'linda casa amarela'
>>> a = 4.40
>>> type(a)
Codificação 2.2: Uma mesma variável recebendo valores diferentes.
2.3.4. Identificadores
Um identificador de uma variável, também referido como nome, é formado por
uma sequência de um ou mais caracteres, de acordo com as seguintes regras:
● Simplificadamente, pode conter apenas combinações de letras, dígitos e
sublinhados (não pode conter símbolos especiais como &, ¨, %, $, #, @, !);
2.3.6. Comentários
Podemos inserir comentários em nossos códigos-fonte, algo útil para ajudar
tanto outros programadores que lerão nossos códigos quanto a nós mesmos para
recordar a razão de determinadas instruções. Comentários são ignorados na execução do
2
PEP 8 -- Style Guide for Python Code | Python.org
programa, portanto é algo para auxiliar humanos, não computadores. Linhas iniciadas
com # são interpretadas como comentários, conforme a Codificação 2.4.
a = 1045.00 # salário mínimo de 2020
b = 1100.00 # salário mínimo de 2021
c = b - a # aumento do salário mínimo
print('O salário mínimo aumentou:', c, 'reais')
Codificação 2.4: Exemplos de comentários em um código-fonte Python.
Ainda não discutimos o que é a instrução print(), isso será feito em outra
aula, mas nosso palpite é que você já é capaz de imaginar o que ela faz.
Use os botões “< Prev” e “Next >” para controlar a execução. Veja no canto
inferior-direito da tela as variáveis criadas, com seus respectivos valores, e no canto
superior-direito o que foi exibido na tela, conforme a Figura 2.14. A seta verde-claro
indica a instrução que acabou de ser executada e a vermelha indica a próxima instrução.
Bibliografia e referências
PSF. A Referência da Linguagem Python. 2021. Disponível em: <https://docs.python.
org/pt-br/3/reference/index.html>. Acesso em: 28 fev. 2021.
PSF. Lexical analysis. 2020. Disponível em: <https://docs.python.org/3/reference/
lexical_analysis.html>. Acesso em: 21 jan. 2021.
PSF. Expressions. 2020. Disponível em: <https://docs.python.org/3/reference/
expressions.html>. Acesso em: 21 jan. 2021.
STURTZ, J. Operators and Expressions in Python. Real Python, 2018. Disponível em:
<https://realpython.com/python-operators-expressions/>. Acesso em: 21 jan. 2021.
Texto base
3
Expressões, operadores de atribuição, funções
integradas e entrada/saída de dados
Resumo
Nesta aula os objetivos são: (I) relembrar expressões; (II) conhecer os operadores de
atribuição composta; (III) compreender o conceito de precedência e associatividade de
operadores; (IV) entender como Python avalia expressões; (V) descobrir o que são
funções e como usar funções integradas; (VI) exibir dados para o usuário; (VII) obter
entradas do usuário; (VIII) conhecer tipos de erros comuns em programação.
3.1. Motivação
Como já aprendemos o que são expressões, relembraremos pontos importantes e
conheceremos outros, principalmente para compreender a forma que Python avalia
expressões. Poderemos até substituir nossas calculadoras pela Shell, que possui muitas
facilidades! Abordaremos funções, um recurso de programação que possibilita, por
exemplo, usar códigos de outros programadores em nossos programas. Conheceremos
funções para entrada e saída de dados, permitindo programas mais personalizados. Por
fim, conheceremos alguns tipos de erros comuns na criação de programas.
💎 VOCÊ CONHECE?
Ada Lovelace, matemática, escritora e reconhecida como a
primeira programadora do mundo! Escreveu um algoritmo que
poderia ser processado na máquina analítica de Charles Babbage. A
linguagem de programação Ada foi batizada em sua homenagem,
assim como diversos eventos e premiações científicas.
Fonte da imagem: https://www.biography.com/scholar/ada-lovelace
3.2. Expressões
Uma expressão é uma combinação de operandos com zero ou mais operadores,
resultando em um valor, sendo que os operandos podem ser, por exemplo, constantes e
variáveis. Para que uma expressão esteja correta, essa combinação precisa ser válida.
Um único operando já é considerado uma expressão válida. Também podemos usar
parênteses, como na matemática. Teste na Shell a Codificação 3.1.
>>> 3.1415 # expressão válida, resulta em 3.1415
>>> (2 + 5) # expressão válida, resulta em 7
>>> 3.0 * 2 # expressão válida, resulta em 6.0
>>> 10 / 0 # expressão válida, porém resulta em erro de
aritmética (erro de divisão por zero)
>>> +123 # expressão válida, resulta em 123
>>> 'oi' # expressão válida, resulta em 'oi'
>>> 123+ # expressão inválida, falta um operando à direita
>>> 2 * 3) # expressão inválida, parênteses desbalanceados
Codificação 3.1: Exemplos de expressões válidas e inválidas.
lembre-se que para acessar o valor da variável m, basta inserir na Shell >>> m e
pressionar [ENTER].
Tabela 3.1: Relação parcial dos operadores de atribuição composta do Python.
>>> m = 2 # m recebe 2
>>> m **= 5 # m recebe 32
>>> m //= 4 # m recebe 8
>>> m += 1 # m recebe 9
>>> m /= 2 # m recebe 4.5
>>> m %= 2 # m recebe 0.5
>>> m -= 0.2 # m recebe 0.3
>>> m *= 5 # m recebe 1.5
Codificação 3.3: Uma mesma variável recebendo diversas atribuições.
Talvez você tenha achado estranho que o segundo valor atribuído à variável a
seja diferente do segundo valor atribuído à variável b, porém note que na terceira coluna
da Tabela 3.1 está definido que a equivalência dos operadores de atribuição composta
com a atribuição simples (=) só é atingida ao colocarmos a expressão, originalmente à
direita, entre parênteses. Logo, a segunda atribuição à c corresponde à segunda de a.
É importante conhecer esse conjunto de operadores de atribuição, pois são
frequentemente usados no mercado de trabalho, em fóruns e livros. Ao longo desta
disciplina, usaremos principalmente os operadores =, += e -=.
Novamente, teste a Codificação 3.5 na Shell e verifique se compreendeu a razão
dos valores atribuídos às variáveis.
>>> x = 10 # x recebe 10
>>> y = 10 # y recebe 10
>>> z = 10 # z recebe 10
>>> x += 2 * 3 # x recebe 16
>>> y = y + 2 * 3 # y recebe 16
>>> z = z + (2 * 3) # z recebe 16
Codificação 3.5: Atribuição composta e atribuição simples equivalente.
Você pode ter ficado com dúvida sobre a razão das três últimas atribuições terem
o mesmo resultado, mesmo não existindo parênteses na segunda atribuição para y. Isso
ocorreu porque a precedência da multiplicação é naturalmente superior à da adição,
como na matemática, tornando a ausência de parênteses irrelevante nesta instrução. Para
entender plenamente como Python avalia expressões, é necessário estudarmos como
essa linguagem de programação define a precedência e associatividade dos operadores.
maior precedência são avaliadas antes daquelas com operadores de menor precedência.
Porém, a precedência natural de um operador pode ser artificialmente modificada com o
uso de pares de parênteses, da mesma forma como fazemos na matemática.
Por exemplo, a expressão 4+1*5 resulta em 9, afinal a multiplicação tem
precedência sobre a adição e por isso será avaliada primeiro, mesmo que a adição esteja
escrita antes na expressão. Lembre-se que a leitura de qualquer instrução é feita da
esquerda para a direita, por isso apontamos que “a adição apareceu antes na expressão”.
Neste segundo exemplo, a expressão (4+1)*5 resulta em 25, pois a precedência
do operador de adição que está entre os parênteses foi alterada, aumentando sua
prioridade. Em expressões com pares de parênteses aninhados, como (4+(1*5))/2, a
precedência é do mais aninhado para o menos aninhado. Uma conclusão natural, afinal
o par de parênteses mais externo aumentou a prioridade da parte da expressão que está
dentro dele e que, por sua vez, contém outro par de parênteses, aumentando a prioridade
da parte da expressão dentro deste segundo par, e assim sucessivamente.
Porém, existem casos em que uma expressão pode conter operadores de mesma
precedência, por exemplo, 8/4*2. Repare que nesta expressão não podemos nos basear
na precedência para decidir qual parte da expressão Python avaliará primeiro, afinal
divisão e multiplicação têm a mesma precedência. No entanto, se a expressão contivesse
parênteses, não haveria dúvidas, pois (8/4)*2 resultaria 4.0, e 8/(4*2) resultaria
1.0. Para resolver o impasse existe o conceito de associatividade.
A associatividade é uma propriedade que define como operadores de mesma
precedência devem ser agrupados, caso estejam na mesma expressão e sem parênteses
que determinem a ordem da avaliação.
Por exemplo, na expressão 8/4*2 o operando 4 está precedido e sucedido por
operadores de mesma precedência, portanto poderia ser associado ao operador da
esquerda, impondo que a avaliação começaria por 8/4, ou poderia ser associado ao
operador da direita, impondo que a avaliação começaria por 4*2. Com fundamento na
regra de associatividade dos operadores de divisão real e multiplicação, poderemos
concluir, sem ambiguidade, o resultado da expressão. Neste exemplo, o resultado será
4.0, porque esses dois operadores são associativos à esquerda.
Os operadores podem ser associativos à esquerda, associativos à direita ou não
associativos. Veja a descrição de cada tipo:
● Associativos à esquerda: as operações são agrupadas da esquerda para a
direita. Portanto, o operando será associado ao operador à sua esquerda;
● Associativos à direita: as operações são agrupadas da direita para a esquerda.
Portanto, o operando será associado ao operador à sua direita;
● Não associativos: as operações não podem ser encadeadas, por uma de duas
razões: (I) porque possuem comportamento indefinido, gerando um erro ou;
** Exponenciação À direita
>>> t = 1 + 2 - 3 + 4 - 5
>>> t # qual o valor de t?
Codificação 3.7: 2º expressão que será analisada e avaliada como em Python.
1) Associatividade: t = 1 + 2 - 3 + 4 - 5 ⇨ t = 3 - 3 + 4 - 5
2) Associatividade: t = 3 - 3 + 4 - 5 ⇨ t = 0 + 4 - 5
3) Associatividade: t = 0 + 4 - 5 ⇨ t = 4 - 5
4) Associatividade: t = 4 - 5 ⇨ t = -1
5) Único operador: t = -1
>>> u = 2 ** 1 ** 3
>>> u # qual o valor de u?
Codificação 3.8: 3º expressão que será analisada e avaliada como em Python.
1) Associatividade: u = 2 ** 1 ** 3 ⇨ u = 2 ** 1
2) Associatividade: u = 2 ** 1 ⇨ u = 2
3) Único operador: u = 2
Note que neste último exemplo foi necessário analisar a associatividade, que no
caso do operador de exponenciação é à direita, conforme a Tabela 3.2. O operando 1,
por estar entre dois operadores de exponenciação, foi associado àquele à sua direita.
>>> v = ((2 ** 1) ** 3)
>>> v # qual o valor de v?
Codificação 3.9: 4º expressão que será analisada e avaliada como em Python.
1) Parênteses: v = ((2 ** 1) ** 3) ⇨ v = (2 ** 3)
2) Parênteses: v = (2 ** 3) ⇨ v = 8
3) Único operador: v = 8
>>> x = 4
>>> y = 5
>>> x += y += 1 # quais os valores das variáveis?
Codificação 3.13: 8º expressão que será analisada e avaliada como em Python.
3.6. Funções
Uma função é uma sequência de instruções que executa alguma tarefa específica
e que tem um nome. Por exemplo, podemos imaginar uma função que calcula a raiz
quadrada de um número não negativo qualquer, ou uma função que coleta dados
inseridos pelo usuário, ou uma função que exibe dados na tela.
Podemos dividir as funções em três tipos, de acordo com sua origem:
1) Integradas: disponibilizadas com a própria linguagem de programação e
prontas para uso imediatamente, sem necessidade de instrução adicional;
2) Importadas: criadas por outros programadores e disponibilizadas para serem
incluídas no ambiente de programação, mediante instrução que faça a
importação, e então usadas de modo semelhante às integradas;
3) Definidas: criadas pelo próprio programador e disponíveis para serem
utilizadas no código-fonte em que são definidas. É possível distribuí-las para
serem importadas em outros códigos-fonte.
função. Esse valor é o resultado da avaliação da função, algo semelhante ao que ocorre
quando avaliamos expressões. Podemos associar os argumentos como as entradas da
função e o valor de retorno como a saída da função.
Conceitualmente, nem todas as funções precisam retornar um valor, isso
depende do objetivo da função, definido por quem a criou. Não entraremos em todos os
detalhes neste momento, mas veremos em outras aulas que, na prática, o Python possui
um tratamento especial para funções que não precisariam retornar um valor.
Atente para o fato de que o valor de retorno da função pode ser usado como um
valor qualquer, podendo, por exemplo, ser atribuído à uma variável. Veja o exemplo:
>>> x = abs(-29) # atribui o valor de retorno de abs à x.
>>> x + 15
44
Codificação 3.16: Atribuição do valor de retorno de abs à uma variável.
Também podemos usar a função em uma expressão com outros operandos, basta
lembrar que a função será executada e imaginar que sua chamada será substituída pelo
valor retornado, tornando-se um operando como outro qualquer.
>>> abs(-29) + 15 # soma 15 ao valor retornado por abs.
44
Codificação 3.17: Chamada à uma função usada como operando em uma expressão.
Porém, isso só faz sentido com funções que retornem valor como resposta e que
esse valor seja compatível com a expressão onde a função está inserida. Por exemplo, se
a função está em uma expressão aritmética, o valor de retorno deve ser um número.
Existem funções que requerem a passagem de mais de um argumento, neste caso
eles deverão ser passados na ordem correta e separados por vírgulas. Veja, por exemplo,
a função pow que retorna como resposta a potência de uma base elevada a um expoente,
semelhante ao operador de exponenciação. Vamos refletir. Para que pow possa realizar a
exponenciação, quais os dados necessários? Exatamente! A base e o expoente que se
pretende calcular! O primeiro argumento é a base e o segundo é o expoente, como
vemos na Codificação 3.18.
>>> pow(2, 5) # 2 é a base e 5 o expoente
32
Codificação 3.18: Chamada à uma função com dois argumentos.
Figura 3.1: Resumo da documentação da função abs. Fonte: Elaborado pelo autor.
📚 VAMOS LER!
O Python 3.9.1 possui 69 funções integradas, número que pode variar entre as versões.
Veja a relação atualizada em: https://docs.python.org/pt-br/3/library/functions.html
Talvez surja a indagação: “por que usar print se ao inserir uma expressão na
Shell o resultado é exibido após a execução?”. A Shell do IDLE sempre exibe o
resultado de uma expressão após avaliá-la. Porém, isso é uma característica desta
ferramenta que foi construída com foco no aprendizado de Python, e não é garantido em
outros ambientes, além da Shell ser inadequada para criação de programas completos.
Lembre-se que códigos-fonte sem print, quando executados, não exibem nada.
Para confirmar, insira a Codificação 3.19 no editor e execute-a com o atalho [F5].
nome = 'Megan'
nome
idade = 34
idade
Codificação 3.20: Programa sem saída de dados.
Não houve saída? Agora modifique o código-fonte para que fique conforme a
Codificação 3.21 e execute-o novamente.
nome = 'Megan'
print(nome)
idade = 34
print(idade)
Codificação 3.21: Programa com saída de dados.
Se quisermos que apenas o último print quebre a linha, basta acrescentar como
último argumento da função um end, seguido de um sinal = com um valor à direita que
será exibido no lugar da quebra de linha. Veja um exemplo na Codificação 3.23
print('boa ', end='')
print('noite ', end='')
print('vizinhança')
Codificação 3.23: Programa em que apenas o último print quebrará uma linha.
No exemplo acima, passamos uma string vazia ('') para o argumento nomeado
end, cujo valor padrão é um caractere de quebra de linha ('\n'), não se preocupe em
entender os detalhes agora, pois isso será estudado mais a frente, quando aprendermos a
definir nossas próprias funções em Python.
A função print não foi projetada para retornar valor, por isso você não a verá
como valor de atribuição para uma variável ou usada em uma expressão.
A função input, pode receber no máximo um argumento, que deve ser uma
string e será exibida para o usuário. Note que o retorno de input sempre é uma string e
isso pode ser inadequado em algumas situações. Execute no editor a Codificação 3.25,
que solicita o nome, o valor do empréstimo e a quantidade de parcelas, e exibe o total da
dívida, que é o valor do empréstimo multiplicado pela quantidade de parcelas.
nome = input('Seu nome: ')
valor = input('Valor do empréstimo: ')
parcelas = input('Quantidade de parcelas: ')
print(nome, ', a dívida será de:', valor * parcelas)
Codificação 3.25: Programa sem conversões dos valores retornados por input.
Note que na Codificação 3.26, tanto para a função int quanto para float foi
passado como argumento a própria função input, que devolve como resposta uma
string. Quando uma função é passada como argumento para outra, a execução inicia
pela mais interna (argumento), para que então a mais externa possa ser executada.
Vamos analisar a sequência de execução da segunda instrução (input cujo valor de
retorno será convertido para float), sendo que o princípio é aplicável também à
terceira (input cujo valor de retorno será convertido para int).
Podemos resumir os três tipos de erros com a seguinte analogia: (1) em erros de
sintaxe, o interpretador não entende o que deve fazer, como falar em um idioma
desconhecido; (2) em erros de execução, o interpretador entende o que deve fazer, mas
não consegue completar a tarefa, como tentar abrir uma porta com a chave errada; (3)
em erros de semântica ou lógica, o interpretador entende o que deve fazer e consegue
completar com sucesso todas as instruções, mas isso não resolve o problema
especificado, como descarregar um caminhão de tijolos no endereço errado.
👁️🗨️
VOCÊ SABIA?
Existe uma empresa que mantém um indicador de popularidade sobre linguagens de
programação e com atualizações mensais! O site é: https://www.tiobe.com/tiobe-index/
Em 2020, Python ganhou pela 4ª vez o prêmio “Linguagem de Programação do Ano”,
dado à linguagem com maior crescimento em popularidade no ano. No site consta o
método usado para medir a popularidade.
Bibliografia e referências
Texto base
4
Operadores e expressões relacionais e lógicas,
estruturas de seleção simples e composta
Resumo
Nesta aula os objetivos são: (I) apresentar as estruturas de controle de fluxo de
execução; (II) conhecer as expressões relacionais e lógicas, assim como seus
operadores; (III) entender a associatividade dos operadores relacionais; (IV)
compreender o que são expressões equivalentes e complementares; (V) conceituar a
avaliação de curto-circuito; (VI) construir estruturas de seleção simples e composta.
4.1. Motivação
Os primeiros computadores eletrônicos foram chamados de “cérebros
eletrônicos”, causando a impressão de que poderiam pensar como humanos. Embora
sejam máquinas extremamente complexas, computadores simplesmente executam
aquilo que lhes é instruído, sem autonomia para tomar decisões por conta própria… ao
menos por enquanto. Assim, não há mais inteligência em um computador do que há nas
instruções que lhe são dadas, e sua vantagem está em conseguir executar uma sequência
de instruções de modo mais confiável e rápido do que uma pessoa poderia fazer.
Com estruturas sequenciais de controle de fluxo, já conseguimos resolver
diversos problemas simples, mas muitas vezes precisamos escolher se iremos ou não
executar determinadas instruções com base em dados que só serão conhecidos em
tempo de execução, muitas vezes dependentes de entradas fornecidas pelo usuário do
programa. Para isso, veremos as estruturas de seleção, que permitirão que nossos
programas reajam de acordo com decisões pré-determinadas e com base no que ocorrerá
durante seu fluxo de execução.
Figura 4.1: Estruturas de controle de fluxo básicas. Fonte: Elaborado pelo autor.
No Brasil, para estar habilitado a dirigir, não basta ter 18 anos, é preciso também
ser aprovado pelo Detran, por isso, na Codificação 4.2, pode_dirigir receberá True
apenas se pode_tirar_cnh e (and) aprovado_detran estiverem com o valor True.
Portanto, para compreender as possibilidades de relações verificáveis entre os
operandos nas expressões relacionais e lógicas, é necessário conhecer os operadores
relacionais e lógicos disponíveis na linguagem de programação Python.
👁️🗨️
VOCÊ SABIA?
Os valores lógicos também são conhecidos como valores booleanos por causa de
George Boole, um matemático e filósofo britânico, criador da álgebra booleana no
século XIX, fundamental para o desenvolvimento da computação moderna. Veja mais:
Como matemático inventou há mais de 150 anos a fórmula de buscas usada pelo Google
a NÃO a a b aEb a b a OU b
True False True True True True True True
False True True False False True False True
False True False False True True
False False False False False False
Significado Python
NÃO not
E and
OU or
🏹 VAMOS PRATICAR!
1) Crie na Shell do Python as variáveis a seguir: a = 4; b = 10; c = 50; d = 1; e = 5. Em
seguida faça a avaliação das seguintes expressões (tente antecipar o resultado da Shell):
i) a == c v) a == b ix) c <= c
ii) a < b vi) c < d x) c <= e
iii) d > b vii) b > a xi) d != a
iv) c != e viii) c <= e xii) e != e
>>> True and False >>> (10 < 0) and (10 > 2)
>>> True or False >>> (10 < 0) or (10 > 2)
>>> not True or True >>> not (3 != 0) or (8 > 5)
>>> not (True or True) >>> not (3 != 0 or 8 > 5)
>>> True and not False >>> not not True
==, !=, >, >=, <, <= Operadores relacionais. Não associativos
or OU lógico.
Ordem de
Operador Descrição Associatividade
resolução
1° ** Exponenciação. À direita
==, !=,
5° Operadores relacionais. Não associativos
>, >=, <, <=
8° or OU lógico.
1
O operando entre os dois operadores relacionais só será avaliado uma vez, mesmo que internamente a
expressão relacional encadeada seja transformada em duas expressões menores conectadas por um and. O
valor da avaliação é guardado e usado nas duas expressões. Isso é importante pois garante economia de
recursos computacionais e evita problemas quando, por exemplo, o operando é uma chamada a uma
função ou uma expressão mais complexa, em que uma dupla avaliação poderia modificar o operando.
Essas expressões, quando juntas, cobrem toda a reta dos números reais sem
nenhuma sobreposição (observe que idade < 18 não inclui o número 18), isso
significa que para cada possível valor de idade, se uma das expressões for verdadeira, a
outra será obrigatoriamente falsa. Veja outros exemplos na Codificação 4.5.
>>> 'Maria' != 'Megan' # Expressão A
>>> 'Maria' == 'Megan' # Expressão B (complementar à A)
>>> not('Maria' == 'Megan') # Expressão C (complementar à B)
>>> not('Maria' != 'Megan') # Expressão D (complementar à A)
Codificação 4.5: Exemplo de expressões complementares.
📚 VAMOS LER!
A negação (complementar) de conjunções (E lógico) e disjunções (OU lógico) foi um
assunto tratado por Augustus De Morgan, um importante matemático e lógico britânico,
no século XIX e que originou às “Leis De Morgan”, amplamente usadas para resolver
problemas de lógica. Veja mais: https://pt.wikipedia.org/wiki/Teoremas_de_De_Morgan
a = int(input('Primeiro: '))
b = int(input('Segundo: '))
print(f'{a} é divisível por {b}: {b != 0 and a % b == 0}')
Codificação 4.7: Programa que usufrui do curto-circuito do operador and.
a = int(input('Primeiro: '))
b = int(input('Segundo: '))
print(f'{a} não é divisível por {b}: {b == 0 or a % b != 0}')
Codificação 4.8: Programa que usufrui do curto-circuito do operador or.
2
O Python aceita quantidade qualquer de espaços, desde que todas as instruções do mesmo bloco tenham
o mesmo número de espaços, mas o valor padrão recomendado pela comunidade é de 4 espaços.
Após o comando else não é preciso (nem permitido) colocar uma segunda
condição, pois será considerada automaticamente a condição complementar àquela do
if. Consequentemente, quando a condição do if resultar em True, o bloco 1 será
executado e o bloco 2 ignorado e, quando a condição do if resultar em False, ocorrerá
o oposto, pois o bloco 2 será executado e o bloco 1 ignorado.
Como else é um complemento do if, jamais deve ser escrito isoladamente.
Repare que Python identifica o if correspondente ao else por meio da indentação, a
regra é simples: cada else corresponde ao if mais próximo que o antecede no mesmo
nível de indentação. Só pode existir um else por if.
Como exemplo de programa que utiliza uma estrutura de seleção composta,
insira no editor do IDLE a Codificação 4.12 e teste seu funcionamento.
Um erro frequente com iniciantes em programação é assumir que todo if deve
ser complementado com um else. Não faz sentido! Quando não existem dois blocos de
instruções mutuamente exclusivos (ao executar um bloco o outro deve necessariamente
ser ignorado), usa-se seleção simples que, como já explicado, não tem else. Veja um
exemplo deste erro de lógica na Codificação 4.13.
print('Total:', total)
Codificação 4.13: Exemplo de programa com estrutura de seleção composta inútil.
Na Codificação 4.13, quando o valor é inferior a cem reais, não é preciso fazer
nada especial, portanto o else é desnecessário, veja que seu bloco contém uma
instrução inútil, que consome processamento e não altera o valor da variável.
Bibliografia e referências
Texto base
5
Strings e estruturas de seleção aninhadas
Resumo
Nesta aula os objetivos são: (I) avançar o estudo de strings; (II) introduzir o conceito
de flags booleanas; (III) apresentar as estruturas de seleção aninhadas e encadeadas;
(IV) entender o funcionamento do comando elif.
5.1. Motivação
Conhecemos vários tipos de dados em Python, inclusive as strings. No entanto,
conforme os problemas se sofisticam, precisaremos de mais recursos para manipular
textos, por isso vamos nos aprofundar nas operações que podemos realizar sobre strings.
Também sabemos criar programas que tomam decisões simples, selecionando
um entre dois caminhos, mas isso é insuficiente para lidar com problemas mais
complexos, envolvendo árvores de decisão, em que uma decisão está dentro de outra.
Portanto, ampliaremos e sofisticaremos essa estrutura de controle, com seleções mais
elaboradas, possibilitando a resolução de problemas envolvendo mais níveis de decisão.
💎 VOCÊ CONHECE?
Grace Hopper foi uma importante programadora, formada
em matemática e física que, dentre diversos feitos, construiu
compiladores que foram base para o projeto que gerou a
popular linguagem de programação Cobol.
Tornou-se tenente e trabalhou com cálculos secretos no Mark
I, o primeiro computador eletromecânico dos Estados Unidos.
Fonte da imagem: https://www.biography.com/scientist/grace-hopper
5.2. Strings
Já utilizamos strings em nossos programas, são usadas para representar cadeias
de caracteres, ou seja, textos. Agora poderemos aprofundar um pouco mais sobre esse
tipo de dados, aprendendo a concatenar, repetir, comparar e formatar strings.
5.2.1. Concatenação
Quando precisamos juntar duas ou mais strings, realizamos uma operação
chamada de concatenação de strings. Em Python, essa operação é feita usando o
operador +. Veja na Codificação 5.1 um exemplo executado na Shell.
A concatenação não altera seus operandos, mas gera uma nova string que pode,
inclusive, ser atribuída a uma variável. Veja isso na Codificação 5.2.
5.2.2. Repetição
Uma forma simples de gerar uma nova string cujo conteúdo seja a repetição do
conteúdo de outra é com o operador *, que é sobrecarregado quando um de seus
operandos é uma string e o outro é um número inteiro, conforme a Codificação 5.3.
>>> risada_timida = 'kk'
>>> risada_longa = risada_timida * 3
>>> risada_longa
'kkkkkk'
Codificação 5.3: Exemplo de repetição de string.
5.2.3. Comparação
Podemos realizar comparações entre strings com operadores relacionais, os
mesmos usados para verificar relações entre números. Exemplos na Codificação 5.4.
>>> 'maria' != 'MaRiA' # Exemplo 1
True
>>> 'maria' == 'MARIA' # Exemplo 2
False
>>> 'Meg' < 'Megan' # Exemplo 3
True
>>> 'beatriz' > 'bia' # Exemplo 4
False
Codificação 5.4: Exemplo de comparação entre strings.
Figura 5.1: Procedimento de comparação entre strings. Fonte: Elaborado pelo autor.
Mas qual a razão para um caractere ser identificado como “menor” ou “maior”
do que outro? Intuitivamente, podemos assumir a ordem alfabética, em que um caractere
X é considerado menor do que um caractere Y se X antecede Y no alfabeto. Porém isso
é apenas uma simplificação, que não é suficiente para o exemplo da Codificação 5.5.
>>> 'oi!' < 'oi?'
True
Codificação 5.5: Exemplo de comparação entre strings.
📚 VAMOS LER!
Unicode é uma padronização que permite que computadores representem e manipulem
caracteres de quase todos os sistemas de escrita existentes. Na tabela Unicode existem
códigos associados a símbolos, sendo que cada código está mapeado para apenas um
símbolo. Há milhares de códigos, basta consultá-los em: https://unicode-table.com/pt/
Não nos aprofundaremos neste tópico, sendo suficiente saber que os códigos são
sequenciais e que nosso alfabeto está disposto nesta tabela da forma como estamos
habituados, consequentemente 'A' é menor que 'B', 'B' é menor que 'C' e assim
sucessivamente. Também é importante saber que as letras maiúsculas são listadas nesta
tabela antes das letras minúsculas, logo os códigos das maiúsculas são menores que os
das minúsculas, justificando o resultado da Codificação 5.7.
>>> 'ANA' < 'ana'
True
>>> print('A =', ord('A'), '| a =', ord('a'))
A = 65 | a = 97
Codificação 5.7: Comparação de strings com letras maiúsculas e minúsculas.
Por fim, temos a função integrada chr que realiza o inverso da função ord, isto
é, recebe como argumento um número natural representando um código Unicode e
retorna como resposta o caractere correspondente. Veja o exemplo na Codificação 5.8.
>>> print(chr(48), chr(49), chr(50), chr(51), chr(9733))
0 1 2 3 ★
Codificação 5.8: Exemplo de uso da função chr.
5.2.4. Formatação
Às vezes é necessário formatar strings, estipulando, por exemplo, quantidade de
colunas, alinhamento, quantidade de casas decimais para representar floats, etc. O
Python possui algumas formas para criar strings formatadas1, dentre as quais citamos:
1) Interpolação: formatação utilizando o operador de interpolação (%), com sintaxe
próxima ao estilo usado na função printf da Linguagem C. É compatível com
todas as versões do Python, mas seu uso é desencorajado para novos projetos;
2) Método format: método de strings em que os argumentos são formatados e
inseridos nos marcadores identificados por pares de chaves. Aceito a partir do
Python 2.6. É um avanço em relação à interpolação, com maior controle sobre a
formatação, mas aumenta a verbosidade podendo reduzir a legibilidade do
código. É recomendado para projetos Python que antecedem à versão 3.6;
3) f-strings ou “strings literais formatadas”: é a abordagem mais recente para
formatação de strings, basicamente uma string comum prefixada com a letra f
ou F (antes da abertura de aspas/apóstrofos) e com valores ou expressões entre
pares de chaves que serão formatados e inseridos na string. Compatível com as
versões do Python a partir da 3.6, é a forma recomendada para novos projetos;
4) Template strings: é uma forma especial de criação de strings e é recomendada
para situações em que a formatação da string será fornecida pelo usuário final,
pois seu funcionamento traz algumas proteções contra injeção de código
malicioso. Não será abordada neste curso.
1
Para saber mais leia Python String Formatting Best Practices – Real Python
Símbolo Alinhamento
< À esquerda.
> À direita.
^ Centralizado.
Teste a Codificação 5.13 na Shell, lembrando que o valor também poderia estar
em uma variável ou ser o resultado de uma expressão.
Por padrão, colunas não ocupadas por caracteres do valor são preenchidas com
espaços. Para personalizar o caractere de preenchimento adiciona-se outro especificador
2
Format Specification Mini-Language (Python Software Foundation, 2021).
Observe que o ponto (.) usado na formatação de cada uma das variáveis na
Codificação 5.14 tem significado distinto, pois estão em posições diferentes no padrão
que define a formatação.
>>> item = 'camiseta'
>>> preco = 49.9
>>> f'{item:.<20} R$ {preco:.2f}'
'camiseta............ R$ 49.90'
Codificação 5.14: Formatação de preenchimento de colunas não utilizadas pelo valor.
if maior_de_idade:
print('Você é maior de idade.')
Codificação 5.16: Programa que verifica se o usuário é maior de idade (com flag).
Note que a Codificação 5.15 e Codificação 5.16 são equivalentes, porém, nesta
última, não há operadores na condição do if, apenas uma variável com valor booleano.
Vamos incluir mais condições para podermos visualizar as vantagens no uso de flags
booleanas. Insira a Codificação 5.17 no editor, teste a sua execução e em seguida pense
em como substituir as condições por flags para facilitar a leitura do código.
idade = int(input('Qual a sua idade? '))
cnh = input('Você tem CNH? (s/n)')
A Codificação 5.18 é equivalente à Codificação 5.17, mas com flags. Não existe
apenas uma versão válida, o importante é usar identificadores significativos para o
problema, minimizando ambiguidades e prezando pela simplicidade.
idade = int(input('Qual a sua idade? '))
cnh = input('Você tem CNH? (s/n)')
if pode_dirigir:
print('Você pode dirigir.')
Codificação 5.18: Programa que verifica se o usuário pode dirigir (com flag).
Note que a escolha entre usar ou não flags booleanas dependerá da situação e
experiência da programadora/programador, algo naturalmente desenvolvido com treino.
Cabe a cada um encontrar um equilíbrio em seu uso, assim como qualquer recurso em
programação, portanto, procure praticar todas as diferentes formas, com foco
primeiramente em entender como funcionam e não em sua aplicabilidade definitiva.
Cada recurso aprendido é um novo instrumento em sua caixa de ferramentas.
As principais vantagens de se usar flags booleanas são:
● Melhorar legibilidade do código, facilitando a construção e o entendimento
de condições mais complexas;
● Guardar a informação relativa a uma determinada condição avaliada durante
a execução do código;
● Aprimorar o desempenho do programa quando uma condição deve ser
avaliada diversas vezes, por exemplo em laços de repetição, assunto ainda
não abordado.
3
A implementação padrão, e mais utilizada, do interpretador do Python é feita na linguagem C, porém há
também interpretadores feitos em Java, C#, Rust e no próprio Python, entre outras linguagens. Lembrando
que o interpretador é o responsável por traduzir nossos programas para linguagem de máquina (binário).
4
O Python aceita uma quantidade qualquer de espaços, desde que todas as instruções do mesmo bloco
tenham o mesmo número de espaços, mas o padrão recomendado é de 4 espaços (Rossum et. al., 2013).
Após desenhar, veja se consegue dizer quando cada instrução é executada, isto é,
qual o conjunto de entradas que precisam ser dadas para que o fluxo de execução passe
por aquela instrução em particular. Em seguida, compare seu mapa com o da Figura 5.2.
Figura 5.2: Mapa do fluxo de execução da Codificação 5.19. Fonte: Elaborado pelo autor.
Figura 5.3: Visão da estrutura de seleção mais externa. Fonte: Elaborado pelo autor.
Figura 5.4: Visão das estruturas de seleção no 2º nível. Fonte: Elaborado pelo autor.
Figura 5.5: Intervalos de classificação das notas. Fonte: Elaborado pelo autor.
if nota >= 9: #2
letra = 'A' #3
else:
if nota >= 8: #4
letra = 'B' #5
else:
if nota >= 6: #6
letra = 'C' #7
else:
if nota >= 4: #8
letra = 'D' #9
else:
letra = 'E' #10
Figura 5.6: Mapa de caminhos possíveis da Codificação 5.21. Fonte: Elaborado pelo
autor.
if nota >= 9:
letra = 'A'
elif nota >= 8:
letra = 'B'
elif nota >= 6:
letra = 'C'
elif nota >= 4:
letra = 'D'
else:
letra = 'E'
🏹 VAMOS PRATICAR!
1) Crie um programa que solicite ao usuário um número de 1 à 7 e exiba o dia da
semana correspondente. Assuma que a semana começa no domingo (1) e termina no
sábado (7). Use apenas seleção simples, ou seja, sem else nem elif.
2) Refaça o exercício anterior, agora utilizando a seleção encadeada, mas sem usar o
comando elif, de modo que seja observada a importância do alinhamento correto de
indentação entre os diversos comandos if e else.
3) Refaça o exercício anterior, agora utilizando elif.
4) Com o Python Tutor, compare o fluxo de execução dos códigos dos exercícios
anteriores.
Bibliografia e referências
BADER, D. Python String Formatting Best Practices. Real Python, 2018. Disponível
em: <https://realpython.com/python-string-formatting/>. Acesso em: 26 jan. 2021.
JABLONSKI, J. Python 3's f-Strings: An Improved String Formatting Syntax (Guide).
Real Python, 2018. Disponível em: <https://realpython.com/python-f-strings/>.
Acesso em: 26 jan. 2021.
PYTHON SOFTWARE FOUNDATION. Common string operations. 2021.
Disponível em: <https://docs.python.org/3/library/string.html>. Acesso em: 26 jan.
2021.
PYTHON SOFTWARE FOUNDATION. Input and output. 2021. Disponível em:
<https://docs.python.org/3/tutorial/inputoutput.html>. Acesso em: 26 jan. 2021.
ROSSUM, G. V., WARSAW, B., COGHLAN, N. Style Guide for Python Code. 2013.
Disponível em: <https://www.python.org/dev/peps/pep-0008/>. Acesso em: 27 jan.
2021.
STURTZ, J. Basic Input, Output, and String Formatting in Python. Real Python, 2019.
Disponível em: <https://realpython.com/python-input-output/>. Acesso em: 26 jan.
2021.
Texto base
6
Criação de funções em Python
Resumo
Nesta aula os objetivos são: (I) conhecer as funções importadas; (II) definir nossas
próprias funções; (III) entender a relação entre argumentos e parâmetros de funções;
(IV) compreender o que é valor de retorno; (V) introduzir o conceito de escopo de
variáveis; (VI) escrever documentação nas funções; (VII) organizar o código-fonte.
6.1. Motivação
Conhecemos funções pré-definidas e sabemos como usá-las, mas não podemos
nos limitar a elas, pois frequentemente teremos problemas para os quais não existem
funções prontas. Seria inviável criar e integrar à linguagem uma função para cada
problema existente, basicamente por dois motivos: 1) novos problemas surgem o tempo
todo e; 2) o pacote de instalação do Python ficaria cada vez maior e eventualmente não
teríamos mais espaço disponível. Por isso, veremos como criar nossas próprias funções
e quais as principais vantagens ao usá-las em nossos programas.
💎 VOCÊ CONHECE?
Donald Knuth é professor emérito da Universidade de Stanford e
um cientista da computação mundialmente conhecido. Também é
autor da coleção de livros “The Art of Computer Programming”,
referência em Ciência da Computação.
Knuth é um dos principais responsáveis pelo campo da Análise de
Algoritmos e pela criação do sistema de tipografia TeX.
Fonte da imagem: https://www-cs-faculty.stanford.edu/~knuth/
6.2. Introdução
Após criar soluções para diversos problemas, é comum que certas sequências de
código se tornem frequentes, às vezes repetidas no mesmo programa. Uma forma de
reduzir a duplicação de código é criar funções com os códigos mais utilizados.
A sintaxe para definição de uma função em Python é bastante simples, mas para
usarmos as funções corretamente precisamos entender:
● Quando e por que devemos criar uma função;
● Como o funciona a passagem de valores para uma função;
● Como a função pode retornar um valor de resposta;
● O que acontece com as variáveis que criamos dentro de uma função.
As principais vantagens do uso de funções são:
● Abstração e reusabilidade, evitando duplicidade de código;
● Modularização, permitindo que um problema inicial seja dividido em
problemas menores e mais fáceis de serem resolvidos;
● Separação de escopo, criando área de trabalho local para a função e evitando
conflito entre variáveis internas da função e demais variáveis do programa.
Estas vantagens levam a um código com maior legibilidade, que será mais fácil
de manter, atualizar e corrigir. A abstração está relacionada a separação entre “o que”
precisa ser feito e “como” será feito. Quando utilizamos apenas funções integradas e
importadas, a preocupação é apenas com “o que” a função faz, ao criar nossas próprias
funções o “como” também é nossa responsabilidade. Porém, uma vez que nossas
funções estão definidas e devidamente testadas, aumentando a confiança que estão
corretas, voltamos a lidar apenas com o “o que”, podendo utilizá-las de modo
semelhante às funções integradas e importadas, usufruindo das mesmas vantagens.
Primeiro, importamos o módulo math. Isso criará uma variável de mesmo nome
que nos dará acesso ao módulo (descubra o tipo do dado referenciado pela variável
usando a função integrada type). Denominamos os conteúdos de um módulo como
propriedades. Para acessar as propriedades, usamos um ponto após o nome da variável,
seguido pelo nome da propriedade que queremos acessar. A descrição completa do
módulo de matemática está disponível na documentação do Python (PSF, 2021).
Lembrando que se a propriedade for uma função, será necessário usar um par de
parênteses para executá-la, semelhante a qualquer função que usamos até agora, pois
caso contrário, acessaremos somente a referência para o objeto da função na memória.
Ao criar códigos-fonte, recomenda-se que as importações estejam no começo do
arquivos, sendo necessário importar cada módulo uma única vez em um mesmo arquivo.
Inclusive, instruções que tentem importar um módulo já importado não terão efeito.
Crie nomes de funções que sejam claros indicativos do que ela faz, pois isso
facilitará a legibilidade e uso. As regras para nomes de funções são idênticas àquelas
para variáveis, até porque em Python o nome da função também é uma variável, só que
uma variável que referencia um código de função que está na memória.
Portanto evite criar funções com o mesmo nome de outras variáveis, pois isso
poderá gerar problemas em que a variável que referencia a função tem seu conteúdo
sobrescrito por outro valor, ou uma variável qualquer tem seu conteúdo perdido por
passar a referenciar o código de uma função.
Ocorreu algo na Shell? Apenas reiniciou, mas não exibiu a soma? Aparentemente
nada aconteceu, porém a função soma foi criada corretamente e está na memória, pronta
para ser chamada. Para conferir, digite na Shell apenas o nome da função, sem os
parênteses e veja se obtém um resultado similar ao da Codificação 6.4.
>>> soma
<function soma at 0x0000017FC3BB7CA0>
Codificação 6.4: Endereço da função soma na memória.
2
O nome da função juntamente com a sequência de seus parâmetros é chamada de assinatura/cabeçalho
da função. Em outras linguagens, esse conceito também pode incluir o tipo dos parâmetros e o tipo do
retorno da função, mas isso depende de como a linguagem trata a tipagem de dados.
Sabemos que ao chamar uma função podemos passar valores como argumentos
se, é claro, a função for definida com parâmetros que receberão esses argumentos.
Assim, podemos entender os parâmetros com variáveis de entrada da função, e os
argumentos como os valores que serão atribuídos a essas variáveis.
Por padrão, os argumentos são atribuídos aos parâmetros seguindo a ordem em
que são passados na chamada da função. Logo, o primeiro argumento será atribuído ao
primeiro parâmetro, o segundo argumento será atribuído ao segundo parâmetro e assim
por diante. Veja na Figura 6.1 duas funções definidas pelo programador e suas
respectivas chamadas, note a relação entre os argumentos e os parâmetros.
Na Codificação 6.3, os parâmetros da função soma são n1 e n2, e ela foi
chamada duas vezes na Codificação 6.6, uma com os argumentos 3 e 5, e outra com os
argumentos 7 e -9, retornando, em cada chamada, o resultado da soma de seus
parâmetros. O retorno de um valor é feito com o comando return seguido pelo valor
retornado, que pode ser originado de uma variável ou expressão mais complexa.
Modificaremos a Codificação 6.3 para melhorar a visualização do fluxo de
execução do programa contido em “funcao.py”, altere-o conforme a Codificação 6.7.
Figura 6.1: Relação entre argumentos e parâmetros. Fonte: Elaborado pelo autor.
print('Fora da função!') #6
Codificação 6.7: “funcao.py” alterado para melhor visualização do fluxo de execução.
Figura 6.2: Resultado da execução da Codificação 6.7. Fonte: Elaborado pelo autor.
>>> soma(3, 5)
Início do bloco da função
8
>>> soma(7, -9)
Início do bloco da função
-2
Codificação 6.8: Novas chamadas à função soma definida pelo programador.
Notamos que a cada chamada à soma o primeiro print é executado, mas não o
segundo, por quê? A razão é que o segundo print está após a instrução return, que
sempre é executada antes dele. Quando uma função executa um comando return, ela
retorna o valor da expressão à direita de return, se existir, e encerra sua execução,
devolvendo o controle do fluxo para à instrução que a chamou. Logo, qualquer instrução
no bloco da função posterior à execução de um return é inalcançável.
É possível que uma função tenha mais de um return, o que pode ser útil caso
estejam em blocos de código diferentes como, por exemplo, em uma estrutura de
seleção, que a depender de sua condição executará um ou outro return, jamais os dois.
A função par na Codificação 6.9 retorna um valor booleano indicando se n é par.
def par(n):
if n % 2 == 0:
return True
else:
return False
print('Fora da função!') #5
soma(3, 5) #6
soma(7, -9) #7
Codificação 6.10: Chamadas à função soma sem exibição dos valores retornados.
Figura 6.3: Resultado da execução da Codificação 6.10. Fonte: Elaborado pelo autor.
Figura 6.4: Resultado da execução da Codificação 6.11. Fonte: Elaborado pelo autor.
Portanto, temos basicamente duas opções para que o valor final produzido por
uma função seja exibido:
1) Exibir o valor retornado pela função na instrução em que ela foi chamada,
como na Codificação 6.9, ou atribuir o valor à uma variável e depois exibi-la;
2) Alterar o código da função para que o valor seja exibido e não retornado.
Note que a função foi executada corretamente, inclusive sua instrução print
cumpriu o papel esperado, exibindo a soma. Porém, há algo estranho na Codificação
6.14. Qual o valor retornado por uma função que nem mesmo tem um return explícito?
Descubra ao tentar exibir o valor da variável teste, como na Codificação 6.15. Repare
que não é possível visualizar o conteúdo desta variável sem o uso de print.
>>> print(teste)
None
Codificação 6.15: Valor da variável que recebeu uma atribuição na Codificação 6.14.
👁️🗨️
VOCÊ SABIA?
Algumas linguagens de programação diferenciam funções que retornam valor daquelas
que não retornam. Linguagens como C, classificam funções sem valor de retorno como
funções do tipo void (vazia). A linguagem Pascal, por exemplo, possui function e
procedure, sendo este último algo semelhante à uma função, porém sem valor retornado.
Veja mais em: https://en.wikipedia.org/wiki/Void_type
A variável nome foi criada fora de qualquer função, então é global, isso significa
que quando o Python não encontrar o identificador nome no escopo local da função, irá
procurá-lo no escopo global. Caso o nome também não esteja no escopo global, será
disparado um erro de “nome não definido”.
Variáveis locais têm prioridade sobre globais, caso existam duas variáveis como
o mesmo identificador a local será a utilizada por padrão, como na Codificação 6.17.
def diga_ola(nome): # parâmetros são variáveis locais
print(f'Olá {nome}!') # acessa a variável local
Figura 6.7: Escopo global e local no Python Tutor. Fonte: Elaborado pelo autor.
Qualquer atribuição a variável dentro de uma função gera uma variável local,
porém se a intenção é alterar variáveis globais, deve-se indicar com o comando global
sucedido pelo nome das variáveis entre vírgulas, como na Codificação 6.18.
def diga_ola():
global nome, titulo # são identificadores globais
print(f'Olá {titulo} {nome}!') # acessa a variável global
nome = 'Megan' # modifica a variável global
titulo = 'Sra.' # modifica a variável global
3
Simplificadamente, o encapsulamento diz respeito a uma função ocultar seus detalhes de funcionamento
interno, de modo que tudo que for necessário para que ela funcione esteja contido nela.
Parâmetros
----------
n1, n2: int ou float
Números a serem somados.
Retorno
-------
int ou float
Resultado da soma de n1 com n2.
"""
s = n1 + n2
return s
Codificação 6.19: Exemplo de docstring em uma função.
Figura 6.8: Visualização da docstring parcial na Shell. Fonte: Elaborado pelo autor.
Figura 6.9: Visualização da docstring integral na Shell. Fonte: Elaborado pelo autor.
# Importações
import math
# Definição de funções
def dobro(x):
return 2 * x
def saudacao(nome):
print(f'Bem-vindo {nome}!')
# Código principal
nome = input('Qual o seu nome? ')
saudacao(nome)
dobro_pi = dobro(math.pi)
print(f'O dobro de {math.pi:.4f} é {dobro_pi:.4f}')
Codificação 6.20: Modelo simplificado de organização para códigos-fonte.
📚 VAMOS LER!
Há muito conhecimento relativo às funções em Python e suas implicações práticas,
apenas começamos a explorá-lo e estaremos em constante aprendizado.
Para aprender um pouco mais sobre funções, leia o capítulo 3 do livro Pense em Python,
disponível em: https://penseallen.github.io/PensePython2e/03-funcoes.html.
E para se aprofundar ainda mais em alguns dos detalhes envolvidos na definição de
funções leia: https://realpython.com/defining-your-own-python-function/
Bibliografia e referências
Texto base
7
Estrutura de repetição indefinida (while)
Resumo
Nesta aula os objetivos são: (I) entender a necessidade da estrutura de repetição para
controle do fluxo de execução; (II) compreender a estrutura de repetição com
quantidade de repetições indefinida: laço while; (III) usar variáveis contadoras,
acumuladoras e de sinalização booleana para controlar a execução do while; (IV)
conceituar “laço infinito” e suas consequências; (V) utilizar estruturas de repetição
combinadas com estruturas de seleção.
7.1. Motivação
Durante nosso curso aprendemos diversos recursos de programação, porém há
algo muito importante realizado pelos computadores que precisamos abordar: a
repetição de instruções. É natural que programas executem uma sequência de instruções
repetidas vezes, aliás essa é uma das principais características em que as máquinas
superam os seres humanos. Nesta aula compreenderemos a importância de utilizar uma
estrutura de repetição e como escrevê-la na linguagem de programação Python.
💎 VOCÊ CONHECE?
Mary Kenneth Keller, nascida em 1913, foi uma importante freira e
cientista da computação, sendo a primeira mulher com doutorado na
área e defensora da inclusão de mulheres na computação.
Mary participou do desenvolvimento da linguagem de programação
BASIC e fundou um departamento de ciências da computação na
Universidade Clarke (Iowa - EUA).
Fonte da imagem: www.zmescience.com/science/woman-computer-science-phd/
7.2. Introdução
Já aprendemos duas formas para controlar o fluxo de execução de um programa:
(I) estrutura de controle sequencial, em que o fluxo de execução é dado pela ordem em
que as instruções são escritas e; (II) estrutura de controle de seleção ou condicional,
onde o fluxo de execução sofre desvios de acordo com o resultado da avaliação de uma
condição. Agora aprenderemos a controlar o fluxo de execução de modo que uma
sequência de instruções possa ser repetida diversas vezes sem que seja necessário
escrevê-la diversas vezes, para tanto usaremos as estruturas de repetição. Para recordar,
as três estruturas básicas de controle de fluxo estão ilustradas na Figura 7.1.
print(1)
print(2)
print(3)
print(4)
print(5)
Codificação 7.1: Exibição dos cinco primeiros números naturais positivos.
x = int(input('Valor: '))
print(x + 1)
print(x + 2)
print(x + 3)
print(x + 4)
print(x + 5)
Codificação 7.2: Exibição dos cinco naturais posteriores ao da entrada.
x += 1
print(x)
x += 1
print(x)
x += 1
print(x)
x += 1
print(x)
x += 1
print(x)
Codificação 7.3: Alternativa para exibição dos cinco naturais posteriores ao da entrada.
Mesmo que a Codificação 7.3 tenha mais linhas de código do que a Codificação
7.2, a mudança gerou facilidade para o programador, que pode simplesmente replicar o
incremento da variável x e sua exibição, sem nenhuma alteração. Com isso, caso o
enunciado solicitasse os cinquenta naturais posteriores à entrada, ao invés de cinco,
bastaria copiar e colar essas duas instruções mais quarenta e cinco vezes. É trabalhoso?
Sim! Mas menos trabalhoso e menos suscetível a erros do que a versão anterior.
Você deve ter observado que tanto a solução para o primeiro problema quanto
para o segundo são possíveis de serem implementadas com o conhecimento que temos
até o momento, porém são trabalhosas e, eventualmente, tornam-se inviáveis quando a
quantidade de repetições é muito grande. Porém, existem problemas que simplesmente
são impossíveis de serem resolvidos apenas copiando e colando instruções.
Um exemplo no qual existe esse impedimento é a terceira variação do problema
para exibição de números naturais: suponha que o programa deverá exibir uma
sequência de números naturais delimitada pelos valores início e fim, que são dois
naturais dados pelo usuário como entrada. Note que o programa deverá exibir a
sequência completa, incluindo os extremos início e fim. Por exemplo, caso o usuário
insira o valor 5 como início e 12 como fim, a saída será 5 6 7 8 9 10 11 12.
Usando o que sabemos até agora, qual a solução para essa terceira variação do
problema? Impossível construí-la! Não há como prever a quantidade de valores
exibidos, afinal isso dependerá dos valores de entrada dados pelo usuário, algo que só
será descoberto quando o programa estiver em execução. Então, precisamos ampliar
nossos recursos de programação para resolver esse problema, precisamos dos laços!
while <condição>:
<bloco de código>
Codificação 7.4: Estrutura do laço de repetição while.
x = 1
while x <= 5:
print(x)
x += 1
print('Tchau!') # instrução imediatamente posterior ao laço.
Codificação 7.5: Exibição dos cinco primeiros números naturais positivos.
O bloco de código do while será executado exatamente cinco vezes, pois após a
quinta execução de x += 1 a variável x estará com o valor 6, fazendo x <= 5 resultar
em False. No entanto, é importante observar que a condição do laço será sempre
avaliada uma vez mais, neste caso exatamente seis vezes, pois apenas na sexta avaliação
que a condição resultará em False e, consequentemente, encerrará o loop. A instrução
que exibe “Tchau!” é executada só uma vez, pois está fora do while.
A segunda variação do problema de números naturais, cuja primeira solução
consta na Codificação 7.2 e uma versão melhorada na Codificação 7.3, seria facilmente
solucionada com while, conforme Codificação 7.6.
x = int(input('Valor: '))
qtd_exibidos = 0
while qtd_exibidos < 5:
x += 1
print(x)
qtd_exibidos += 1
Codificação 7.6: Exibição dos cinco primeiros naturais posteriores ao da entrada.
atual (neste caso apenas a exibição); (b) depois de terminar, passamos para o próximo
valor de x (neste caso um incremento de 1); (c) voltamos à condição para verificar se x
ainda atende à condição; (d) caso atenda (condição resulta True) executamos mais uma
vez o laço, repetindo o processo; (e) caso não atenda (condição resulta False),
encerramos o laço e passamos para a próxima instrução fora do bloco do while.
🏹 VAMOS PRATICAR!
1) Teste no Python Tutor as soluções propostas para as três variações do problema de
números naturais, tanto aquelas com loop while quanto aquelas sem loop. Certifique-se
de ter entendido completamente o fluxo de execução do programa.
2) Crie um programa que exiba todos os números inteiros de 100 até 200 em ordem
crescente. Depois crie outro programa que exiba de 200 até 100 em ordem decrescente.
3) Crie um programa que leia um número natural n dado pelo usuário e exiba só os n
primeiros pares a partir do 0. Por exemplo, se n=6 será exibido 0 2 4 6 8 10.
4) Crie um programa que solicite ao usuário dois números naturais x e y, o programa
deverá exibir o quociente da divisão inteira de x por y sem usar os operadores de
divisão e multiplicação. Por exemplo, se x=7 e y=2 a resposta será 3, pois podemos
raciocinar que o quociente da divisão inteira de x por y é dado pela quantidade de vezes
que y pode ser subtraído de x sem que x se torne negativo.
contador = 10
while contador > 0:
print(contador)
contador -= 1
print('Fogo!')
Codificação 7.9: Programa em que a variável contadora controla o número de repetições.
🏹 VAMOS PRATICAR!
5) Crie um programa que peça letras como entrada, uma por vez, até que seja lida a letra
'x', ao final, o programa deve exibir a quantidade de letras lidas sem contabilizar 'x'.
Observação: lembre-se que o Python diferencia maiúsculas e minúsculas.
deverá parar de solicitar novos preços quando o crédito disponível for insuficiente para
pagar por um deles. Ao final, exiba o total da compra e o crédito restante”. A
Codificação 7.11 é uma solução válida para esse enunciado.
🏹 VAMOS PRATICAR!
6) Refaça a solução da Codificação 7.11 de modo
que todo produto inserido pelo usuário seja
numerado sequencialmente, iniciando em 1, e que
o programa exiba a mensagem “Compra do item X
negada!”, após a leitura do item que extrapolar o
valor do crédito, onde X é o número do item.
Acrescente as instruções necessárias para, no final,
exibir a quantidade de itens que puderam ser
comprados. À direita há um exemplo de execução.
total = 0
quero_comprar = True # será usada como flag booleana no loop.
while quero_comprar:
preco = float(input('Preço: '))
total += preco
opcao = input('Continuar comprando (s/n)? ')
if opcao != 's':
quero_comprar = False
Note que a clareza do nome dado à variável definida como flag booleana é muito
importante, pois influenciará na forma como o programador entenderá o fluxo de
execução. No exemplo da Codificação 7.12, podemos entender a leitura da primeira
linha do loop como “enquanto eu quiser comprar, execute o bloco de instruções”. Por
isso, é evidente que quando a variável quero_comprar receber False, poderemos
entender como “não quero comprar”, indicando que o loop deve ser encerrado.
n = 0 n = 0
while n <= 10: while n >= 0:
print(n) print(n)
n += 2
print('Acabou!') print('Acabou!')
Codificação 7.13: Exemplos de programas com um laço infinito.
🏹 VAMOS PRATICAR!
7) Corrija ambos exemplos da Codificação 7.13 para que a execução seja encerrada após
exibir os números pares no intervalo fechado [0..10].
📚 VAMOS LER!
Existem alguns detalhes do loop while que são particulares da linguagem Python. Veja
mais em: https://www.programiz.com/python-programming/while-loop.
def consoantes():
codigo_unicode = ord('a')
while codigo_unicode <= ord('z'):
letra = chr(codigo_unicode)
if (letra != 'a' and letra != 'e' and letra != 'i' and
letra != 'o' and letra != 'u'):
print(letra)
codigo_unicode += 1
Codificação 7.14: Função com combinação de laço e seleção.
def primo(n):
qtd_divisores = 0
divisor = 1
while divisor <= n:
if n % divisor == 0:
qtd_divisores += 1
divisor += 1
if qtd_divisores == 2:
return True
else:
return False
Codificação 7.15: Função com combinação de laço e seleção.
Bibliografia e referências
DOWNEY, A. B. Iteração. In: Pense em Python. São Paulo: Editora Novatec, 2016.
cap. 7. Disponível em: <https://penseallen.github.io/PensePython2e/
07-iteracao.html>. Acesso em: 07 de fev. 2021.
PYTHON SOFTWARE FOUNDATION. Instruções compostas. 2021. Disponível em:
<https://docs.python.org/pt-br/3/reference/compound_stmts.html#>. Acesso em: 08
fev. 2021.
STURTZ, J. Python "while" Loops (Indefinite Iteration). Real Python, 2020.
Disponível em: <https://realpython.com/python-while-loop/>. Acesso em: 09 fev.
2021.
Texto base
8
Laços aninhados e interrupção de laços
Resumo
Nesta aula os objetivos são: (I) aprender os comandos de encerramento e interrupção
de laços; (II) simular a estrutura de repetição repeat…until usando while; (III) utilizar
laços de repetição para validação de entradas; (IV) entender a necessidade das
estruturas de repetição aninhadas; (V) resolver problemas com repetições aninhadas.
8.1. Motivação
Há situações em que um laço de repetição deve ser interrompido caso algo
específico ocorra e Python possui comandos que podem nos ajudar nisso. Usando os
comandos de interrupção de laços também poderemos simular um tipo de estrutura de
repetição não embutida na linguagem Python, porém útil para a resolução elegante de
alguns tipos de problemas! Com a simulação dessa nova estrutura de repetição
poderemos realizar validações de dados de entrada, algo muito importante quando
lidamos com interação com o usuário. Por fim, você já imaginou repetir uma repetição?
Acredite, esse tipo de situação é muito comum na programação e vamos estudá-la!
💎 VOCÊ CONHECE?
Allan Turing, nascido em 1912, foi um matemático, cientista da
computação e criptoanalista responsável pela formalização de
conceitos gerais de algoritmos e computação. Dentre seus feitos,
desenvolveu técnicas para acelerar a quebra de codificações de
mensagens alemãs interceptadas durante a 2ª Guerra Mundial.
Fonte da imagem: https://www.biography.com/scientist/alan-turing
8.1. Introdução
Até agora aprendemos três formas básicas para controlar o fluxo de execução de
um programa: (I) estrutura de controle sequencial; (II) estrutura de controle de seleção
ou condicional e; (III) estrutura de controle de repetição.
preços quando o crédito disponível for insuficiente para pagar um dos itens. Ao final,
exiba o crédito restante”. A Codificação 8.1 é uma solução válida para esse enunciado.
credito = float(input('Seu crédito: R$ '))
while credito > 0:
item = float(input('Preço do item: R$ '))
if item > credito:
print('Compra negada! Ultrapassa seu crédito.')
break
credito -= item
print(f'Crédito restante: R$ {credito:.2f}')
Codificação 8.1: Exemplo de funcionamento do comando break.
Outro exemplo de problema que pode ser beneficiado com o uso de break é o
seguinte: “Crie um programa que receba como entrada um número real x, um caractere
op simbolizando um operador aritmético (+, -, * ou /) e outro real y. O programa
exibirá o resultado da expressão x op y. O procedimento deve ser repetido enquanto o
usuário desejar, porém deve ser encerrado antes de ocorrer um erro por causa de uma
divisão por zero.”. A Codificação 8.2 é uma solução válida para esse enunciado.
calcular = input('Calcular (s/n)? ')
while calcular == 's':
x = float(input('x: '))
op = input('operador: ')
y = float(input('y: '))
if op == '+':
resultado = x + y
elif op == '-':
resultado = x - y
elif op == '*':
resultado = x * y
elif op == '/':
if y == 0:
print('Divisão por zero!\n')
break
else:
resultado = x / y
print(f'{x} {op} {y} = {resultado}\n')
calcular = input('Calcular (s/n)? ')
print('Calculadora encerrada')
Codificação 8.2: Calculadora com prevenção de erro de divisão por zero.
def invertido(n):
while True:
print(n % 10, end='')
n = n // 10
if n == 0: break
🏹 VAMOS PRATICAR!
1) Refaça a solução da Codificação 8.5, porém usando a estrutura de repetição while
tradicional, sem usar if e break. Observe no Python Tutor o fluxo de execução de
ambas as soluções. Por fim, discuta com seus colegas sobre a legibilidade de ambas
versões e quais são as possíveis vantagens e desvantagens de cada abordagem.
def somatorio(n):
soma = 0
natural = 1
while natural <= n:
soma += natural
natural += 1
return soma
while True:
n = int(input('Natural entre 0 e 100: '))
if 0 <= n <= 100: break
...
n = int(input('Natural entre 0 e 100: '))
Por fim, a Codificação 8.8 exibe uma terceira versão em que é utilizada uma flag
booleana. Isso pode ser útil em situações em que temos mais de uma condição que pode
encerrar o laço ou caso seja necessário, em algum momento posterior, verificar quais
restrições foram obedecidas pelo usuário, em especial se o programa precisar da
validação de múltiplas entradas.
...
n_valido = False
De início, atribui-se False a flag booleana para forçar que o fluxo de execução
acesse o bloco de instruções do laço, que pode ser lido como “repita enquanto a flag
não for válida”. Uma vez no laço, recebe-se a entrada e verifica-se as condições da
validação e, caso elas sejam atendidas, altera-se a flag para True. Observe que esta
solução é semelhante à do laço repeat…until, pois a condição no if é a mesma: “o
número é válido?”. Porém, ao invés de break, alteramos o valor da flag, e o laço será
interrompido na próxima verificação à condição do while (com break, o laço é
interrompido instantaneamente).
Figura 8.4: Laço aninhado em fluxograma e em Python. Fonte: Elaborado pelo autor.
A Codificação 8.9 é válida, mas a forma como os segundos são exibidos ainda
não se assemelha à de um relógio, então vamos para o próximo enunciado: “Altere a
função para que os segundos sejam exibidos no formato de um relógio digital, ou seja,
hh:mm:ss. Use a função sleep da biblioteca time para aguardar um segundo entre as
exibições”. A Codificação 8.10 é uma solução válida para essa atualização do problema.
def relogio():
s = 0
while s < 60:
print(f'00:00:{s:02}')
sleep(1)
s += 1
Codificação 8.10: Segunda versão da função que simula um relógio.
def relogio():
m = 0
while m < 60:
s = 0
while s < 60:
print(f'00:{m:02}:{s:02}')
sleep(1)
s += 1
m += 1
Codificação 8.11: Terceira versão da função que simula um relógio.
1
Dica: para testar o código, altere o tempo de espera na função sleep para um valor pequeno, como 1
centésimo ou 1 milésimo de segundo: sleep(0.01) ou sleep(0.001).
def relogio():
h = 0
while h < 24:
m = 0
while m < 60:
s = 0
while s < 60:
print(f'{h:02}:{m:02}:{s:02}')
sleep(1)
s += 1
m += 1
h += 1
Codificação 8.12: Quarta versão da função que simula um relógio.
def relogio():
while True:
h = 0
while h < 24:
m = 0
while m < 60:
s = 0
while s < 60:
print(f'{h:02}:{m:02}:{s:02}')
sleep(1)
s += 1
m += 1
h += 1
Codificação 8.13: Quinta versão da função que simula um relógio.
Outro exemplo2 de aplicação de laços aninhados: “crie uma função que receba
como parâmetro um número natural n e desenhe um tabuleiro n x n, no qual os
quadrados escuros sejam desenhados com dois caracteres █, dado pelo código Unicode
9608, e os quadrados claros com dois espaços. Dica: assumindo que a primeira linha
do tabuleiro é a zero, assim como a primeira coluna, podemos descobrir como desenhar
os quadrados verificando se a soma de sua linha com sua coluna é par, isto é, quando a
soma for par, temos um quadrado escuro, e quando a soma for ímpar, temos um
quadrado claro.”. A solução pode ser vista na Codificação 10.14, e a Figura 10.5 ilustra
um exemplo de execução da função tabuleiro de 5 por 5 casas.
2
Exemplo baseado em Pereira (2021).
def tabuleiro(n):
linha = 0
while linha < n:
coluna = 0
while coluna < n:
if (linha+coluna) % 2 == 0:
print(2 * chr(9608), end='')
else:
print(2 * ' ', end='')
coluna += 1
print()
linha += 1
Codificação 8.14: Função que desenha um tabuleiro de tamanho personalizado.
Figura 8.5: Exemplo de execução da função tabuleiro. Fonte: Elaborado pelo autor.
🏹 VAMOS PRATICAR!
2) Crie uma nova versão da função relogio em que o horário inicial seja 23:59:59 e a
função exiba a contagem regressiva até 00:00:00.
3) Refaça a Codificação 10.14 de modo que o tabuleiro não seja necessariamente
quadrado, ou seja, o número de linhas possa ser diferente do número de colunas. Note
que será necessário incluir mais um parâmetro na função para representar as colunas.
4) Crie um programa que chame uma função com
três parâmetros. O primeiro parâmetro é um natural
linhas, o segundo é um natural colunas e o
terceiro um caractere s. A função deve exibir um
retângulo de tamanho linhas x colunas composto
apenas por s, conforme exemplo à direita.
Bibliografia e referências
DOWNEY, A. B. Iteração. In: Pense em Python. São Paulo: Editora Novatec, 2016.
cap. 7. Disponível em: <https://penseallen.github.io/PensePython2e/
07-iteracao.html>. Acesso em: 07 de fev. 2021.
PEREIRA, S. L. Fundamentos I. In: Análise de Algoritmos. 2021. Disponível em:
<https://www.ime.usp.br/~slago/aa-02.ppsx>. Acesso em: 18 mar. 2021.
STURTZ, J. Python "while" Loops (Indefinite Iteration). Real Python, 2020.
Disponível em: <https://realpython.com/python-while-loop/>. Acesso em: 12 fev.
2021.
TAGLIAFERRI, L. How To Use Break, Continue, and Pass Statements when
Working with Loops in Python 3. 2017. Disponível em:
<https://www.digitalocean.com/community/tutorials/how-to-use-break-continue-and-
pass-statements-when-working-with-loops-in-python-3>. Acesso em: 14 fev. 2021.
Texto base
9
Sequências e estrutura de repetição definida (for)
Resumo
Nesta aula os objetivos são: (I) compreender o que são sequências; (II) conhecer os
tipos de sequências em Python: strings, listas, tuplas e intervalos; (III) distinguir
sequências mutáveis e imutáveis; (IV) distinguir sequências homogêneas e
heterogêneas; (V) percorrer sequências por meio de seus índices; (VI) entender a
estrutura de repetição com quantidade de repetições definida: laço for; (VII) percorrer
sequências por meio de seus itens com o laço for.
9.1. Motivação
Ao lidarmos com programas maiores e mais complexos, torna-se evidente a
necessidade manipular mais dados e, consequentemente, melhorar o gerenciamento
deles. Logo, precisamos de estruturas eficientes para manipulação de dados. Neste
capítulo introduziremos estruturas que permitirão armazenar grandes quantidades de
dados e também um laço de repetição bastante útil para trabalhar com elas!
9.2. Introdução
Não é raro lidarmos com problemas que exigem manipulação de uma grande
quantidade de dados. Nestes casos, pensar em armazená-los em variáveis simples é algo
difícil, indesejável e, às vezes, impossível. Para essas situações aprenderemos o conceito
de sequências, um recurso importante e frequente em programas mais avançados.
No entanto, não é muito útil armazenar uma imensa quantidade de dados se não
puderem ser acessados de modo simples e rápido, para isso aprenderemos a segunda
estrutura de repetição do Python, o laço de repetição for, em que a quantidade de
repetições do bloco de código é definida antes do loop ser iniciado. Este laço em Python
possui uma sintaxe que o torna especialmente útil para acessar itens de sequências,
facilitando a escrita e a leitura do código.
9.3. Sequências
Ao trabalhar com soluções para problemas mais complexos, é frequente a
necessidade de manipulação de grandes quantidades de dados. Para lidar com muitos
dados, é indispensável uma forma eficiente de armazenamento e acesso, evitando, por
exemplo, a criação de diversas variáveis. Até porque, em certos problemas, é impossível
criar a quantidade necessária de variáveis para guardar todos os valores, pois muitas
vezes a quantidade exata só será conhecida durante a execução do programa. Para esses
casos, usaremos um recurso da linguagem Python: as sequências.
Podemos imaginar uma sequência como uma variável com diversos
compartimentos, chamados de itens da sequência, inclusive o termo “sequência” indica
que há noção de ordem entre os itens (1º, 2º, 3º etc.). Cada item é identificado por dois
valores: o nome da variável à qual a sequência foi atribuída e um número inteiro
referente à sua posição na sequência, chamado índice.
Em Python, os índices são contados a partir do zero, em ordem crescente, logo o
primeiro item tem índice zero, o segundo tem índice um, o terceiro tem índice dois e
assim sucessivamente.
Denominamos como tamanho da sequência a quantidade de itens que ela possui.
Note que, como consequência da forma como os índices são associados aos itens,
começando em zero, o último índice sempre será um a menos que o tamanho da
sequência, ou seja, se tivermos uma sequência com 5 itens, o último terá índice 4. O
último índice é, portanto, dado pela fórmula 𝑡𝑎𝑚𝑎𝑛ℎ𝑜 𝑑𝑎 𝑠𝑒𝑞𝑢ê𝑛𝑐𝑖𝑎 − 1.
No entanto, Python possui uma característica pouco usual em outras linguagens,
que é a existência de índices negativos ou regressivos, isto é, os itens são associados a
dois índices, um que marca a posição relativa ao início da sequência e outro que marca a
posição relativa ao fim dela. Logo, o último item da sequência também está associado ao
índice -1, o penúltimo ao índice -2 e assim sucessivamente, sempre reduzindo uma
unidade a cada item mais próximo ao início da sequência. A Figura 9.1 é uma ilustração
simplificada de uma sequência de números inteiros que foi atribuída à variável idades.
Figura 9.1: Uma sequência atribuída a uma variável. Fonte: Elaborado pelo autor.
Figura 9.2: Modificação de item de uma sequência mutável. Fonte: Elaborado pelo autor.
Note que itens de sequências imutáveis não podem ser modificados, porém isso
não impede que a variável que referencia a sequência receba um novo valor. É
importante observar que, desta forma, sobrescreve-se o valor da variável e não o valor
de um item da sequência. A Figura 9.4 ilustra uma atribuição a uma variável que
referencia uma sequência (mutável ou imutável, é irrelevante) e, consequentemente,
deixa de referenciá-la.
No exemplo da Figura 9.4, caso a sequência não esteja referenciada por outra
variável qualquer, será automaticamente apagada da memória pelo Python, pois será
interpretado que é uma sequência sem uso, pois não há como acessá-la. A relação
completa de operações disponíveis para sequências é chamada de “Operações Comuns
de Sequências” (PSF1, 2021a). Para as sequências mutáveis, também existem as
operações listadas em “Tipos de Sequências Mutáveis” (PSF, 2021b).
1
PSF - Python Software Foundation.
9.3.3.1. Strings
Já introduzimos o tipo de dados string, porém, propositalmente, não trabalhamos
com suas características de sequência. As strings em Python são sequências imutáveis e
homogêneas, em que todos os itens devem ser caracteres Unicode. Assim, como em
outras sequências, é possível acessar um item específico da string indexando-a. Podem
ser definidas de várias formas:
Indica-se string vazia com um par de delimitadores sem nenhum conteúdo entre
eles ''. Observe que a string ' ' não está vazia, pois contém um caractere de espaço.
Veja alguns exemplos de manipulação desse tipo de sequência na Figura 9.6.
Figura 9.6: Exemplos de operações com string. Fonte: Elaborado pelo autor.
9.3.3.2. Listas
Sequências do tipo list são mutáveis e potencialmente heterogêneas. Veja alguns
exemplos de manipulação desse tipo de sequência na Figura 9.7, que pode ser definidas
de várias formas:
Figura 9.7: Exemplos de operações com lista. Fonte: Elaborado pelo autor.
9.3.3.3. Tuplas
Sequências do tipo tuple são imutáveis e potencialmente heterogêneas, seu uso é
comum quando é necessário garantir que a sequência não seja modificada, como em
chaves de dicionários ou ao ser passada como argumento para funções que produzam
efeitos colaterais. Veja alguns exemplos de manipulação desse tipo de sequência na
Figura 9.8. Assim como listas, tuplas podem ser definidas de várias formas:
Figura 9.8: Exemplos de operações com tupla. Fonte: Elaborado pelo autor.
9.3.3.4. Intervalos
Sequências do tipo range são imutáveis e homogêneas, gerando um intervalo de
números inteiros, algo útil principalmente para controle de laços de repetição. Intervalos
são criados com uso do construtor range() acompanhado dos argumentos início,
fim e passo, que devem ser números inteiros. Existem três variações para criação de
intervalos, dependendo do número de argumentos:
Figura 9.9: Exemplos de operações com intervalo. Fonte: Elaborado pelo autor.
📚 VAMOS LER!
Em Python 3, a sequência range é baseada em uma técnica de programação denominada
“avaliação preguiçosa”, em que o processamento para gerar um valor é propositalmente
atrasado até o instante em que o valor seja realmente necessário. Essa estratégia permite
economia de recursos computacionais, como processamento e memória. Veja mais
detalhes em: Avaliação preguiçosa – Wikipédia, a enciclopédia livre
media = soma / 4
print(f'Média = R$ {media:.2f}')
Na Codificação 9.1 foi necessário criar quatro variáveis para guardar quatro
entradas do usuário. Se fossem mil salários, seriam necessárias mil variáveis. Porém,
podemos construir uma versão alternativa que, ao invés de usar variáveis simples, usará
uma lista para guardar os salários, conforme a Codificação 9.2.
salarios = [0, 0, 0, 0]
soma = 0
media = soma / 4
print(f'Média = R$ {media:.2f}')
Você notou que a Codificação 9.2 é maior que a Codificação 9.1 e talvez imagine
que não houve benefício em substituir as variáveis simples pela lista. E você está certo!
Do modo como a lista foi usada, não houve ganho e a legibilidade foi prejudicada, isso
porque a usamos de modo idêntico às variáveis simples, escrevendo uma instrução para
cada acesso e atribuição, mesmo que essas instruções estejam praticamente idênticas,
variando apenas os índices dos itens. O que precisamos é de uma abordagem diferente.
Podemos percorrer listas variando apenas seu índice, uma vez que o nome da
variável que referencia a sequência é sempre o mesmo. Com esse recurso, podemos usar
uma variável como índice e alterá-la de acordo com a necessidade de indexação. Essa
flexibilidade permite a abordagem da Codificação 9.3, em que repetimos as atribuições e
acessos aos itens usando loop ao invés de reescrever as instruções.
salarios = [0, 0, 0, 0]
soma = 0
media = soma / 4
print(f'Média = R$ {media:.2f}')
for (i = 0; i < n; i += 1)
<bloco de código>
Codificação 9.4: Laço for tradicional, comum em outras linguagens de programação.
No entanto, esse tipo de laço for tradicional não existe em Python. Em seu
lugar temos um laço que opera de maneira mais intuitiva sobre as sequências, e que
reduz a chance de erros na manipulação dos índices, que é necessária para escrevermos
tanto o laço while, vistos nos exemplos anteriores, quanto o laço for tradicional.
Esse laço é conhecido, em inglês, como for each, que pode ser traduzido
como “para cada”, e podemos então interpretar essa estrutura de repetição como “para
cada item da sequência, faça…”. Veja na Codificação 9.5 a sintaxe adotada pelo Python.
salarios = []
soma = 0
for _ in range(4):
salario = float(input('Salário: R$ '))
soma += salario
salarios.append(salario)
media = soma / 4
print(f'Média = R$ {media:.2f}')
Na Codificação 9.6 foram usados dois laços for: (I) um para receber os valores
dos salários, acumulá-los na variável soma e adicioná-los à lista e; (II) outro para
percorrer a lista de salários e exibir os inferiores a média. Note que o nome da variável
referencia seu conteúdo e está no plural, indicando que é uma sequência de salários.
Note que a função range retorna um intervalo [0..4[, e portanto o laço será
executado 4 vezes, como usamos append para incluir os itens na lista, não precisamos
nos preocupar em gerenciar índices.
2
Essa inicialização é necessária em linguagens de nível mais baixo, isto é, mais próximas à linguagem de
máquina, como C e C++. Esse gerenciamento de alocação de memória é mais trabalho e mais propenso a
erros, porém torna o código mais eficiente quando bem feito, sendo útil em diversas aplicações críticas.
print()
nomes = []
for _ in range(5):
nomes.append(input('Nome: '))
qtd = 0
for nome in nomes:
if (nome[0]=='A' or nome[0]=='E' or nome[0]=='I' or
nome[0]=='O' or nome[0]=='U'):
qtd += 1
print()
🏹 VAMOS PRATICAR!
Refaça a solução da Codificação 9.6 para dois novos cenários:
1) O programa perguntará quantos salários serão inseridos, em seguida receberá cada
salário, calculará a média e exibirá todos os salários que sejam inferiores à média.
2) O programa receberá os salários indefinidamente, até que o usuário digite o valor -1
como salário. Então o programa deve seguir o restante do fluxo, calcular a média e
exibir todos os salários inferiores, como no cenário 1. Neste caso, será necessário usar
while, pois a quantidade de salários não está definida antes da execução do laço.
👁️🗨️
VOCÊ SABIA?
Esse tipo de laço for é tão útil e vantajoso que diversas linguagens de programação têm
uma implementação equivalente, como C++, C#, Java, JavaScript, Pascal, Perl, PHP,
Ruby, Rust e Visual Basic. Cada linguagem adota uma sintaxe, mas todas com o mesmo
propósito: percorrer um conjunto de itens, um de cada vez, sem a necessidade de manter
um índice explícito, executar um bloco de instruções para cada item e encerrar quando
não houver mais itens. Veja mais em: https://en.wikipedia.org/wiki/Foreach_loop
Bibliografia e referências
DOWNEY, A. B. Listas. In: Pense em Python. São Paulo: Editora Novatec, 2016. cap.
10. Disponível em: <https://penseallen.github.io/PensePython2e/10-listas.html>.
Acesso em 21 de fev. 2021.
DOWNEY, A. B. Tuplas. In: Pense em Python. São Paulo: Editora Novatec, 2016. cap.
12. Disponível em: <https://penseallen.github.io/PensePython2e/12-tuplas.html>.
Acesso em 21 de fev. 2021.
PROGRAMIZ, L. Python for Loop. 2017. Disponível em: <https://www.programiz.
com/python-programming/for-loop>. Acesso em: 14 fev. 2021.
PSF. Tipos Embutidos: Operações Comuns de Sequências. 2021a. Disponível em:
<https://docs.python.org/3/library/stdtypes.html#common-sequence-operations>.
Acesso em: 24 fev. 2021.
PSF. Tipos Embutidos: Tipos Sequências Mutáveis. 2021b. Disponível em:
<https://docs.python.org/3/library/stdtypes.html#mutable-sequence-types>. Acesso
em: 24 fev. 2021.
STURTZ, J. Lists and Tuples in Python. Real Python, 2018. Disponível em:
<https://realpython.com/python-lists-tuples/>. Acesso em: 21 fev. 2021.
WEBER, B. Defining Main Functions in Python. Real Python, 2019. Disponível em:
<https://realpython.com/python-main-function/>. Acesso em: 20 fev. 2021.
Texto base
10
Operações básicas com sequências
Resumo
Nesta aula os objetivos são: (I) explorar o funcionamento do Python em relação aos
tipos de dados e sua representação em memória; (II) conhecer as principais funções e
métodos para trabalhar com sequências; (III) compreender os tipos de passagem de
argumentos para funções e como são aplicados em Python.
10.1. Motivação
Para manipularmos dados em sequências de maneira eficaz, é importante entender
quais operações estão disponíveis para todas as sequências e quais são específicas para
determinado tipo. Também é necessário aprender o que são objetos e como Python trata
os objetos criados na memória, inclusive na passagem de argumentos para funções. Com
esse conhecimento, poderemos escolher abordagens mais adequadas para cada problema.
10.2. Introdução
Até o momento, vimos que, em Python, uma possível classificação das sequências
é em relação a sua mutabilidade, isto é, alguns tipos de sequências podem ter seus itens
modificados e outros não. Portanto, veremos neste capítulo quais operações são comuns
a todos os tipos de sequência e quais são válidas apenas para sequências mutáveis.
Também veremos que muitas dessas operações são feitas por meio de métodos.
Por isso, será necessário introduzir o conceito de objeto e de método, além da relação
entre eles e como isso se aplica em Python.
Por fim, estudaremos o comportamento dos objetos quando passados como
argumentos para funções integradas, importadas ou definidas pelo programador.
● Pertencimento;
● Concatenação e repetição;
● Indexação e fatiamento;
● Tamanho, item mínimo e item máximo;
● Busca por valor;
● Contagem de ocorrências.
10.4.1. Pertencimento
Verifica se um item pertence à uma sequência. A operação de pertencimento é
feita com o operador in e a operação inversa pode ser feita com o operador not in.
Veja na Codificação 10.4.
>>> 3 in [1, 2, 5]
False
>>> 5 in range(10)
True
>>> 'z' not in 'banana'
True
>>> 3 * 'Abc'
'AbcAbcAbc'
>>> 5 * [0]
[0, 0, 0, 0, 0]
>>> 4 * (123,)
(123, 123, 123, 123)
Aparentemente, criamos uma lista com três sub-listas contendo o número 2. Mas
ao alterarmos o primeiro item da primeira sub-lista, as demais sub-listas também são
alteradas, como consta na Codificação 10.9.
>>> s[0][0] = 5
>>> s
[[5], [5], [5]]
Teste os exemplos das Codificações 10.9 e 10.10 no Python Tutor para visualizar
o que aconteceu e para aprender mais a respeito, leia a discussão na página da PSF em
https://docs.python.org/pt-br/3/faq/programming.html#faq-multidimensional-list.
>>> lista = [30, 12, 13, 41, 15, 6, 78, 44, 19]
>>> lista[2:4] # se omitido, passo é 1
[13, 41]
>>> lista[1::3] # se omitido, fim é len(lista)
[12, 15, 44]
>>> lista[:3] # se omitido, início é 0
[30, 12, 13]
>>> lista[5:1] # se fim <= início, a fatia é vazia
[]
>>> lista[5:1:-1] # se passo é negativo, inverte a ordem da fatia
[6, 15, 41, 13]
>>> lista[:] # fatia correspondente à lista inteira
[30, 12, 13, 41, 15, 6, 78, 44, 19]
Função Descrição
>>> lista = [30, 12, 13, 41, 15, 6, 78, 44, 19]
>>> len(lista)
9
>>> len(range(3, 10, 2))
4
>>> sum(lista)
258
>>> max(lista)
78
>>> min('Python') # Por que a menor letra é 'P' e não 'h'?1
'P'
Codificação 10.13: Utilização das funções integradas len, sum, min e max.
1
Os caracteres são comparados por seus códigos na tabela Unicode e, neste caso, letras latinas maiúsculas
antecedem as minúsculas. Para recordar basta usar as funções ord(caractere) e chr(código).
2
O texto na mensagem de erro varia de acordo com o tipo de sequência.
Na Codificação 10.18, o número 100 foi adicionado ao final da lista, assim como
a lista [1, 2, 3], que também foi adicionada inteiramente com um único item.
Na Codificação 10.19, vemos que a lista original foi estendida com cada um dos
itens da tupla (1, 2, 3). A sequência que recebe os itens precisa ser mutável, portanto
uma lista, mas a sequência que cede os itens pode ser de qualquer tipo.
>>> lista = [15, 10, 99]
>>> lista.extend((1, 2, 3))
[15, 10, 99, 1, 2, 3]
Codificação 10.19: Extensão de uma sequência (mutável) com itens de outra sequência.
É importante observar que nas duas primeiras formas, com del e pop, o item é
diretamente acessado por meio de seu índice, portanto é uma operação com custo
computacional constante, independente do tamanho da lista. Já no método remove, a
lista é percorrida até que o valor seja encontrado ou ela termine, portanto há um laço
implícito e o custo computacional cresce proporcionalmente ao tamanho da lista.
O inverso é feito com o método join, que recebe como argumento uma
sequência de strings e gera uma nova string com os itens do argumento concatenados e
separados pelos caracteres da string em que foi chamado, como na Codificação 12.26.
>>> '_'.join(['1', '2', '3', '4', '5'])
'1_2_3_4_5'
>>> '_sep_'.join(['a', 'b', 'c'])
'a_sep_b_sep_c'
>>> ''.join(['a', 'b', 'c']) # separador é uma string vazia
'abc'
>>> ' '.join(['a', 'b', 'c']) # separador é um espaço
'a b c'
Codificação 10.26: Utilização do método join para unir uma lista de strings.
A variável nome não precisou de uma nova atribuição, pois o tipo de seu valor já
estava correto no primeiro desempacotamento, afinal seu conteúdo deve ser uma string.
Na codificação 10.29, vemos um exemplo desse desempacotamento sendo feito
diretamente para os parâmetros de uma função, que é feito com o uso de um asterisco
antes do argumento passado à função.
def teste(a, b, c):
print(f'a = {a} | b = {b} | c = {c}')
Note que usar desempacotamento para passar argumentos a uma função não tem
relação com a forma como ela foi definida, pois a função receberá os argumentos já
desempacotados, ou seja, uma chamada comum. Porém, é necessário garantir que o
desempacotamento resulte em quantidade de argumentos igual a de parâmetros.
Entretanto, há funções com quantidade variável de argumentos, como a função
print. Não entraremos em detalhes de como construir essas funções, mas você poderá
👁️🗨️
aprender mais sobre elas em Mastromatteo (2019).
VOCÊ SABIA?
É possível unir os dois recursos de Python, uma função com quantidade variável de
argumentos e o desempacotamento de uma sequência para passar argumentos à função.
Assim, podemos, por exemplo, exibir todos os itens de uma lista só com uma instrução:
>>> lista = [2, 3, 5, 7]
>>> print(*lista)
n = 7
valido = True
letra = 'r'
Codificação 10.32: Chamada à uma função com passagem de argumento por valor.
3
Se esse fosse o caso, é possível que os programas em Python teriam muitos bugs, e a linguagem
dificilmente teria ganho a notoriedade que possui hoje em tantas áreas diferentes da programação.
4
Isso só ocorre quando usamos funções ou métodos especiais que acessam os itens do objeto. Se fizermos
uma atribuição direta de outro valor à variável, a referência será sobrescrita normalmente, independente
da mutabilidade do objeto.
Figura 10.5: Mutabilidade dos principais tipos em Python. Fonte: Elaborado pelo autor.
n1 = 1
lista = [1]
teste(n1, lista)
print('fim')
5
Note que isso não significa que sejamos obrigados a alterar o próprio objeto, é perfeitamente possível
criar funções que não possuam efeitos colaterais mesmo quando usadas com objetos mutáveis, como
mostrado na Codificação 10.34.
converte_1(numeros_1)
numeros_3 = converte_2(numeros_2)
Codificação 10.34: Programa para visualização de funções com e sem efeitos colaterais.
Bibliografia e referências
Texto base
11
Ordenação e busca em sequências
Resumo
Nesta aula os objetivos são: (I) entender a necessidade de ordenar itens de sequências;
(II) construir algoritmos para ordenação de sequências mutáveis; (III) vislumbrar
outros algoritmos de ordenação; (IV) conhecer recursos integrados ao Python para
ordenação; (V) entender como buscar itens em sequências; (VI) conhecer e
implementar algoritmos de busca linear e busca binária.
11.1. Motivação
Ao trabalhar com grande quantidade de dados, duas operações costumam ocorrer
com frequência: (I) a busca por dados específicos e (II) a ordenação dos dados por algum
critério, o que pode, inclusive, influenciar nas buscas. Neste capítulo estudaremos os
conceitos envolvidos nesses algoritmos e construiremos programas para essa finalidade.
Também veremos mais características e recursos que podem ser adicionados ao uso e
criação de funções, são os argumentos nomeados e argumentos padrão.
💎 VOCÊ CONHECE?
Margaret Hamilton, uma cientista da computação, engenheira
de software e ex-diretora da Divisão de Software do MIT.
Atuou no programa de voo do projeto Apollo 11 da NASA.
Seu software permitiu que o pouso na Lua não fosse abortado,
priorizando instruções críticas. Pelos seus feitos notáveis, foi
homenageada pelo Google: https://youtu.be/B7CnVGtd1_Y
Fonte da imagem: https://computerhistory.org/profile/margaret-hamilton/
11.2. Ordenação
Ordenar uma sequência consiste em reorganizar seus itens seguindo uma ordem,
por exemplo, dispondo-os em sequência crescente ou decrescente. Geralmente,
sequências ordenadas possibilitam buscas mais eficientes aos seus itens. Há diversos
algoritmos para ordenação, na seção 11.2.5 abordaremos alguns populares. Para
conhecer a função e o método de ordenação integrados do Python, há a seção 11.2.6.
Codificação 11.1: Troca entre itens de uma lista usando uma variável temporária.
Codificação 11.2: Troca entre itens de uma lista usando atribuição paralela.
Podemos melhorar! Criaremos uma função, que receberá como argumentos uma
sequência mutável s e índices i e j que sejam válidos em s. A função trocará s[i]
com s[j]. Veja essa função na Codificação 11.3 e um teste na Codificação 11.4.
def troca(s, i, j):
s[i], s[j] = s[j], s[i]
Codificação 11.3: Função que realiza a troca entre dois itens da sequência argumento.
Codificação 11.4: Troca entre itens de uma lista usando a função troca.
Codificação 11.5: Código que empurra o item máximo para o fim da sequência.
Note que a variável i assumirá os índices de lista, desde o primeiro até seu
penúltimo. Isso ocorre pois a condição do if se baseia em comparar pares de itens
adjacentes, em que o 1º está na posição i e o 2º, evidentemente, na posição i+1. Caso i
também assumisse o último índice, não seria possível formar um par, pois não existiria
item no índice i+1, pois se existisse, estaria após o último, o que é incoerente.
Veja na Tabela 11.1 o teste de mesa da Codificação 11.5 e observe a dinâmica
das trocas, em que o item máximo da dupla é empurrado em direção ao fim da lista.
lista
i lista[i] lista[i+1] troca?
[0] [1] [2] [3] [4]
40 30 20 50 10 0 40 30 True
30 40 20 50 10 1 40 20 True
30 20 40 50 10 2 40 50 False
30 20 40 50 10 3 50 10 True
30 20 40 10 50
Podemos melhorar! Criaremos uma função que receberá como argumentos uma
sequência mutável s e seu tamanho lógico n. A função empurrará o item máximo da
sequência s para o final dela, isto é, para a posição de índice n-1. Dizemos que n é o
tamanho lógico de s, pois pode diferir do tamanho físico, obtido com a função len.
Essa abordagem é útil em algoritmos onde é necessário trabalhar com apenas parte da
sequência, reduzindo-a apenas logicamente, sem excluir itens. Veja essa função na
Codificação 11.6 e um teste de execução na Codificação 11.7.
def empurra(s, n):
for i in range(n-1):
if s[i] > s[i+1]:
troca(s, i, i+1)
Codificação 11.6: Função que empurra o item máximo para o fim da sequência.
1
Lembre-se que na Codificação 11.7, len(lista) resultou no valor 5, que era o tamanho físico da lista.
Figura 11.1: Sequência de chamadas à função empurra. Fonte: Elaborado pelo autor.
def bubble_sort_2(lista):
lista = lista[:]
n = len(lista)
while n > 1:
empurra(lista, n)
n -= 1
return lista
11.2.4. Estabilidade
Dizemos que um algoritmo de ordenação é estável quando elementos iguais
mantêm a mesma ordem entre si após serem ordenados. Essa característica é útil quando
são feitas ordenações sucessivas na mesma sequência de dados como, por exemplo,
ordenar uma lista de clientes pelo nome e depois por nascimento, de modo que clientes
com o mesmo aniversário ainda permaneçam ordenados alfabeticamente. Ou seja, é útil
em aplicações onde uma sequência de itens podem ser ordenados por diversos filtros,
como por exemplo, no refinamento da busca por um produto em um site de compras.
Note que sorted não gera efeitos colaterais, logo a sequência argumento
permanece inalterada. Para situações em que a sequência original não precise ser
preservada, há o método sort, exclusivo para listas e mais eficiente em consumo de
memória, pois não a duplica. Veja um exemplo na Codificação 11.13.
>>> lista = [40, 30, 20, 50, 10]
>>> lista.sort()
>>> lista
[10, 20, 30, 40, 50]
👁️🗨️
VOCÊ SABIA?
O algoritmo de ordenação usado na função sorted e no método sort é o
Timsort, um algoritmo híbrido bastante eficiente, uma combinação do Merge Sort e
Insertion Sort. Foi implementado por Tim Peters em 2002 (Peters, 2002), e também é
utilizado em diversas outras linguagens de programação.
11.3. Busca
Ao trabalhar com coleções2 de dados é comum que buscas sejam feitas para
verificar se um item pertence à coleção ou para acessar dados associados ao item
buscado, também chamados de dados satélites, caso o item seja encontrado.
Há vários algoritmos de busca, alguns exigem pré-condições para que funcionem,
como a busca binária, um algoritmo com ótimo desempenho, mas que supõe que os
itens estejam ordenados em sequência crescente ou decrescente. Esse algoritmo permite
encontrar um item dentre 1 bilhão de itens com, no máximo, 31 comparações!
Outros algoritmos funcionam em coleções sem pré-condições, como a busca
linear. Neste algoritmo, o item buscado será comparado com cada item da coleção até
que a sequência termine ou até que seja encontrado. O custo dessa flexibilidade é o
baixo desempenho, por não ser possível aplicar estratégias mais sofisticadas.
Para uma abordagem inicial, pense no problema de encontrar uma palavra em
um dicionário de papel, ou um nome em uma lista telefônica, se alguém ainda se recorda.
Sabemos que nestas situações os itens estão em ordem alfabética, o que permite
abrir o dicionário aproximadamente ao meio e olhar se a palavra buscada está naquela
página, ou se ela deveria estar em uma página antecessora ou sucessora. Caso a palavra
não esteja na página aberta, podemos descartar essa página, juntamente com as
antecessoras (se a palavra estiver alfabeticamente à diante), ou juntamente com as
sucessoras (se a palavra estiver alfabeticamente atrás), e buscar a palavra novamente,
com o mesmo procedimento, nas páginas que restaram. Assim funciona a busca binária.
Agora, imagine que as palavras estão distribuídas aleatoriamente no dicionário,
sem uma ordem conhecida. Neste caso, o procedimento descrito anteriormente não
funcionaria, pois seria impossível ter certeza que a palavra buscada está antes ou após
aquelas da página corrente. Uma solução seria abrir o dicionário na primeira página e
ler todas as palavras sequencialmente até encontrar a palavra buscada. Assim funciona a
busca linear, também conhecida como busca sequencial.
2
Coleção é uma estrutura que permite armazenar itens. É um termo mais genérico do que “sequência”.
🏹 VAMOS PRATICAR!
1) Crie uma função semelhante a da Codificação 11.15, porém que retorne -1 quando
não encontrar o valor buscado. Esta versão mantém a coerência do tipo do valor
retornado em todos os casos, imprescindível em diversas linguagens de programação.
2) Crie uma função semelhante àquela da Codificação 11.15, porém que o valor de
retorno seja uma lista com os índices de todos os itens iguais ao valor buscado.
👁️🗨️
encontrado e será retornado um valor indicativo, neste caso, None.
VOCÊ SABIA?
O Python possui o módulo integrado bisect que possui recursos para inserir itens em
uma lista mantendo-a ordenada, além de recursos para busca.
Veja mais em: https://docs.python.org/pt-br/3/library/bisect.html
if valor == sequencia[meio]:
return meio
elif valor < sequencia[meio]:
fim = meio - 1
else:
inicio = meio + 1
return None
Bibliografia e referências
DOWNEY, A. B., Pense em Python. 1 ed. São Paulo: Novatec Editora Ltda., 2016.
PETERS, T., Mailing lists: [Python-Dev] Sorting. 2002. Disponível em: <https://mail.
python.org/pipermail/python-dev/2002-July/026837.html>. Acesso em: 14 mar. 2021.
PETERS, T., Descrição do algoritmo de ordenação do Python. 2021. Disponível em:
<https://github.com/python/cpython/blob/master/Objects/listsort.txt>. Acesso em: 14
mar. 2021.
PSF. Python HOWTOs: Ordenação. 2021a. Disponível em: <https://docs.python.org/
pt-br/3/howto/sorting.html#sortinghowto>. Acesso em: 14 mar. 2021.
ROSSUM, G. V. Código fonte dos objetos de lista. 2021. Disponível em:
<https://github.com/python/cpython/blob/master/Objects/listobject.c>. Acesso em:
14 mar. 2021.
Texto base
12
Sequências aninhadas e cópias de objetos
Resumo
Nesta aula os objetivos são: (I) conhecer as sequências aninhadas; (II) usar sequências
aninhadas para representar matrizes e tabelas; (III) recordar os conceitos de objetos
mutáveis e imutáveis; (IV) entender o mecanismo de cópia de objetos em Python; (V)
compreender o que são cópias rasas e profundas; (VI) aprender como chamar funções
com argumentos nomeados e como defini-las com argumentos padrão.
12.1. Motivação
Vimos que sequências são importantes ao lidar com grandes quantidades de
dados, como para calcular a média de altura de uma população, a folha de pagamento de
uma empresa, contar itens em um estoque e assim por diante, pois podemos usar laços
de repetição para iterar sobre essas sequências e realizar operações que na maioria das
vezes seriam inviáveis ou impossíveis se dependêssemos apenas de variáveis simples.
No entanto, há problemas que naturalmente apresentam um aninhamento dessas
informações, como por exemplo guardar as notas de todos os alunos de todas as turmas
de uma instituição de ensino. Aqui temos uma lista de turmas, cada turma é uma lista de
💎
alunos e para cada aluno temos uma lista de notas. Isto é, são sequências de sequências!
VOCÊ CONHECE?
Dennis Ritchie foi um cientista da computação, criador da linguagem
de programação C e co-criador do sistema operacional Unix.
Em 1983, juntamente com Ken Thompson, recebeu o prêmio Turing
por sua contribuição relacionada aos sistemas operacionais. Até hoje,
C é referência para outras linguagens de programação como C# e Java.
Fonte da imagem: http://brainprick.com/dennis-ritchie-the-creator-of-c-and-unix/
O aninhamento pode ser feito com objetos literais, como na Codificação 12.1, ou
com objetos que sejam referenciados por variáveis, como mostra a Codificação 12.2.
>>> t1 = [10, 11, 12]
>>> t2 = [20, 21, 22]
>>> t3 = [30, 31, 32]
>>> t = [t1, t2, t3]
1
Neste capítulo, se não houver indicação em contrário, o termo “sequência” será usado para referenciar
apenas listas e tuplas, pois intervalos e strings são sequências homogêneas e não são aptas a conterem
itens que sejam outras sequências, por mais que possam ser itens de outras sequências. A string é uma
sequência exclusivamente de caracteres e o intervalo uma sequência exclusivamente de números inteiros.
Codificação 12.6: Função para cálculo da média dos valores de uma sequência.
media_0 = calcula_media(aluno_0)
media_1 = calcula_media(aluno_1)
media_2 = calcula_media(aluno_2)
media_3 = calcula_media(aluno_3)
É possível representar a matriz da Figura 12.1 de forma mais adequada, sem usar
12 variáveis simples ou 4 listas. Até porque, caso a quantidade de alunos ou atividades
aumentasse, usar variáveis simples ou mesmo listas simples poderia se tornar inviável.
Por isso, criaremos uma matriz usando listas aninhadas, conforme Codificação 12.8.
alunos = [[5.5, 7.0, 8.7],
[8.0, 6.0, 9.2],
[7.8, 8.3, 8.5],
[0.0, 9.9, 9.1]]
Na Codificação 12.8 foi criada uma matriz de notas de alunos, em que cada linha
representa um aluno e cada coluna representa uma nota do respectivo aluno. Veja que
chamamos a matriz de alunos, pois essa variável referencia uma lista em que cada item
é uma lista de notas de um aluno.
Para criar uma tabela usando sequências aninhadas, como a ilustrada na Figura
12.2, o procedimento é semelhante e a pode ser visto na Codificação 12.9.
Desta forma, mesmo que alunos contenha milhares de alunos, podemos calcular
a média sem alterar a Codificação 12.10. Mas podemos melhorar! Construiremos uma
função que receberá como argumento uma matriz de notas de alunos de uma turma e
retornará uma lista com as médias dos alunos. Assim, será possível usar a mesma função
para o cálculo das médias de diversas turmas. Veja como na Codificação 12.11.
def calcula_medias(turma):
medias = []
for aluno in turma:
media = calcula_media(aluno)
medias.append(media)
return medias
Codificação 12.11: Função para cálculo da média de todos os alunos de uma turma.
em que cada linha representa as notas de um aluno em escala zero a dez. A função deve
atualizar a matriz para a escala zero a vinte, mantendo a proporção”. Na Codificação
12.12 há uma solução sem efeitos colaterais, ou seja, não altera o argumento passado.
def atualiza_notas_1(turma):
turma_atualizada = []
for aluno in turma:
aluno_atualizado = []
for nota in aluno:
aluno_atualizado.append(nota * 2)
turma_atualizada.append(aluno_atualizado)
return turma_atualizada
Observe que não precisamos mudar os itens de turma, apenas os itens das
sequências aninhadas em turma, logo o laço externo não precisa ser alterado, apenas o
laço interno, que agora irá operar sobre os índices de cada aluno, para que os valores
possam ser alterados. Porém, também podemos usar a abordagem da Codificação 12.14,
em que os itens da matriz são acessados por meio de seus dois índices: linha e coluna.
def atualiza_notas_3(turma):
for linha in range(len(turma)):
for coluna in range(len(turma[linha])):
turma[linha][coluna] *= 2
Codificação 12.14: Atualização de notas com acesso à matriz por sua linha e coluna.
def coleta_notas():
notas = input().split()
for i in range(len(notas)):
notas[i] = float(notas[i])
return notas
def preenche_turma(qtd_alunos):
turma = []
for i in range(qtd_alunos):
print(f'{i + 1}º aluno:', end=' ')
aluno = coleta_notas()
turma.append(aluno)
return turma
def calcula_media(aluno):
soma = 0
for nota in aluno:
soma += nota
return soma / len(aluno)
def resumo_turma(turma):
for aluno in turma:
media = calcula_media(aluno)
print(f'notas: {aluno} | média: {media:5.2f}')
Figura 12.3: Exemplo de execução da Codificação 12.15. Fonte: Elaborado pelo autor.
🏹 VAMOS PRATICAR!
1) Crie uma função que receba uma matriz numérica como argumento e retorne o valor
da soma de seus itens. Acesse os itens da matriz indexando-a com os dois índices.
2) Crie uma função que receba uma matriz numérica como argumento e devolva a
matriz oposta, ou seja, uma cópia da matriz argumento com os itens com sinal invertido.
👁️🗨️
VOCÊ SABIA?
Em Python, uma das principais ferramentas para trabalhar com vetores e matrizes
é a biblioteca Numpy, desenvolvida para realizar com maior eficiência cálculos
complexos com diferentes tipos de sequências. É uma biblioteca utilizada em áreas como
ciência de dados, engenharia, matemática aplicada, estatística, economia, entre outras.
Essa biblioteca, como outras do CPython, foi desenvolvida em C, mas sua
interface é Python, possibilitando a eficiência dos códigos em C, com a simplicidade da
sintaxe Python. Portanto, escreve-se um código fácil de ler e entender, sem abrir mão do
desempenho necessário para aplicações que tratam com enorme quantidade de dados.
O Numpy, juntamente com outras bibliotecas do Python que dependem dele,
como Scikit-image, SciPy, MatPlotLib e Pandas, foi usado em duas situações com
repercussão mundial: a geração da primeira imagem de um buraco negro (Numpy,
2020a) e a detecção de ondas gravitacionais (Numpy, 2020b) pelos cientistas do
Observatório de Ondas Gravitacionais por Interferômetro Laser (LIGO).
Em ambos os casos, Python foi usado para coletar, tratar, analisar e gerar
visualizações com terabytes de dados diários. São aplicações de Python em problemas
complexos, com muitos dados e com exigência de excelente desempenho computacional.
existem dois tipos de cópias em Python, a rasa e a profunda. A diferença entre cópia
rasa e profunda só é relevante quando o objeto copiado contém outros objetos mutáveis.
Figura 12.5: Cópia rasa de um objeto mutável com copy. Fonte: Elaborado pelo autor.
Para uma lista s, temos ainda as seguintes formas de obtermos cópias rasas:
● Com um fatiamento completo: s[:]
● Com o método copy dos objetos do tipo lista: s.copy()
● Com a função construtora de listas: list(s)
Assim como a função copy, as três formas anteriores retornam um novo objeto
lista, contendo itens com as mesmas referências da original, o que pode ser visto na
Figura 12.6. Contudo, esse tipo de cópia, dependendo do objeto copiado, não desvincula
a cópia completamente do objeto original, veremos mais detalhes no próximo tópico.
Figura 12.6: Cópias rasas de uma lista. Fonte: Elaborado pelo autor.
Figura 12.7: Cópias rasas de uma lista de listas. Fonte: Elaborado pelo autor.
Figura 12.8: Cópia profunda de uma lista de listas. Fonte: Elaborado pelo autor.
Figura 12.9: Alteração da renderização do Python Tutor. Fonte: Elaborado pelo autor.
Figura 12.10: Estado da memória após a 3ª instrução. Fonte: Elaborado pelo autor.
Figura 12.11: Estado da memória após a 4ª instrução. Fonte: Elaborado pelo autor.
a = 10
b = 5
div1 = divide(numerador=a, denominador=b)
div2 = divide(denominador=b, numerador=a)
div3 = divide(numerador=b, denominador=a)
print(f'1º: {a}/{b} = {div1}') # 1º: 10/5 = 2.0
print(f'2º: {a}/{b} = {div2}') # 2º: 10/5 = 2.0
print(f'3º: {b}/{a} = {div3}') # 3º: 5/10 = 0.5
Codificação 12.23: Função inválida pela ordem do parâmetro com argumento padrão.
Figura 12.12: Erro de sintaxe na Codificação 12.23. Fonte: Elaborado pelo autor.
Codificação 12.24: Solução inicial usando uma lista vazia como argumento padrão.
Se testarmos essa função na Shell do Python, passando uma lista como argumento,
veremos que ela se comporta como esperado, veja um exemplo na Codificação 12.25.
Porém, veja na Codificação 12.26 o que ocorre ao não passar o argumento lista.
>>> x = adiciona_item(5, [1, 3])
>>> x
[1, 3, 5]
>>> y = adiciona_item('c', ['a', 'b'])
>>> y
['a', 'b', 'c']
>>> x = adiciona_item(3)
>>> x
[3]
>>> y = adiciona_item('a')
>>> y
[3, 'a']
>>> z = adiciona_item(1)
>>> z
[3, 'a', 1]
lista.append(x)
return lista
>>> x = adiciona_item(3)
>>> x
[3]
>>> y = adiciona_item('a')
>>> y
['a']
>>> z = adiciona_item(1)
>>> z
[1]
Codificação 12.29: Função que guarda a quantidade de vezes que foi executada.
Bibliografia e referências
Case Study: First Image of a Black Hole. Numpy 2020a. Disponível em:
<https://numpy.org/case-studies/blackhole-image/>. Acesso em: 20 mar. 2021.
Case Study: Discovery of Gravitational Waves. Numpy 2020b. Disponível em:
<https://numpy.org/case-studies/gw-discov/>. Acesso em: 20 mar. 2021.
DOWNEY, A. B. Pense em Python. 1 ed. São Paulo: Novatec Editora Ltda., 2016.
PSF. The Python Language Reference: Function Definition. 2021b. Disponível em:
<https://docs.python.org/3/reference/compound_stmts.html#function-definitions>.
Acesso em: 14 mar. 2021.
PSF. The Python Tutorial: More Control Flow Tools. 2021c. Disponível em:
<https://docs.python.org/3/tutorial/controlflow.html#more-on-defining-functions>.
Acesso em: 15 mar. 2021.
STURTZ, J. Lists and Tuples in Python. Real Python, 2018. Disponível em: <https://
realpython.com/python-lists-tuples/#lists-can-be-nested>. Acesso em: 20 mar. 2021.