Você está na página 1de 183

Proceedings of the XII SIBGRAPI (October 1999) 101-104

Linguagem de Programação

Texto base

1
Introdução à disciplina e conceitos iniciais

Prof. Me. Lucio Nunes de Lira


Prof. MSc. Rafael Maximo Carreira Ribeiro

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.2. Diretrizes da disciplina


Para verificar as diretrizes da disciplina, conteúdo programático, metodologia de
ensino, critérios de avaliação e bibliografia, consulte o documento “Plano de Ensino”.

Núcleo de Educação a Distância | Faculdade Impacta


Linguagem de Programação

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

Núcleo de Educação a Distância | Faculdade Impacta


Linguagem de Programação

operações intelectuais que visam a determinação do que é verdadeiro ou falso. Faremos


uso de lógica para escrever as instruções de nossos algoritmos, de modo que a execução
de cada instrução faça sentido para atingir o objetivo final.
Segundo um dos mais importantes nomes da Computação, Knuth (1997), um
algoritmo tem cinco características fundamentais:
1) Finitude: um algoritmo precisa terminar após um número finito de passos;
2) Definição: cada passo do algoritmo precisa ser precisamente definido, as
ações devem ser especificadas clara e rigorosamente evitando ambiguidade;
3) Entrada: um algoritmo tem zero ou mais entradas, que são dados fornecidos
ao algoritmo ao ser iniciado, ou dinamicamente conforme for executado;
4) Saída: um algoritmo tem zero ou mais saídas, que geralmente estão
relacionadas com os dados fornecidos nas entradas;
5) Eficácia: é esperado que um algoritmo seja eficaz, no sentido que suas
operações devem ser suficientemente básicas para que possam, a princípio,
ser feitas precisamente e em um tempo finito por alguém com lápis e papel.
Nesta disciplina nossa atenção será direcionada para a lógica de programação,
para que possamos estudar as estruturas básicas para a construção de algoritmos
destinados a serem executados em computadores com uso de uma linguagem de
programação. Porém, antes de direcionarmos nossos estudos aos algoritmos propostos
para solucionar problemas computacionais, é preciso entender o que é a abstração e
como criar representações de problemas por meio da abstração.

1.4. Abstração e representação


Para resolver problemas computacionalmente são necessários pelo menos dois
instrumentos: (I) uma representação que capte todos os aspectos relevantes do
problema, descartando detalhes insignificantes para a sua resolução e; (II) um algoritmo
que resolva o problema com base em ações aplicadas na representação construída.
Compreenderemos a importância da representação no desenvolvimento de
algoritmos analisando um clássico problema de lógica, que chamaremos de “o homem e
o rio”. Este problema está ilustrado na capa da obra de Dierbach (2012), conforme a
figura à direita.
Um homem vive no lado leste de um rio e deseja vender
um lobo, um bode e um repolho em uma vila no lado oeste.
Porém, seu barco comporta apenas ele e mais um personagem.
Além disso, o homem não pode deixar o lobo sozinho com o
bode, porque o lobo comeria o bode, também não pode deixar o
bode sozinho com o repolho, porque o bode comeria o repolho.
Como o homem poderá atravessar todos em segurança?

Núcleo de Educação a Distância | Faculdade Impacta


Linguagem de Programação

Há uma abordagem óbvia para resolver este problema: escrever um algoritmo


que teste diversas sequências de combinações de duplas e viagens de uma margem à
outra do rio, até encontrar uma sequência válida, ou seja, que resolva o problema
obedecendo às restrições. Tentar todas as possibilidades buscando uma solução para um
problema é conhecido como “abordagem por força bruta”. Geralmente não é uma boa
estratégia, por ser ineficiente, consumindo muitos recursos como, por exemplo, tempo.
O que seria uma representação adequada para este problema? Uma vez que
apenas os aspectos relevantes do problema precisam ser representados, todos os detalhes
irrelevantes podem ser omitidos. Uma representação que deixa de fora detalhes do que
está sendo representado é uma forma de abstração.
O uso de abstração é predominante na computação. Pense, a cor do barco é
relevante? A largura do rio? O nome do homem? Não! A única informação relevante é
onde está cada personagem/entidade desse contexto, a cada momento antes e após a
execução de um passo/instrução da solução proposta para o problema. A localização
coletiva de cada entidade, neste caso, refere-se ao estado do problema. Assim, o estado
inicial do problema, que chamamos de α, pode ser representado conforme a Tabela 1.1.
Tabela 1.1: Estado inicial α.

Homem Lobo Bode Repolho


0 0 0 0
Note que nesta representação criamos uma tabela em que o nome das colunas
são as entidades relevantes no contexto do problema, e os valores das colunas são
indicações sobre os respectivos estados de cada entidade.
Arbitrariamente, definimos que colunas com 0 (zero) indicam que a respectiva
entidade está no lado leste do rio. Já colunas com 1 (um) indicam que a entidade está no
lado oeste do rio. Por exemplo, se a primeira instrução do algoritmo fosse “homem
atravessa com o bode de leste para oeste”, a representação do novo estado do problema,
gerado após a execução desta instrução, seria como ilustrado na Tabela 1.2. Neste novo
estado apenas homem e bode estão no lado oeste e as localizações do lobo e do repolho
não foram alteradas, pois a instrução executada não os impactou.
Tabela 1.2: Estado atual após a execução de uma instrução.

Homem Lobo Bode Repolho


1 0 1 0
Com essa representação plenamente assimilada, podemos estabelecer que uma
solução para este problema é a aplicação de um algoritmo que tenha como entrada o
estado inicial α (Tabela 1.1), processe α com uma sequência de instruções logicamente
ordenadas e que conduzam à saída do estado final β, ilustrado na Tabela 1.3.

Núcleo de Educação a Distância | Faculdade Impacta


Linguagem de Programação

Tabela 1.3: Estado final β.

Homem Lobo Bode Repolho


1 1 1 1
Portanto, podemos generalizar nossos algoritmos em três etapas: (I) receber
entradas; (II) processá-las e; (III) gerar saídas que resolvam o problema de acordo com
as entradas processadas. Esse conceito está ilustrado na Figura 1.1.

Figura 1.1: Generalização do processo algorítmico. Fonte: Elaborado pelo autor.

É importante diferenciar a “representação” do “algoritmo” que será aplicado


sobre ela. A tabela é apenas uma forma de representar o “mundo do problema” que
queremos resolver, já o algoritmo é uma sequência de ações que serão aplicadas neste
“mundo” de forma a deixá-lo no estado que desejamos, solucionando o problema.

🏹 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.

1.5. Linguagens de Programação


Até agora discutimos sobre algoritmos de forma genérica, porém, o foco da
disciplina é o estudo de algoritmos que serão executados em computadores, ou seja,
serão transformados em programas. Thomas Cormen, conceitua esse tipo de algoritmo:
Também podemos considerar um algoritmo como uma ferramenta
para resolver um problema computacional bem especificado. O
enunciado do problema especifica em termos gerais a relação desejada
entre entrada e saída. O algoritmo descreve um procedimento
computacional específico para se conseguir essa relação entre entrada
e saída. (CORMEN et al., 2012, p. 3).

Núcleo de Educação a Distância | Faculdade Impacta


Linguagem de Programação

Para que os algoritmos possam ser executados automaticamente é necessário que


sejam escritos em linguagens compreensíveis aos computadores, que são as linguagens
de programação. A codificação, também chamada de implementação, é a fase em que o
programador escreve seus algoritmos em uma linguagem de programação.
Como escrito por Lira (2019, p. 29), “Linguagens de programação são conjuntos
de símbolos e regras de sintaxe que permitem a construção de instruções que descrevem,
de forma não ambígua, ações que podem ser entendidas e executadas por meio de
computadores”. Cada linguagem de programação define um padrão para a escrita de
suas instruções, esses padrões têm motivações diversas, algumas linguagens favorecem
a legibilidade para humanos, outras a concisão, ou o desempenho na execução etc.
Em nossa disciplina utilizaremos a linguagem de programação Python 3, que
dentre as vantagens destacam-se: (I) sintaxe relativamente simples, o que pode ajudar no
ensino e aprendizagem; (II) crescimento de popularidade no meio acadêmico e
profissional, facilitando a busca de materiais de apoio, ajuda em fóruns e
empregabilidade; (III) é portátil, ou seja, pode ser usada em diversos sistemas
operacionais, como Windows, MacOS e Linux; (IV) é uma linguagem de código aberto
e suportada por diversos ambientes gratuitos para programação.
Para entender como criar algoritmos que serão implementados em linguagens de
programação, é útil começar com a apresentação dos principais componentes envolvidos
nos sistemas computacionais, porém com objetos comuns, de forma lúdica, sem abordar
diretamente um computador. Para isso, conheceremos o Computador Simplificado.

1.6. Computador Simplificado


Podemos simular procedimentos computacionais sem a necessidade de um
computador, aprendendo conceitos básicos e usando objetos do dia a dia que, em
seguida, serão mapeados para correspondentes de um sistema computacional eletrônico.
O Computador Simplificado (que abreviaremos como CS) se encaixa neste contexto!
À direita da Figura 1.2 está um algoritmo com seis instruções, à esquerda há
uma representação simplificada de um sistema computacional sem referência direta aos
componentes de um computador, apenas objetos do cotidiano com função equivalente.
No CS, temos: (I) ao centro, uma pessoa chamada Megan, responsável por
executar as instruções do algoritmo; (II) à esquerda, papéis descartáveis, cada um
representando uma entrada de dados; (III) à direita, uma tela para exibir dados para
outras pessoas; (IV) acima, uma lousa em que cada parte tem um nome, em nosso caso
uma única letra, e pode guardar dados temporariamente, afinal pode ser facilmente
apagada; (V) abaixo, um diário para registrar dados, de modo a armazená-los por longos
períodos. As setas ao redor de Megan indicam o fluxo em que os dados podem trafegar.

Núcleo de Educação a Distância | Faculdade Impacta


Linguagem de Programação

Figura 1.2: Algoritmo e CS (nota sobre direitos autorais 1). Fonte: Elaborado pelo autor.

Suponha que o algoritmo comece a ser executado. A primeira instrução solicita


que Megan leia um papel e guarde o valor na parte da lousa chamada A. Após a
execução desta instrução, o estado será alterado conforme o ilustrado na Figura 1.3.

Figura 1.3: CS após a execução da 1ª instrução. 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.

Núcleo de Educação a Distância | Faculdade Impacta


Linguagem de Programação

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.

Figura 1.4: CS após a execução da 2ª instrução. Fonte: Elaborado pelo autor.

Após a execução da quarta instrução (propositalmente pulamos a terceira), o


estado do CS estará conforme a Figura 1.5. É importante observar que Megan pode
tanto escrever na lousa quanto consultá-la, assim como no diário. A exibição de dados
na tela corresponde ao que anteriormente denominamos como “saída” do algoritmo.

Figura 1.5: CS após a execução da 4ª instrução. Fonte: Elaborado pelo autor.

Na Figura 1.6 observamos o que ocorre após a execução da quinta instrução do


algoritmo, que registra no diário o conteúdo de Z dividido por 2. Registrar dados

Núcleo de Educação a Distância | Faculdade Impacta


Linguagem de Programação

corresponde a guardá-los em um local em que não sejam apagados tão facilmente,


diferentemente daqueles que estão na lousa, uma área de trabalho temporária.

Figura 1.6: CS após a execução da 5ª instrução. Fonte: Elaborado pelo autor.

Com reconhecimento da importância de cada parte do CS, a Figura 1.7 ilustra a


correspondência de seus itens em relação aos componentes de um computador real.

Figura 1.7: Correspondência entre o CS e um computador. Fonte: Elaborado pelo autor.

O mapeamento é dado assim: (a) Megan é o processador, pois é o componente


que executa as instruções do algoritmo; (b) os papéis descartáveis são dados fornecidos
como entrada, geralmente inseridos pelo usuário por meio de um teclado, mouse ou tela
sensível ao toque; (c) a tela é a saída, que corresponde a um monitor ou impressora, por
exemplo; (d) a lousa simboliza a memória principal, conhecida como Memória RAM,
que serve para guardar dados temporariamente, pois ao cortar o fluxo de energia do

Núcleo de Educação a Distância | Faculdade Impacta


Linguagem de Programação

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

BROOKSHEAR, J. G. Ciência da Computação: uma abordagem abrangente. 7 ed.


Porto Alegre: Bookman, 2008.
CORMEN, T. H. et al. Algoritmos: teoria e prática. 3 ed. Rio de Janeiro: Elsevier,
2012.
DIERBACH, C. Introduction to Computer Science Using Python: A Computational
Problem-Solving Focus. 1 ed. New York: Wiley, 2012.
KNUTH, D. E. The art of computer programming. 3 ed. Massachusetts:
Addison-Wesley, 1997. ISBN: 978-0-201-89683-1.
LIRA, L. N. Instrumentos de apoio ao ensino e aprendizagem de algoritmos e
programação de computadores: implicações no desempenho discente em
instituição de educação profissional. 197f. Dissertação (Mestrado Profissional em
Gestão e Desenvolvimento da Educação Profissional). Centro Estadual de Educação
Tecnológica Paula Souza, São Paulo, 2019.
PEREIRA, S. L. Algoritmos e lógica de programação em C: uma abordagem
didática. 1 ed. São Paulo: Érica, 2010. ISBN: 978-85-365-0327-1.
ZIVIANI, N. Projeto de algoritmos com implementações em Pascal e C. 4 ed. São
Paulo: Pioneira, 1999.

Núcleo de Educação a Distância | Faculdade Impacta


Proceedings of the XII SIBGRAPI (October 1999) 101-104
Linguagem de Programação

Texto base

2
Ambiente de desenvolvimento, tipos de dados,
operadores aritméticos e variáveis

Prof. Me. Lucio Nunes de Lira


Prof. MSc. Rafael Maximo Carreira Ribeiro

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

Núcleo de Educação a Distância | Faculdade Impacta


Linguagem de Programação

2.2. Preparação do ambiente computacional


Usaremos um computador com sistema operacional Microsoft Windows 10™,
mouse, teclado e conexão com a internet, pois este é o sistema operacional mais
amplamente utilizado. Mas isso não é um requisito obrigatório e é possível acompanhar
as aulas a partir de outros sistemas operacionais, como Linux ou Apple macOS™, por
exemplo. Nos dois sistemas mencionados, o Python está pré-instalado por padrão, mas a
versão pode variar de acordo com o sistema ou distribuição, sendo comum que haja
mais de uma versão do Python instalada, pois alguns programas desses sistemas usam a
versão obsoleta 2.7. Portanto, é importante verificar qual a versão de seu computador e,
se necessário, realizar uma atualização. Este curso não é compatível com nenhuma
versão de Python 2. Geralmente recomenda-se a instalação da versão estável mais
recente do Python, porém, para esta disciplina, é suficiente qualquer uma a partir da 3.6.

2.2.1. Download e instalação do Python no Windows


Iniciaremos com o download do interpretador do Python juntamente com o
IDLE, ambos são softwares gratuitos e de código aberto e serão instalados por meio do
mesmo arquivo. Para isso basta acessar o site https://www.python.org, clicar na guia
“Downloads” e, em seguida, no botão “Python 3.9.1” (atualmente a última versão
estável disponível), como ilustrado na Figura 2.1.

Figura 2.1: Site para download do Python. Fonte: Elaborado pelo autor.

Execute o arquivo baixado, preferencialmente como administrador, configure a


primeira tela conforme ilustrado na Figura 2.2 e clique em “Customize installation”.

Núcleo de Educação a Distância | Faculdade Impacta


Linguagem de Programação

Figura 2.2: 1ª tela de instalação do Python. Fonte: Elaborado pelo autor.

Configure a segunda e a terceira tela de instalação conforme indicado pela


Figura 2.3 e clique em “Next” e, em seguida, em “Install”. Aguarde a conclusão da
instalação e, na última tela, clique em “Close”.

Figura 2.3: Recortes da 2ª e 3ª telas de instalação. 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.

Núcleo de Educação a Distância | Faculdade Impacta


Linguagem de Programação

Figura 2.4: Acesso ao IDLE Python. Fonte: Elaborado pelo autor.

O IDLE possui dois módulos, o interpretador interativo e o editor de


código-fonte, analisaremos as funcionalidades de cada um deles.

2.2.3. Interpretador interativo


Por padrão, ao clicar no atalho do IDLE no Menu Iniciar a tela exibida será do
interpretador interativo, também conhecido como Shell, como ilustrado na Figura 2.5.

Figura 2.5: Interpretador interativo do IDLE Python (Shell). Fonte: Elaborado pelo autor.

Este módulo é usado principalmente para testar instruções de Python e executar


programas construídos no editor. Repare na sequência de três sinais de maior (>>>)
indicando que a Shell está pronta para receber novas instruções. A cada instrução, o
programador deve teclar [ENTER] para executá-la e gerar o efeito correspondente. Na
Figura 2.6 há um exemplo de uma instrução clássica em programação, sendo uma
tradição que iniciantes em uma linguagem comecem seus estudos com a digitação dela.

Núcleo de Educação a Distância | Faculdade Impacta


Linguagem de Programação

Figura 2.6: Execução de uma instrução inserida na Shell. Fonte: Elaborado pelo autor.

A Shell é bastante útil e possui atalhos e recursos interessantes, podendo,


inclusive, substituir uma calculadora tradicional, veremos posteriormente como fazer
isso. Alguns dos atalhos que facilitam a codificação são:
● Para regressar a última instruções inserida, posicione o cursor na linha com
>>> e pressione as teclas [ALT]+[P];
● Para avançar para a próxima instrução inserida, posicione o cursor na linha
com >>> e pressione as teclas [ALT]+[N];
● Para interromper a execução de uma instrução ou programa pressione as
teclas [CTRL]+[C].

🏹 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.

2.2.4. Editor de código-fonte


A Shell é muito útil para testar trechos de código, mas existem situações em que
precisaremos construir um programa completo e guardar seu código-fonte para que
possa ser editado e executado diversas vezes sem a necessidade de reescrevê-lo após
encerrar o IDLE. Para isso existe o módulo editor de código-fonte. Para acessá-lo,
clique no menu “File > New File” da janela da Shell. Veja a ilustração na Figura 2.7.

Núcleo de Educação a Distância | Faculdade Impacta


Linguagem de Programação

Figura 2.7: Menu para acesso ao editor de código-fonte. Fonte: Elaborado pelo autor.

Será aberta uma janela semelhante à do Bloco de Notas do Microsoft Windows


em que, ao contrário da Shell, não há os três sinais de maior (>>>), como pode ser visto
na Figura 2.8. É nesta janela que o programador traduzirá seu algoritmo em instruções
para Python, gerando assim um código-fonte.

Figura 2.8: Módulo 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.

Cada caractere é importante, portanto tome cuidado para não esquecer de


nenhum ou trocá-lo por acidente. Caso tenha dúvida se em Python letras maiúsculas e
minúsculas são interpretadas como caracteres diferentes, a resposta é SIM! Python é
uma linguagem case-sensitive, isso quer dizer que n1 é completamente diferente de N1.

Núcleo de Educação a Distância | Faculdade Impacta


Linguagem de Programação

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.

Após o salvamento do arquivo, a barra de título será alterada para corresponder


ao nome do arquivo e local em que foi salvo. Para executar o código-fonte e visualizar o
programa funcionando, vá até o menu “Run > Run Module”. O procedimento de
execução e o resultado, supondo as entradas 10 e 7, estão ilustrados na Figura 2.11.
Note que as saídas do programa estão em azul e as entradas do usuário em preto.

Figura 2.11: Execução do código-fonte e programa gerado. Fonte: Elaborado pelo autor.

Observe que a execução do programa, mesmo que solicitada no editor, é feita na


Shell. Caso queira fazer alguma alteração no código, basta selecionar novamente a
janela do editor, que provavelmente foi sobreposta pela da Shell, fazer as modificações e
executá-lo novamente, o código-fonte será automaticamente salvo.

👁️‍🗨️
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

Núcleo de Educação a Distância | Faculdade Impacta


Linguagem de Programação

▪️ 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. Conceitos básicos de Python


Agora que temos conhecimento sobre o ambiente de programação que usaremos
durante a disciplina, vamos aprender alguns conceitos básicos da linguagem Python.

2.3.1. Constantes ou Literais


Constantes são símbolos que representam valores e não podem ser alterados, são
também chamadas de literais e geralmente são utilizadas em expressões. Exemplos de
constantes em Python: 9, -541, 3.1415, 'estou aprendendo Python!', "Olá
Megan", False e True.
O Python possui alguns tipos de constantes, e cada tipo deve ser usado de acordo
com o contexto do problema. Os quatro tipos que usaremos de início são:
● Números inteiros (int): valores numéricos que não possuem ponto decimal.
Exemplos: 13, 123456789, -65, 0, 0b1011(base 2), 0o7(base 8), 0xF(base 16).
● Números reais (float): valores numéricos com ponto decimal ou escritos em
notação científica. Note que é utilizado um ponto, não uma vírgula.
Exemplos: 2.7345, .25, -65.0, 0.0, 6.02e-23, 6.02E-23, 2e1.
● Valores booleanos (bool): também conhecidos como valores lógicos, existem
apenas dois valores, um corresponde a falso e o outro a verdadeiro.
Exemplos: False e True. Note que somente a primeira letra é maiúscula e
não há aspas como nas strings a seguir.
● Textos (string): são cadeias de caracteres delimitadas por apóstrofos ou
aspas. São usadas para representar textos (letras, palavras ou frases).
Exemplos: 'Olá!', '123', "Python é 10", "True", "3+5", 'X'.

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.

Núcleo de Educação a Distância | Faculdade Impacta


Linguagem de Programação

2.3.2.1. Operadores aritméticos


Os operadores aritméticos, quando aplicados em operandos numéricos resultam
valores numéricos, assim como na matemática. Observe esses operadores na Tabela 2.1.
Tabela 2.1: Operadores aritméticos do Python.

Operador Descrição Exemplos

+ 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

+ Mantém o sinal do operando à direita. + 3 → 3


(unário) Observação: é a função identidade. + (-3) → -3

- - (3) → -3
Inverte o sinal do operando à direita.
(unário) - (-7) → 7

* Multiplica o primeiro operando pelo segundo. 3 * 8 → 24

Quociente da divisão real do primeiro operando 9 / 2 → 4.5


/
pelo segundo. 9.0 / 2 → 4.5

Quociente da divisão inteira do primeiro 9 // 2 → 4


//
operando pelo segundo. 9.0 // 2 → 4.0

Resto da divisão inteira do primeiro operando 9 % 2 → 1


%
pelo segundo. 9.0 % 2 → 1.0

Exponenciação, eleva o primeiro operando ao


** 3 ** 2 → 9
segundo.

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

Núcleo de Educação a Distância | Faculdade Impacta


Linguagem de Programação

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.

Um destaque importante da linguagem Python é que o tipo do dado está


relacionado ao valor atribuído e não a variável que recebeu esse valor, diferentemente de
outras linguagens de programação como C, C++ e Java, dentre outras. Note também que
uma mesma variável pode armazenar, inclusive, valores de tipos distintos.

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 &, ¨, %, $, #, @, !);

Núcleo de Educação a Distância | Faculdade Impacta


Linguagem de Programação

● Não pode iniciar com dígito;


● Não pode ser uma palavra reservada (abordaremos a seguir).
Recomenda-se criar identificadores concisos, porém descritivos:
● idade é melhor que i;
● tamanho_nome é melhor que tamanho_do_nome_da_pessoa.
Evite abreviar exageradamente, escreva por extenso para melhorar a legibilidade:
● sobrenome é melhor que sbrnome;
● litros é melhor que ltrs;
● data_criacao é melhor que dt_cri.
Existe um guia de estilo para programação em Python, criado e mantido pela
própria comunidade mundial da linguagem, com o intuito de fornecer recomendações
para facilitar a leitura do código. Esse guia é conhecido como PEP82 e embasou a forma
como foram escritos os códigos deste material.
No decorrer da disciplina, apresentaremos as partes relevantes do guia para que a
sua utilização ocorra de maneira natural, não sendo necessário decorá-lo integralmente.
Por enquanto, temos mais uma recomendação relativa à criação de identificadores:
● Use apenas letras minúsculas e sem acentuação, separando as palavras com
um sublinhado para melhorar a legibilidade.
Vale relembrar que o Python é uma linguagem case-sensitive, diferenciando
letras maiúsculas de minúsculas, portanto o identificador meu_nome não é o mesmo que
Meu_Nome ou MEU_NOME.

2.3.5. Palavras reservadas


Python possui palavras reservadas, as palavras-chave, chamadas em inglês de
keywords, e não podem ser usadas como identificadores, pois têm papel especial na
linguagem. O Python 3.9.1 possui 36 keywords, porém a quantidade varia entre as
versões, para saber quais são as de sua versão execute na Shell a Codificação 2.3.
>>> help('keywords')
Codificação 2.3: Instrução que exibe as keywords da versão usada do Python.

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

Núcleo de Educação a Distância | Faculdade Impacta


Linguagem de Programação

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.

2.4. Python Tutor


Há um ambiente gratuito que pode ajudar a entender o que ocorre na memória e
na saída de dados após a execução de cada instrução do código-fonte, é o Python Tutor.
Esse sistema on-line, gera representações gráficas relacionadas ao código-fonte e são
dinamicamente atualizadas a cada instrução executada.
O Python Tutor pode ser acessado no endereço http://pythontutor.com/. Para
começar a usá-lo, clique em “Start visualizing your code now”. Insira seu código-fonte e
clique no botão “Visualize Execution”, conforme a Figura 2.13. Note que você pode
tanto copiar e colar o código quanto digitar diretamente na caixa de texto.

Figura 2.13: Python Tutor antes da execução do código-fonte.


Fonte: Elaborado pelo autor.

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.

Núcleo de Educação a Distância | Faculdade Impacta


Linguagem de Programação

Figura 2.14: Python Tutor após a execução de algumas instruções do código-fonte.


Fonte: Elaborado pelo autor.

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.

Núcleo de Educação a Distância | Faculdade Impacta


Proceedings of the XII SIBGRAPI (October 1999) 101-104
Linguagem de Programação

Texto base

3
Expressões, operadores de atribuição, funções
integradas e entrada/saída de dados

Prof. Me. Lucio Nunes de Lira


Prof. MSc. Rafael Maximo Carreira Ribeiro

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

Núcleo de Educação a Distância | Faculdade Impacta


Linguagem de Programação

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.

Quando uma instrução contém uma expressão, Python avaliará a expressão, ou


seja, encontrará o valor resultante, caso seja válida. Em expressões inválidas, ocorrerão
erros que podem variar de acordo com a razão de estarem inválidas.

3.3. Operadores de atribuição


Conhecemos o operador de atribuição simples de Python (=), e entendemos que
seu papel é atribuir valores de expressões às variáveis, como na Codificação 3.2.
>>> x = 10 + 5 # atribui o valor 15 à variável x
Codificação 3.2: Exemplo de atribuição de valor à variável.

É importante compreender que antes da atribuição, a expressão à direita de =


será avaliada. Logo, a variável x não receberá a expressão 10 + 5, mas sim o valor
resultante da avaliação de 10 + 5, que é 15. O mesmo ocorre em qualquer atribuição.
Em Python, há outros operadores de atribuição, chamados de operadores de
atribuição composta, ou atribuição aumentada, usados para tornar algumas instruções
mais concisas e, dependendo da maturidade do programador, mais legíveis. Veja parte
desses operadores na Tabela 3.1. Também é possível substituir instruções com atribuição
composta por atribuição simples combinada com outro operador.
Podemos dizer que estes operadores são um atalho de sintaxe para quando
precisamos acessar o valor de uma variável, realizar uma operação com ele, e atribuir o
resultado da operação à mesma variável. Teste a Codificação 3.3 e verifique se
compreendeu a razão dos valores atribuídos a variável m a cada nova instrução,

Núcleo de Educação a Distância | Faculdade Impacta


Linguagem de Programação

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.

Operador Descrição Exemplos


= Atribui o resultado da expressão à variável. x = expressão
x += expressão
+= Atribui o resultado da adição à variável.
x = x + (expressão)
x -= expressão
-= Atribui o resultado da subtração à variável.
x = x - (expressão)
Atribui o resultado da multiplicação à x *= expressão
*=
variável. x = x * (expressão)
Atribui o quociente da divisão real à x /= expressão
/=
variável. x = x / (expressão)
Atribui o quociente da divisão inteira à x //= expressão
//=
variável. x = x // (expressão)
x %= expressão
%= Atribui o resto da divisão inteira à variável.
x = x % (expressão)
Atribui o resultado da exponenciação à x **= expressão
**=
variável. x = x ** (expressão)

>>> 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.

👁️‍🗨️ VOCÊ SABIA?


Esses atalhos de sintaxe são comumente chamados de açúcar sintático, do inglês
syntactic sugar, pois são formas de expressar algo em uma linguagem de programação
de modo mais simples, claro ou conciso, mesmo que já exista outra forma equivalente.
O intuito é facilitar a leitura e escrita de código. Portanto, são recursos que poderiam ser
removidos e mesmo assim a linguagem não perderia nenhuma funcionalidade, embora
poderia perder alguma conveniência.

Núcleo de Educação a Distância | Faculdade Impacta


Linguagem de Programação

Teste a Codificação 3.4 na Shell e verifique se compreendeu a razão dos valores


atribuídos às variáveis.
>>> a = 10 # a recebe 10
>>> b = 10 # b recebe 10
>>> c = 10 # c recebe 10
>>> a *= 2 + 3 # a recebe 50
>>> b = b * 2 + 3 # b recebe 23
>>> c = c * (2 + 3) # c recebe 50
Codificação 3.4: Atribuição composta e atribuição simples equivalente.

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.

3.4. Precedência e associatividade de operadores


Em Python, de modo semelhante à matemática, cada operador possui uma
precedência, que é uma indicação de sua prioridade em relação aos outros operadores
que estejam na mesma expressão. Portanto, partes de uma expressão com operadores de

Núcleo de Educação a Distância | Faculdade Impacta


Linguagem de Programação

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;

Núcleo de Educação a Distância | Faculdade Impacta


Linguagem de Programação

(II) porque representam algo “especial”, uma interpretação diferenciada na


linguagem.
Cada linguagem de programação possui suas próprias regras de precedência e
associatividade de operadores, por isso é recomendado consultar o manual da linguagem
para assegurar que as expressões coincidam com o desejado. A Tabela 3.2 possui uma
relação simplificada dessas regras em relação aos operadores vistos até agora.
Operadores na mesma linha possuem a mesma precedência e associatividade. A ordem
de precedência está decrescente, logo o primeiro operador tem maior precedência e o
último tem menor precedência.
Tabela 3.2: Precedência e associatividade dos operadores vistos até agora.

Operador Descrição Associatividade

** Exponenciação À direita

+, - (unários) Identidade e negação (inversão de sinal)

Multiplicação, divisão real, divisão inteira


*, /, //, % À esquerda
e resto da divisão

+, - (binários) Adição e subtração

=, +=, -=, *=, /=,


Atribuição (simples e composta) Não associativos
//=, %=, **=

3.5. Avaliação de expressões


De posse do conhecimento sobre precedência e associatividade de operadores e
dos demais tópicos estudados até o momento, temos condições de compreender o
procedimento de avaliação de expressões executado pelo Python. Para isso,
analisaremos algumas expressões, avaliando-as de modo semelhante ao feito em Python.
>>> a = 5
>>> b = 4
>>> c = 9
>>> d = 7
>>> e = 1
>>> f = 2
>>> s = 10
>>> s += a + b ** (c - d) / e * f
>>> s # qual o valor de s?
Codificação 3.6: 1º expressão que será analisada e avaliada como em Python.

Núcleo de Educação a Distância | Faculdade Impacta


Linguagem de Programação

1) O par de parênteses prioriza a expressão interna, dando a ela maior precedência,


porém, como a operação contém variáveis, primeiro devemos substituí-las por
seus respectivos valores. Após ser avaliado, o par de parênteses é descartado:
s += a + b ** (9 - 7) / e * f ⇨ s += a + b ** 2 / e * f
2) Precedência: s += a + 4 ** 2 / e * f ⇨ s += a + 16 / e * f
3) Precedência e associativ.: s += a + 16 / 1 * f ⇨ s += a + 16.0 * f
4) Precedência: s += a + 16.0 * 2 ⇨ s += a + 32.0
5) Precedência: s += 5 + 32.0 ⇨ s += 37.0
6) Único operador: s += 37.0
⇨ Atribuição composta, pode ser reescrita como: s = s + (37.0)
⇨ s = 10 + (37.0) ⇨ s = 10 + 37.0 ⇨ s = 47.0

>>> 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

Núcleo de Educação a Distância | Faculdade Impacta


Linguagem de Programação

>>> w = (5*8) + ((9//7) % 2)


>>> w # qual o valor de w?
Codificação 3.10: 5º expressão que será analisada e avaliada como em Python.

1) Parênteses: w = (5*8) + ((9//7) % 2) ⇨ w = 40 + ((9//7) % 2)


2) Parênteses: w = 40 + ((9//7) % 2) ⇨ w = 40 + (1 % 2)
3) Parênteses: w = 40 + (1 % 2) ⇨ w = 40 + 1
4) Precedência: w = 40 + 1 ⇨ w = 41
5) Único operador: w = 41
Independente da precedência, a leitura de um código-fonte é feita de cima para
baixo, ou seja, da primeira para a última linha de instrução, e cada instrução é lida da
esquerda para a direita. Consequentemente, existindo dois pares de parênteses
independentes, o mais a esquerda é avaliado primeiro, como visto na Codificação 3.10.

>>> a = b = c = d = 10 # quais os valores das variáveis?


Codificação 3.11: 6º expressão que será analisada e avaliada como em Python.

Os operadores de atribuição são não associativos em Python, portanto o


encadeamento deles pode gerar uma interpretação especial ou um erro. No caso do
operador de atribuição simples (=), há uma interpretação especial, atribuindo o valor
mais à direita a todas as variáveis à sua esquerda. Recomenda-se cautela em seu uso,
pois pode levar a comportamentos inesperados.

>>> a = b = c = (d = 10) # quais os valores das variáveis?


Codificação 3.12: 7º expressão que será analisada e avaliada como em Python.

A Codificação 3.12 é inválida, pois em Python a atribuição não resulta em valor,


logo não há o que atribuir à c. Note que o = entre parênteses está fora do encadeamento.

>>> 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.

A Codificação 3.13 é inválida, pois operadores de atribuição composta são não


associativos e quando encadeados geram erro.

Núcleo de Educação a Distância | Faculdade Impacta


Linguagem de Programação

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.

Por enquanto focaremos em funções integradas, em próximas aulas criaremos


nossas próprias funções que até poderão ser distribuídas para outros programadores!

3.6.1. Funções integradas


Uma das vantagens em usar funções integradas, e também das importadas, é a
abstração do seu algoritmo, isto é, não precisamos saber detalhes de como foram
construídas. O essencial é saber os nomes das funções e o que elas fazem.
Para usar funções integradas, também conhecidas como built-in functions, é
necessário chamá-las. Para chamar funções, escreve-se seu nome seguido por um par de
parênteses. Ao chamar a função solicitamos sua execução, como na Codificação 3.14.
>>> print() # chama a função print.
Codificação 3.14: Chamada à função print.

Além de seus nomes e parênteses, a maioria das funções exige um terceiro


componente: argumentos. Argumentos são valores inseridos entre os parênteses da
chamada e servem para passar dados à função. Os dados passados são necessários para
que a função cumpra seu objetivo. Por exemplo, a função abs recebe como argumento
um número e retorna o valor absoluto correspondente, conforme a Codificação 3.15.
>>> abs(-29) # chama a função abs com o argumento -29.
29 # valor retornado pela função abs.
Codificação 3.15: Chamada à função abs com o argumento -29.

O valor retornado ou valor devolvido é, como a própria expressão indica, um


valor que será dado como retorno pela função, ou seja, a resposta obtida ao chamar a

Núcleo de Educação a Distância | Faculdade Impacta


Linguagem de Programação

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.

Quem determina quais os argumentos e a ordem correta de passá-los à função é


seu criador, que pode disponibilizar documentação, geralmente na internet. Também
podemos usar o menu Help do IDLE, caso seja uma das funções que acompanham a
instalação. A própria Shell e o editor podem ajudar, basta digitar o nome da função com
a abertura de parênteses e aguardar alguns instantes, conforme Figura 3.1.

Núcleo de Educação a Distância | Faculdade Impacta


Linguagem de Programação

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

3.7. Saída de dados


Você deve ter notado a função embutida print em algumas codificações, apesar
de intuitiva por conta do efeito evidente em sua execução, aguardávamos a explicação
inicial sobre funções para melhor compreensão. Esta função serve para exibir dados e
será bastante usada durante a disciplina. Veja exemplos na Codificação 3.19.
>>> print('Hello World!') # print com um argumento.
Hello World!
>>> print('2 + 2 =', 2+2) # print com dois argumentos.
2 + 2 = 4
>>> idade = 29
>>> print('Tenho', idade, 'anos') # print com três argumentos.
Tenho 29 anos
Codificação 3.19: Exemplos de uso da função print.

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úcleo de Educação a Distância | Faculdade Impacta


Linguagem de Programação

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.

A função print tem uma característica interessante, pois possui quantidade


indeterminada de argumentos, por isso pode ser chamada com zero ou mais argumentos,
e o tipo dos argumentos também é arbitrário, podem ser números, strings etc. O Python
se encarregará das conversões necessárias para que tudo seja exibido corretamente.
Por padrão, print exibe os valores de seus argumentos e uma quebra de linha
'\n', fazendo com que o próximo print exiba seus dados na linha seguinte. Às vezes
esse comportamento é indesejado, como na Codificação 3.22.
print('boa ')
print('noite ')
print('vizinhança')
Codificação 3.22: Programa em que as palavras deveriam estar na mesma linha.

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.

3.8. Entrada de dados


Para permitir que o usuário do programa forneça dados de entrada, usaremos a
função embutida input que, quando executada, faz com que: (I) o programa aguarde
um valor de entrada antes de prosseguir com as próximas instruções; (II) converte o
valor lido para string e; (III) retorna-o como resposta. Há exemplo na Codificação 3.24.

Núcleo de Educação a Distância | Faculdade Impacta


Linguagem de Programação

>>> nome = input('Qual o seu nome? ')


Qual o seu nome? Megan
>>> print(nome)
Megan
Codificação 3.24: Exemplo de utilização da função input.

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.

Ocorreu um erro, certo? A razão é que as variáveis valor e parcelas


receberam strings, consequência do valor retornado por input, e na quarta linha há
uma multiplicação entre essas variáveis, ou seja, entre strings, o que é uma expressão
inválida. Para resolver esse problema, as entradas deverão ser convertidas para o
formato correto, usando as funções integradas int e float.
As funções int e float recebem como argumento um número, ou uma string
que represente adequadamente um número do tipo para qual se deseja converter, e
devolvem como resposta o valor correspondente convertido para um número inteiro ou
em ponto flutuante (real), respectivamente. Veja a correção da Codificação 3.25, agora
com as devidas conversões, na Codificação 3.26.
nome = input('Seu nome: ')
valor = float(input('Valor do empréstimo: '))
parcelas = int(input('Quantidade de parcelas: '))
print(nome, ', a dívida será de:', valor * parcelas)
Codificação 3.26: Programa com 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).

Núcleo de Educação a Distância | Faculdade Impacta


Linguagem de Programação

1) Inicialmente input será executada, pois é a função mais interna:


valor = float(input('Valor do empréstimo: '))
2) Supondo que o usuário dê a entrada 1000.00, a função input converterá
esse valor para string e a retornará como resposta:
valor = float('1000.00')
3) Agora a função float converterá seu argumento, que neste caso é uma
string, para o correspondente número real e retornará esse valor.
valor = float('1000.00') ⇨ valor = 1000.0
4) Por fim, o valor 1000.0, de fato um número real representado em ponto
flutuante, será atribuído à variável.
valor = 1000.0

3.9. Tipos de erros comuns


Observamos que erros acontecem! E teremos que lidar com eles, afinal, faz parte
da rotina de um programador. Para generalizá-los, classificaremos os erros em três tipos:
1) Erros de sintaxe: ocorrem por falha na escrita das instruções da linguagem,
violando regras e estruturas. Por exemplo, a instrução print 'olá', sem
parênteses, é inválida em Python 3, mas é válida em Python 2. Esquecer de
completar toda abertura de parênteses com um parêntese de fechamento ou
usar um operador binário com apenas um operando são outros exemplos.
Geralmente são erros de simples resolução, pois o ambiente de programação
costuma indicar instruções com erros de sintaxe ao executar o código;
2) Erros em tempo de execução: são erros que só aparecem quando o programa
é executado, pois a sintaxe está correta. Também podem ser chamados de
exceções, indicando que algo excepcional ocorreu, como uma violação do
uso esperado do programa. Podem ocorrer, por exemplo, quando o usuário
insere um valor inválido ou quando um recurso necessário para a execução
do programa está ausente. O exemplo a seguir gera esse tipo de erro:
n = int(input('Número inteiro: ')) # Usuário digita ABC
Caso o usuário digite o que consta no comentário, uma exceção será lançada,
indicando que não é possível converter o valor 'ABC' para inteiro.
3) Erros de lógica/semântica: são erros relacionados ao algoritmo. Nesses
casos, o código-fonte pode ser executado e o programa não gerará nenhuma
mensagem de erro, mas o resultado não resolve o problema proposto. Algo
como “escrever certo a coisa errada”, afinal o algoritmo não soluciona o
problema. Estes costumam ser os erros mais difíceis de serem resolvidos.

Núcleo de Educação a Distância | Faculdade Impacta


Linguagem de Programação

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

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.
STURTZ, J. Basic Input, Output, and String Formatting in Python. Real Python, 2019.
Disponível em: <https://realpython.com/python-input-output/>. Acesso em: 21 jan.
2021.

Núcleo de Educação a Distância | Faculdade Impacta


Proceedings of the XII SIBGRAPI (October 1999) 101-104
Linguagem de Programação

Texto base

4
Operadores e expressões relacionais e lógicas,
estruturas de seleção simples e composta

Prof. Me. Lucio Nunes de Lira


Prof. MSc. Rafael Maximo Carreira Ribeiro

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.

Núcleo de Educação a Distância | Faculdade Impacta


Linguagem de Programação

4.2. Estruturas de controle


A ordem na qual as instruções de um programa são executadas pode ser
chamada de fluxo de execução, sendo assim, uma estrutura de controle do fluxo de
execução é uma forma de controlar em qual ordem as instruções serão executadas.
Fundamentalmente existem três tipos de estruturas para controlar o fluxo de
execução de um programa, cuja representação pode ser vista na Figura 4.1. São elas:
I. Estrutura sequencial;
II. Estrutura de seleção ou condicional; e
III. Estrutura de repetição ou iterativa, também chamada de laço.

Figura 4.1: Estruturas de controle de fluxo básicas. Fonte: Elaborado pelo autor.

Por enquanto, os problemas que lidamos requisitaram apenas soluções com


estrutura de controle sequencial, em que a ordem que as instruções são executadas é
idêntica a ordem que foram escritas. Agora, aprenderemos o segundo tipo de estrutura
de controle, a estrutura de seleção ou estrutura condicional, na qual a execução de uma
sequência de instruções ficará condicionada ao resultado da avaliação de uma expressão.
Com essa estrutura de controle, poderemos criar programas que decidam, em
tempo de execução, quais instruções serão executadas com base em uma expressão que
resulte em um valor lógico (também conhecido como valor booleano), ou seja,
verdadeiro ou falso. Portanto, antes de tratarmos da estrutura de seleção em si, veremos
como criar e avaliar expressões que resultam valores lógicos.

4.3. Expressões relacionais e lógicas


Anteriormente lidamos com expressões aritméticas, nas quais existem apenas
operadores aritméticos e operandos numéricos, quando essas expressões são avaliadas o
resultado é um número. Agora estudaremos expressões relacionais e lógicas, em que a
avaliação resultará em um valor verdadeiro ou falso, representados em Python pelas
constantes True e False, respectivamente.

Núcleo de Educação a Distância | Faculdade Impacta


Linguagem de Programação

Expressões relacionais são aquelas em que dois ou mais operandos são


comparados para verificar se uma determinada relação entre eles é verdadeira ou falsa.
A relação é indicada por meio de operadores relacionais (maior, menor, diferente, etc.) e
o resultado é um valor booleano. Um exemplo pode ser visto na Codificação 4.1.
>>> idade = 23
>>> pode_tirar_cnh = idade >= 18 # Resulta True
Codificação 4.1: Exemplo de expressão relacional.

Na Codificação 4.1, idade é comparada com o inteiro 18, o resultado da


comparação será True se o operando à esquerda for maior ou igual (>=) ao da direta, ou
resultará False caso contrário. Note que este exemplo poderia ser aplicado à condição
básica para um brasileiro, em condições normais, tentar obter sua carteira de motorista.
Já expressões lógicas são aquelas em que os operandos são valores booleanos e
usamos os operadores lógicos (E, OU e NÃO), análogos aos da lógica matemática, para
obter um resultado também booleano, indicando se a expressão como um todo é
verdadeira ou falsa. Veja na Codificação 4.2 uma continuação da Codificação 4.1.
>>> aprovado_detran = True
>>> pode_dirigir = pode_tirar_cnh and aprovado_detran
Codificação 4.2: Exemplo de expressão lógica.

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

4.3.1. Operadores relacionais


Os operadores relacionais em Python são mostrados na Tabela 4.1 e,
evidentemente, são todos binários, pois é necessário relacionar um operando a outro.
Esses operadores relacionais também estão presentes na matemática, mas vale
lembrar que a interpretação em Python difere daquelas em equações e inequações

Núcleo de Educação a Distância | Faculdade Impacta


Linguagem de Programação

matemáticas. Na matemática tais sinais são uma afirmação sobre a (in)equação, se


dizemos que 2𝑥 + 2 = 𝑥 + 5, sabemos que ambos os lados da equação devem ter o
mesmo resultado e podemos então calcular o valor de 𝑥.

Tabela 4.1: Operadores relacionais em Python.

Operador Descrição Exemplos


5 == 3 # False
== igual a
8 == 8 # True
5 != 3 # True
!= diferente de
8 != 8 # False
5 > 3 # True
> maior que
8 > 8 # False
5 >= 3 # True
>= maior ou igual a
8 >= 8 # True
5 < 3 # False
< menor que
8 < 8 # False
5 <= 3 # False
<= menor ou igual a
8 <= 8 # True

Sabemos que em Python o sinal de igual simboliza o operador de atribuição


simples (=), indicando que a variável à esquerda do operador receberá o valor resultante
da avaliação da expressão à direita do operador, consequentemente a equação mostrada
no parágrafo anterior é inválida em Python.
Já com os operadores relacionais, é como se fizéssemos uma pergunta em
relação a ambos os operandos, como: “eles são iguais?”, “são diferentes?”, “o da
esquerda é maior que o da direita?”, “o da esquerda é menor ou igual ao da direita?” e
assim sucessivamente, obtendo sempre como resposta um valor verdadeiro ou falso,
que, como já mencionado, são representados em Python por True e False.
Em Python todos os operadores relacionais possuem a mesma precedência entre
si e são não associativos, porém possuem uma interpretação especial quando
encadeados, algo que veremos adiante.

4.3.2. Operadores lógicos


Os operadores lógicos permitem a combinação de comparações ou valores
booleanos, possibilitando a construção de expressões mais complexas. Consideraremos
três operadores lógicos:

Núcleo de Educação a Distância | Faculdade Impacta


Linguagem de Programação

I.NÃO: operador unário que inverte o valor do operando associado, ou seja,


é a negação do operando. Negar True resulta em False e negar False
resulta em True;
II. E: operador binário que resulta True apenas se ambos os operandos forem
True, e False caso contrário. Portanto, basta um operando False para
que o resultado da expressão resulte em False;
III. OU: operador binário que resulta True se pelo menos um dos operandos
for True, e False caso contrário. Portanto, resulta em False apenas se
ambos operandos forem False.
Podemos resumir o funcionamento dos operadores lógicos em uma tabela
verdade para cada operador, como mostrado na Tabela 4.2.
Tabela 4.2: Tabela verdade dos operadores lógicos.

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

A Tabela 4.3 contém as palavras reservadas em Python correspondentes a cada


operador lógico mostrado até agora.
Tabela 4.3: Operadores lógicos em Python.

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

2) Avalie mentalmente as expressões a seguir e confira o resultado digitando-as na Shell:

Núcleo de Educação a Distância | Faculdade Impacta


Linguagem de Programação

>>> 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

Talvez tenha sentido dificuldade em avaliar algumas expressões do exercício 2,


principalmente por não ter certeza sobre qual parte seria avaliada primeiro. Para resolver
isso precisamos saber as regras de precedência e associatividade desses operadores.

4.3.3. Precedência e associatividade


A Tabela 4.4 traz os operadores relacionais e lógicos em ordem decrescente de
prioridade, juntamente com suas associatividades.

Tabela 4.4: Precedência e associatividade dos operadores relacionais e lógicos.

Operador Descrição Associatividade

==, !=, >, >=, <, <= Operadores relacionais. Não associativos

not Negação lógica.

and E lógico. À esquerda

or OU lógico.

Precisamos entender o que ocorre ao digitarmos expressões com operadores


relacionais encadeados, afinal são operadores não associativos. Veja a Codificação 4.3.
>>> 3 < 10 < 23 # Resulta em True
Codificação 4.3: Exemplo de expressão com encadeamento de operadores relacionais.

Se houvesse associatividade dos operadores relacionais em Python, poderíamos


ter uma entre duas possíveis interpretações:
a) Com associatividade à esquerda: 3 < 10 < 23 ⇨ True < 23 ⇨ ?
b) Com associatividade à direita: 3 < 10 < 23 ⇨ 3 < True ⇨ ?
Podemos observar que a interpretação da comparação de um valor booleano com
um valor numérico, ainda que possível em Python (teste na Shell a comparação de True
e False com valores numéricos), não faz sentido neste contexto. Outra forma de
interpretação seria “o número 10 está entre os números 3 e 23?”. Também poderíamos
interpretá-la como de costume na matemática, “3 é menor que 10 que é menor que 23?”
Muitas linguagens de programação não permitem o encadeamento de operadores
relacionais, evitando o problema mencionado. Porém Python, visando facilidade de

Núcleo de Educação a Distância | Faculdade Impacta


Linguagem de Programação

leitura e escrita de código, permite o encadeamento, aproximando o código à notação


matemática. Neste caso, o interpretador traduz a expressão como um encadeamento de
expressões binárias, repetindo1 os operandos entre os operadores relacionais e unindo-os
com and. Vejamos passo a passo como Python avalia a expressão da Codificação 4.3.
1) 3 < 10 < 23 ⇨ 3 < 10 and 10 < 23
2) 3 < 10 and 10 < 23 ⇨ True and 10 < 23
3) True and 10 < 23 ⇨ True and True
4) True and True ⇨ True
É comum que expressões relacionais e lógicas incluam também operadores
aritméticos, então é importante saber a precedência destes operadores não apenas entre
si, mas com relação aos demais tipos de operadores. A Tabela 4.5 mostra a ordem em
que os operadores são resolvidos pelo Python, de acordo com sua precedência, sendo o
operador de maior prioridade resolvido primeiro.
Tabela 4.5: Prioridade dos operadores aritméticos, relacionais e lógicos.

Ordem de
Operador Descrição Associatividade
resolução

1° ** Exponenciação. À direita

2° +, - (unários) Identidade e negação.

Multiplicação, divisão real,


3° *, /, //, % À esquerda
divisão inteira e resto da divisão.

4° +, - (binários) Adição e subtração.

==, !=,
5° Operadores relacionais. Não associativos
>, >=, <, <=

6° not Negação lógica.

7° and E lógico. À esquerda

8° or OU lógico.

Dentre os operadores vistos até o momento, a atribuição, simples ou composta,


tem a menor prioridade independentemente da instrução. Isso é necessário para que a
expressão à direita do operador de atribuição possa ser completamente avaliada e o
resultado atribuído à variável à esquerda.

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.

Núcleo de Educação a Distância | Faculdade Impacta


Linguagem de Programação

4.3.4. Expressões equivalentes e complementares


Duas expressões são equivalentes quando possuem exatamente o mesmo
significado, apesar de escritas de forma diferente. Por exemplo, na Codificação 4.1,
fizemos a comparação da idade do usuário com o número 18, verificando se a idade era
maior ou igual a 18, mas poderíamos verificar se 18 era menor ou igual à idade, e
chegaríamos ao mesmo resultado em ambas as expressões, conforme a Codificação 4.4.
>>> idade = 23
>>> idade >= 18
True
>>> 18 <= idade
True
Codificação 4.4: Exemplo de expressões equivalentes.

Duas expressões são complementares quando uma é a negação da outra, ou seja,


juntas cobrem todo o espaço possível para os valores envolvidos, sem sobreposição. Por
exemplo, a expressão idade < 18 é complementar à idade >= 18, e vice-versa,
como ilustrado na Figura 4.2.

Figura 4.2: Representação de expressões complementares na reta dos números reais.


Fonte: Elaborado pelo autor.

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.

É comum o surgimento de dúvidas ao buscar expressões complementares de


expressões que contenham, além de operadores relacionais, operadores lógicos. Veja na
Codificação 4.6 um exemplo em que pessoas acima de 80 anos não possam obter
carteira de motorista.

Núcleo de Educação a Distância | Faculdade Impacta


Linguagem de Programação

>>> pode_tirar_cnh = idade >= 18 and idade <= 80


Codificação 4.6: Expressão com operadores relacionais e lógicos.

Podemos reescrever a expressão da Codificação 4.6 com uma expressão


equivalente 18 <= idade and idade <= 80 ou também 18 <= idade <= 80,
no entanto, prosseguiremos com a versão inicial, que inclui o operador lógico “E”, cuja
representação na reta dos números reais pode ser vista na Figura 4.3.

Figura 4.3: Representação da junção de duas expressões relacionais com o operador


lógico E. Fonte: Elaborado pelo autor.

Se quisermos encontrar a expressão complementar a esta, ou seja, a expressão


que resulte True para aqueles que não podem obter carta de motorista (negação da
expressão inicial), podemos começar negando os valores booleanos na figura e a partir
dela, tentar chegar em uma expressão. Veja o resultado na Figura 4.4.

Figura 4.4: Representação da expressão complementar à expressão dada na Figura 4.3.


Fonte: Elaborado pelo autor.

Agora, podemos escrever as duas expressões relacionais que resultarão True:


1) idade < 18
2) idade > 80
Observamos que as expressões não podem ser verdadeiras ao mesmo tempo,
pois não existe nenhum número que seja simultaneamente menor que 18 e maior que
80, portanto basta que a primeira OU a segunda resulte em verdadeiro. Com isso,
chegamos de maneira intuitiva à seguinte expressão: idade < 18 or idade > 80.
Podemos concluir que, ao negar a expressão inicial, obtemos sua expressão
complementar e, mais importante, que o operador lógico “E” é substituído pelo “OU” e
os operadores relacionais “maior ou igual” e “menor ou igual” são substituídos por,
respectivamente, “menor” e “maior”. Algo que pode ser visto no seguinte resumo:

Núcleo de Educação a Distância | Faculdade Impacta


Linguagem de Programação

a) Expressão inicial: idade >= 18 and idade <= 80


b) Expressões complementares (em ordem crescente de simplificação):
1) not (idade >= 18 and idade <= 80)
2) not (idade >= 18) or not (idade <= 80)
3) idade < 18 or idade > 80
Se negarmos qualquer uma das expressões complementares, retornaremos à
expressão inicial, fazendo as substituições inversas. Neste exemplo, qualquer uma das
três expressões é considerada complementar à expressão inicial, no entanto, a última é a
mais simples de ser lida por um humano, por ser mais direta usando menos operadores,
e menos custosa de ser executada por um computador, por ter menos operações.

📚 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

4.3.5. Avaliação de curto-circuito


Algumas linguagens de programação definem operadores lógicos com a
característica de avaliação de curto-circuito, também chamada de avaliação mínima.
Operadores lógicos com avaliação de curto-circuito estabelecem que seu segundo
operando só será avaliado caso a avaliação do primeiro não seja suficiente para concluir
o valor da expressão completa.
Em Python, os operadores lógicos and e or são operadores com avaliação de
curto-circuito. Veja o comportamento de cada um deles:
a) and: o segundo operando só será avaliado caso o primeiro resulte em True,
pois em uma expressão com and, o resultado só pode ser antecipado se o
primeiro operando resultar em False, afinal neste caso não importaria o
valor do segundo operando, a expressão sempre resultaria em False;
b) or: o segundo operando só será avaliado caso o primeiro resulte em False,
pois em uma expressão com or, o resultado só pode ser antecipado se o
primeiro operando resultar em True, afinal neste caso não importaria o valor
do segundo operando, a expressão sempre resultaria em True.
Para exemplificar a consequência e utilidade da avaliação mínima, veja a
Codificação 4.7 e a Codificação 4.8, em que essa característica é essencial para o correto
funcionamento dos programas.

Núcleo de Educação a Distância | Faculdade Impacta


Linguagem de Programação

# Crie um programa que receba dois números naturais e exiba uma


# mensagem indicando se o primeiro é divisível pelo segundo.

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.

Note na Codificação 4.7 que, sem a avaliação de curto-circuito, caso o usuário


inserisse o valor zero para b, o programa geraria um erro pela tentativa de divisão por
zero. Porém, a % b == 0 só será avaliada após b != 0 resultar True, logo o erro não
ocorrerá. Como teste, inverta a ordem das sub-expressões e insira valor zero para b.

# Crie um programa que receba dois números naturais e exiba uma


# mensagem indicando se o primeiro não é divisível pelo segundo.

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.

Assim como na Codificação 4.7, na Codificação 4.8, sem a avaliação de


curto-circuito, se o usuário inserisse o valor zero para b, o programa geraria um erro.
Porém, como a % b != 0 só será avaliada após b == 0 resultar False, o erro não
ocorrerá. Novamente, inverta a ordem das sub-expressões e insira valor zero para b.

4.4. Estruturas de seleção ou estruturas condicionais


São as estruturas usadas para selecionar se um trecho de código será executado
com base na avaliação de uma expressão, dizemos que a execução do trecho de código
está condicionada ao resultado desta expressão, por isso a expressão de decisão também
pode ser chamada de condição. Inicialmente, abordaremos dois tipos de estruturas de
seleção, que também estão ilustradas na Figura 4.5:
1) Seleção simples: usada quando condicionamos um bloco de código a ser
executado apenas quando a condição resultar True. Logo, quando a
condição resultar em False o bloco condicionado será pulado/ignorado;
2) Seleção composta: usada quando há dois blocos de código condicionados,
sendo que um deles será executado apenas quando a condição resultar True
e o outro apenas quando a condição resultar False. Portanto, ao executar
um bloco, seja qual for, o outro obrigatoriamente será pulado/ignorado.

Núcleo de Educação a Distância | Faculdade Impacta


Linguagem de Programação

Figura 4.5: Fluxograma das estruturas de seleção simples e composta.


Fonte: Elaborado pelo autor.

4.4.1. Estruturas de seleção simples


As estruturas de seleção simples são escritas em Python com o uso do comando
if, uma das palavras reservadas do Python, como na Codificação 4.9.
if <condição>:
<bloco de código>
Codificação 4.9: Estrutura da seleção simples if.

A definição de um bloco de código em Python é dada por dois fatores:


1) O bloco de código é introduzido por “dois pontos”;
2) Instruções pertencentes ao bloco estão no mesmo nível de indentação entre si
e indentadas em 4 espaços2 relativamente à instrução que introduz o bloco.

A indentação é o espaçamento de uma instrução em relação à sua margem


esquerda, recomenda-se o uso de caracteres de espaços e não caracteres de tabulação.
Na maioria dos editores de código-fonte para Python, incluindo o IDLE, a configuração
padrão insere automaticamente quatro espaços ao pressionarmos a tecla [TAB].
Python identifica que o bloco de código pertencente à estrutura de seleção
terminou ao encontrar a primeira instrução, subsequente ao início do bloco, que regride
em nível de indentação, ou seja, a primeira instrução deslocada de volta para a esquerda.
Caso haja instruções com alguma indentação inesperada, incluindo um bloco vazio, isso
é marcado como erro de sintaxe, impossibilitando a execução do código até a correção.
Como exemplo de programa que utiliza uma estrutura de seleção simples, insira
no editor do IDLE a Codificação 4.10 e teste seu funcionamento.

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.

Núcleo de Educação a Distância | Faculdade Impacta


Linguagem de Programação

idade = int(input('Qual a sua idade? ')) # executado sempre

if idade >= 18:


# Bloco executado apenas quando a condição é verdadeira
print('Você pode ter uma CNH.')
print('Desejamos boa sorte!')

print('Fim') # executado sempre


Codificação 4.10: Exemplo de programa com uma estrutura de seleção simples.

4.4.2. Estruturas de seleção composta


As estruturas de seleção composta são escritas em Python de forma semelhante
às estruturas de seleção simples, porém adicionando o comando else, como um
complemento ao if, conforme a codificação 4.11.
if <condição>:
<bloco de código 1>
else:
<bloco de código 2>
Codificação 4.11: Estrutura da seleção composta if...else.

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.

Núcleo de Educação a Distância | Faculdade Impacta


Linguagem de Programação

idade = int(input('Qual a sua idade? ')) # executado sempre

if idade >= 18:


# Bloco executado quando a condição resulta verdadeiro
print('Você pode ter uma CNH.')
print('Desejamos boa sorte!')
else:
# Bloco executado quando a condição resulta falso
print('Você ainda não completou 18 anos, ', end='')
print('portanto não pode ter uma CNH.')

print('Fim') # executado sempre


Codificação 4.12: Exemplo de programa com uma estrutura de seleção composta.

# Crie um programa que receba como entrada o valor de um produto


# e a quantidade comprada. O programa concederá 10% de desconto
# para compras com total maior ou igual à R$ 100,00.

valor = float(input('Valor: '))


quantidade = int(input('Quantidade: '))
total = valor * quantidade

if total >= 100.0:


total = total * 0.9
else:
total = total

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

PROGRAMIZ. Python if...else Statement. 2020. Disponível em:


<https://www.programiz.com/python-programming/if-elif-else>. Acesso em: 14 fev. 2021.
PYTHON SOFTWARE FOUNDATION. Expressions. 2020. Disponível em:
<https://docs.python.org/3/reference/expressions.html>. Acesso em: 21 jan. 2021.

Núcleo de Educação a Distância | Faculdade Impacta


Proceedings of the XII SIBGRAPI (October 1999) 101-104
Linguagem de Programação

Texto base

5
Strings e estruturas de seleção aninhadas

Prof. Me. Lucio Nunes de Lira


Prof. MSc. Rafael Maximo Carreira Ribeiro

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

Núcleo de Educação a Distância | Faculdade Impacta


Linguagem de Programação

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.

>>> 'Bom' + ' ' + 'dia' + '!'


'Bom dia!'
Codificação 5.1: Exemplo de concatenação de strings.

É importante notar que a operação de concatenação utiliza o mesmo operador


que vimos anteriormente como adição ou identidade (+), porém tal definição se aplica
apenas quando os operandos envolvidos na expressão são numéricos. Isso é comum em
Python e também em outras linguagens de programação.

👁️‍🗨️ VOCÊ SABIA?


Quando um operador tem comportamento diferente dependendo dos operandos em que
é aplicado, dizemos que ocorreu uma sobrecarga do operador. Essa característica é útil
e permite facilidades de sintaxe como a concatenação. Veja mais em:
https://pense-python.caravela.club/17-classes-e-metodos/07-sobrecarga-de-operadores.html

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.

>>> nome = 'Megan'


>>> idade = 34
>>> x = 'Olá ' + nome + '! Você tem ' + str(idade) + ' anos.'
>>> print(x)
>>> print(nome, idade + 5)
Codificação 5.2: Exemplo de concatenação de strings seguida de atribuição.

A função integrada str recebe como argumento um valor de qualquer tipo e


retorna a representação do valor como uma string. Tivemos que usar essa função para
converter o valor da variável idade, um inteiro, para string e assim poder fazer a
concatenação com as demais strings. Para aprendizado, tente realizar a concatenação
sem a conversão e veja o que acontece.

Núcleo de Educação a Distância | Faculdade Impacta


Linguagem de Programação

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.

Talvez surjam dúvidas sobre o Exemplo 4, em que 'beatriz' > 'bia'


resulta em False, neste caso, possivelmente o motivo da expressão do Exemplo 3
resultar em True, também não foi completamente entendido. Para melhor compreensão,
precisamos de mais detalhes sobre como a comparação entre strings é realizada.
Em Python a comparação entre strings ocorre caractere a caractere, ou seja,
compara-se o primeiro caractere da string à esquerda com o primeiro caractere da string
à direita do operador. Enquanto as strings não acabarem (existirem caracteres em ambas
as strings que ainda não foram comparados) e os caracteres comparados forem iguais,
compara-se o próximo par. A Figura 5.1 ilustra o procedimento com base no Exemplo 4
da Codificação 5.4.
Strings de tamanhos distintos, ou seja, com quantidades diferentes de caracteres,
são consideradas diferentes entre si, assim como strings com caracteres diferentes.
Note que não é o tamanho da string que determina se ela é menor ou maior que
outra, mas sim seu conteúdo. Portanto, 'beatriz' não é maior do que 'bia', pois o
caractere 'e' de 'beatriz' não é maior do que 'i' de 'bia'.

Núcleo de Educação a Distância | Faculdade Impacta


Linguagem de Programação

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.

Na Codificação 5.5, as strings têm o mesmo tamanho e conteúdo, exceto pelo


último caractere. Como saber se '!' é menor do que '?' se não estão em nosso
alfabeto? Usaremos a função integrada ord que recebe como argumento um caractere e
devolve como resposta um número natural correspondente, conforme a Codificação 5.6.
>>> ord('!')
33
>>> ord('?')
63
Codificação 5.6: Exemplo de uso da função ord.

Quando caracteres são comparados, internamente ocorre a comparação entre


números naturais que correspondem a esses caracteres. Logo, é fácil perceber que 33 é
menor do que 63, justificando a resposta de 'oi!' < 'oi?'. A dúvida é “o que são
esses números que correspondem aos caracteres?”. São códigos da tabela Unicode.

📚 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

Núcleo de Educação a Distância | Faculdade Impacta


Linguagem de Programação

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

Núcleo de Educação a Distância | Faculdade Impacta


Linguagem de Programação

Nesta disciplina optamos pelas f-strings, pois possuem vantagens em


legibilidade e desempenho, além de serem indicadas pela comunidade (Bader, 2018).
Por ser um tópico extenso, aprenderemos inicialmente a usar f-strings com as
formatações mais recorrentes em nossos programas. Começaremos com um exemplo
simples, como pode ser visto na Codificação 5.9.
>>> mensagem = f'2 + 3 = {2 + 3}, entendeu?'
>>> mensagem
'2 + 3 = 5, entendeu?'
Codificação 5.9: Exemplo de formatação de strings.

Note que a expressão entre o par de chaves é avaliada e o resultado inserido no


mesmo ponto em que está escrita na string. A avaliação ocorre em tempo de execução, o
que permite o uso de f-strings com variáveis e expressões, como na Codificação 5.10.
>>> salario = float(input('Seu salário: '))
Seu salário: 1000.00
>>> f'20% a mais em R$ {salario} dará R$ {salario * 1.2}'
'20% a mais em R$ 1000.0 dará R$ 1200.0'
Codificação 5.10: Exemplo de formatação de strings.

Vimos que utilizar f-strings para formatação melhora a legibilidade do código


quando comparado com a concatenação ou com a passagem de diversos argumentos
para a função print. Entretanto, só isso não seria suficiente para problemas que exigem
formatação mais elaborada, então veremos algumas possibilidades de manipulação de
texto com o uso de f-strings.
No Codificação 5.10, seria útil exibir os valores monetários com duas casas
decimais. Para especificar a formatação de um valor em uma f-string, o pós-fixamos
com : e acrescentamos alguns especificadores.
Para formatar um dado do tipo float, podemos usar o seguinte padrão de
formatação: f'{<valor>:<colunas>.<decimais>f}'. Onde:
● colunas: a quantidade mínima de colunas reservadas para o valor na string
formatada, note que cada caractere do valor ocupa uma coluna, inclusive o
ponto. Se omitido, assume-se a quantidade mínima para expressar o número;
● decimais: o número total de casas decimais que serão representadas na
string, a última casa à direita é arredondada. Se omitido, o padrão será de
seis casas;
● f: indica que a formatação será feita para o tipo float, podendo haver uma
conversão automática entre tipos compatíveis, como o int.

Núcleo de Educação a Distância | Faculdade Impacta


Linguagem de Programação

Veja a Codificação 5.11, em seguida reescreva a Codificação 5.10, porém


formatando os valores em reais com duas casas decimais.
>>> pi = 3.14159265
>>> f'{pi:f}' # sem especificação o padrão são 6 casas decimais
'3.141593'
>>> f'{pi:.3f}' # note o arredondamento na última casa decimal
'3.142'
>>> f'{pi:7.3f}' # 7 colunas com 3 reservadas para parte decimal
' 3.142'
Codificação 5.11: Exemplos de formatação de strings com especificadores.

Existem outros especificadores para formatação de float, como exibição em


notação científica, e também para outros tipos, como int e string. A lista completa com
as explicações e exemplos de uso pode ser vista na documentação oficial do Python2.
Assim como visto com valores do tipo float, podemos definir o tamanho mínimo
de colunas para qualquer string formatada, independente do tipo do dado. O padrão para
isso é f'{<valor>:<colunas>}', como consta na Codificação 5.12.
>>> nome = 'Megan'
>>> f'{nome:10}'
'Megan '
Codificação 5.12: Formatação de quantidade mínima de colunas para um valor qualquer.

Nos exemplos de formatação de float, percebemos que o alinhamento padrão de


números é à direita, já para valores do tipo string o padrão é à esquerda, mas em ambos
os casos podemos controlar o alinhamento adicionando um dos símbolos da Tabela 5.1,
de acordo com o padrão f'{<valor>:<alinhamento><colunas>}'.
Tabela 5.1: Símbolos de alinhamento.

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).

Núcleo de Educação a Distância | Faculdade Impacta


Linguagem de Programação

antes do alinhamento: f'{<valor>:<caractere><alinhamento><colunas>}'. Veja


um exemplo na Codificação 5.14.
>>> f'{"à esquerda":<30}'
'à esquerda '
>>> f'{"à direita":>30}'
' à direita'
>>> f'{"centro":^30}'
' centro '
Codificação 5.13: Formatação de alinhamento do conteúdo de uma string.

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.

5.3. Flags booleanas


O termo flag vem do inglês e significa “bandeira” e bandeiras servem para
sinalizar algo, então uma flag booleana é usada para sinalizar algo no código. Podemos
usá-las para facilitar a leitura de estruturas de seleção e também de repetição indefinida,
que veremos mais à frente no curso.
Uma flag booleana nada mais é do que uma variável que guarda o resultado de
uma comparação ou expressão relacional/lógica. Assim, ao acessarmos o valor dessa
variável ao longo do fluxo de execução do código, podemos saber se a condição em
questão foi atendida ou não, podemos pensar em uma bandeira levantada (True) ou não
(False). Veja a Codificação 5.15, base para a Codificação 5.16 que usará flag booleana.
idade = int(input('Qual a sua idade? '))

if idade >= 18:


print('Você é maior de idade.')
Codificação 5.15: Programa que verifica se o usuário é maior de idade (sem flag).

Na codificação acima, o bloco de código do if só será executado quando o


usuário fornecer como entrada um inteiro maior ou igual a 18. Podemos reescrever este
exemplo utilizando uma flag booleana para guardar o “resultado da condição” que

Núcleo de Educação a Distância | Faculdade Impacta


Linguagem de Programação

queremos averiguar. Recomenda-se que o nome da variável seja representativo em


relação ao que será avaliado. O código implementado está na Codificação 5.16.
idade = int(input('Qual a sua idade? '))
maior_de_idade = idade >= 18

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)')

if idade >= 18 and cnh == 's':


print('Você pode dirigir.')
Codificação 5.17: Programa que verifica se o usuário pode dirigir (sem flag).

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)')

maior_de_idade = idade >= 18


possui_cnh = cnh == 's'
pode_dirigir = maior_de_idade and possui_cnh

if pode_dirigir:
print('Você pode dirigir.')
Codificação 5.18: Programa que verifica se o usuário pode dirigir (com flag).

O uso de flags booleanas pode facilitar a legibilidade do código, aproximando a


leitura do código-fonte ao que normalmente falaríamos em nosso idioma natural, mas é
preciso cuidado, pois a utilização de nomes inadequados pode acarretar erros de
interpretação sutís, de difícil correção. Logo, é extremamente importante que haja uma
relação direta entre a condição/expressão avaliada e o nome escolhido para a flag.

Núcleo de Educação a Distância | Faculdade Impacta


Linguagem de Programação

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.

Vale lembrar que o uso exagerado de flags booleanas, seja colocando-as em


todas as situações possíveis, seja usando-as muitas vezes ao longo do código, pode
resultar no efeito contrário ao esperado, elevando a complexidade do código e piorando
sua legibilidade, pois pode aumentar desnecessariamente a verbosidade do código.
O excesso de verbosidade pode ocorrer especialmente em problemas simples,
como os vistos nos exemplos desta aula. Em uma situação real na qual fosse necessária
a verificação de idade de uma pessoa, boa parte dos desenvolvedores provavelmente
realizaria a comparação diretamente na condição do if, pois idade >= 18 é uma
comparação com interpretação simples (conhecendo o contexto entende-se que o
objetivo é saber se uma pessoa é maior de idade), então o uso da flag maior_de_idade
aumenta desnecessariamente a complexidade do código.
Um questionamento comum é relativo ao consumo extra de memória pelas flags,
o que poderia piorar o desempenho da solução. Quanto a isso, é seguro dizer que para a
maioria das aplicações, o consumo extra é insignificante.
Na implementação do Python em C3, são alocados 28 bytes para uma variável
com o valor True e 24 bytes para o valor False, mais alguns bytes para guardar o
nome da variável em si. Portanto, na maioria das situações, é desnecessária a
preocupação com a quantidade de memória gasta com variáveis simples, e a decisão
sobre o uso de uma flag booleana deve ser feita com base na legibilidade do código,
com foco em deixá-lo mais simples e fácil de entender.

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).

Núcleo de Educação a Distância | Faculdade Impacta


Linguagem de Programação

5.4. Estruturas de seleção aninhadas


A única regra para o código dentro de um bloco de if ou else é que ele deve
ser um código válido em Python. Portanto, é possível que exista uma estrutura de
seleção dentro de outra. Basta seguir as mesmas regras que vimos na definição de
blocos de código em Python e de como if e else se relacionam:
1) O bloco de código é introduzido por “dois pontos”;
2) Instruções pertencentes ao bloco estão no mesmo nível de indentação entre si
e indentadas em 4 espaços4 relativamente à instrução que introduz o bloco.
Para compreender as estruturas de seleções aninhadas e a necessidade de usá-las,
veremos a Codificação 5.19, um programa que solicita a idade do usuário e, de acordo
com a resposta, realiza outras perguntas para descobrir se ele pode dirigir. Tente
entender os possíveis caminhos dentro deste código e desenhe um mapa onde cada if é
uma bifurcação na “estrada” do fluxo de execução e cada instrução é uma “cidade”
neste mapa. Se um if não possui um comando else associado, podemos interpretá-lo
como uma bifurcação cujo lado else não contém nenhuma “cidade”.
idade = int(input('Qual a sua idade? ')) #1
if idade >= 18: #2
cnh = input('Você tem CNH? (s/n)') #3
if cnh == 's': #4
cnh_valida = input('Ela está válida? (s/n)') #5
if cnh_valida == 's': #6
print('Você pode dirigir.') #7
else:
print('Você precisa renovar sua CNH.') #8
else:
print('Você precisa tirar a CNH para dirigir.') #9
else:
print('Você ainda não pode dirigir.') #10
tempo_falta = 18 - idade #11
if tempo_falta <= 2: #12
print('Falta pouco tempo para você poder dirigir.') #13
else:
print('Demorará para você poder dirigir.') #14
print('Mas você pode dirigir um kart se quiser.') #15
print('Fim!') #16
Codificação 5.19: Programa com estruturas de seleção aninhadas.

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).

Núcleo de Educação a Distância | Faculdade Impacta


Linguagem de Programação

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.

Uma forma de entender um trecho de código mais complexo é com a construção


destes mapas ou de fluxogramas. Podemos pensar inicialmente em um mapa visto de
longe, com apenas 2 caminhos, como mostra a Figura 5.3, e conforme aumentamos o
“zoom” neste mapa, caminhos menores surgirão dentro dos caminhos maiores, e assim
sucessivamente para cada novo bloco interno, como podemos ver na Figura 5.4. Dessa
forma, analisamos o código a partir do bloco mais externo para o mais interno.

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.

Núcleo de Educação a Distância | Faculdade Impacta


Linguagem de Programação

Lembre-se de testar esse exemplo também no Python Tutor, para visualizar


passo a passo as instruções que são executadas, em função das entradas fornecidas.
Você possivelmente deve ter concluído que em estruturas de seleção aninhadas a
seleção mais externa funciona como um filtro para suas seleções internas, ou seja, para
que o fluxo de execução do programa atinja o ponto de uma seleção interna,
necessariamente já deve ter passado pela decisão da seleção mais externa.

5.5. Estruturas de seleção encadeadas


As estruturas de seleção encadeadas são um subtipo das estruturas aninhadas.
Dizemos que uma estrutura de seleção A está encadeada em uma estrutura de seleção B,
quando A está dentro do bloco else de B, e A é a única instrução desse bloco.
Quando isso ocorre, dizemos que as seleções estão encadeadas entre si, isto é,
cada seleção do encadeamento só é analisada se todas as anteriores resultarem em
False, e no momento que uma das condições é avaliada True, seu bloco de código é
executado e as demais condições subsequentes, se existirem, serão ignoradas.
Vejamos outro exemplo com um programa que lê a nota de um aluno em uma
escala de 0 a 10, e converte-a para uma escala usando letras, como mostra a Figura 5.5.

Figura 5.5: Intervalos de classificação das notas. Fonte: Elaborado pelo autor.

O programa correspondente está na Codificação 5.20. Observe que:


● A primeira bifurcação no fluxo de execução ocorre no if da instrução #2.
Caso a condição nota >= 9 resulte True o fluxo será direcionado para #3,
caso contrário será direcionado para a primeira instrução do bloco else
correspondente, ou seja, a instrução #4, garantindo que nota é menor que 9;
● O bloco do else só é executado quando a condição do if correspondente
resultar False. No exemplo anterior, todas as instruções a partir da #4 até
#10 fazem parte do bloco do primeiro else, evidente pela indentação,
portanto, se #3 é executada, a próxima instrução deste caminho será #11;
● Caso a nota seja menor do que 9, então a instrução #4 será executada, e
haverá mais uma divisão de caminhos, entre notas maiores ou iguais a 8 e a
negação disso, notas menores que 8. Aqui, vale ressaltar que não é
necessário verificar se nota < 9, pois ao executar #4, sabemos que a
condição da instrução #2 resultou em False e, portanto, o valor de nota é
obrigatoriamente menor que 9.

Núcleo de Educação a Distância | Faculdade Impacta


Linguagem de Programação

nota = float(input('Qual a nota? ')) #1

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

print(f'Sua letra é: {letra}') #11


Codificação 5.20: Programa com estruturas de seleção encadeadas.

5.5.1. O comando elif


O comando elif é uma fusão dos comandos else e if quando se encontram
na situação de uma estrutura de seleção encadeada, como acabamos de ver. Ele é mais
um exemplo de "açúcar sintático”, introduzido na linguagem para facilitar a escrita e,
principalmente, a leitura do código.
A Codificação 5.21 contém lado a lado a sintaxe de uma estrutura de seleção
encadeada sem elif (à esquerda) e com elif (à direita). Os dois códigos apresentam
os mesmos fluxos de execução, veja na Figura 5.6 que a única diferença está na sintaxe.
if <condição 1>: #1 if <condição 1>: #1
<bloco 1> <bloco 1>
else: #2 elif <condição 2>: #2 + #3
if <condição 2>: #3 <bloco 2>
<bloco 2> else: #4
else: #4 <bloco 3>
<bloco 3>
Codificação 5.21: Estrutura de seleção encadeada sem elif (à esq.) e com elif (à dir.).

Na Codificação 5.21, note que ao unirmos #2 com #3 (else e if) formarmos o


elif, e as consequências dessa união são:

Núcleo de Educação a Distância | Faculdade Impacta


Linguagem de Programação

● O comando elif começa com um else, portanto é sempre um


complemento de um if ou outro elif, e não pode existir isoladamente;
● O comando elif termina com um if, portanto podemos complementá-lo
com um else ou outro elif na sequência, e assim por diante sem
quantidade limite;
● O comando else sempre encerra uma estrutura de seleção, seja ele usado
após um if ou elif;
● Esse encadeamento garante que o bloco executado será aquele que estiver
dentro da primeira estrutura de seleção que a condição resultar True, e que
as seleções posteriores no encadeamento serão puladas.

Figura 5.6: Mapa de caminhos possíveis da Codificação 5.21. Fonte: Elaborado pelo
autor.

Podemos então aplicar este novo comando à Codificação 5.20 gerando a


Codificação 5.22. Observe como o código permanece com apenas dois níveis de
indentação, mais conciso, simples e com melhor legibilidade.
nota = float(input('Qual a nota? '))

if nota >= 9:
letra = 'A'
elif nota >= 8:
letra = 'B'
elif nota >= 6:
letra = 'C'
elif nota >= 4:
letra = 'D'
else:
letra = 'E'

print(f'Sua letra é: {letra}')


Codificação 5.22: Estrutura de seleção encadeada com elif.

Núcleo de Educação a Distância | Faculdade Impacta


Linguagem de Programação

🏹 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.

Núcleo de Educação a Distância | Faculdade Impacta


Proceedings of the XII SIBGRAPI (October 1999) 101-104
Linguagem de Programação

Texto base

6
Criação de funções em Python

Prof. Me. Lucio Nunes de Lira


Prof. MSc. Rafael Maximo Carreira Ribeiro

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/

Núcleo de Educação a Distância | Faculdade Impacta


Linguagem de Programação

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.

6.3. Funções importadas


Vimos em Python as funções integradas, que podem ser usadas em qualquer
trecho de código, pois são reconhecidas automaticamente pelo interpretador e,
consequentemente, estão sempre disponíveis. Agora, aprenderemos a criar nossas
próprias funções, mas antes disso, veremos como usar funções desenvolvidas por outros
programadores e que estão disponíveis para uso em pacotes ou módulos extras.
O Python possui um conjunto de módulos extras que chamamos de biblioteca
1
padrão . Os módulos dessa biblioteca são instalados junto com o interpretador, mas não
são carregados automaticamente ao executarmos o Python. Quando precisamos usar um
módulo da biblioteca padrão, primeiro temos que importá-lo para o nosso código-fonte,
indicando ao interpretador para carregá-lo e disponibilizá-lo para uso.
1
Veja mais detalhes em: https://docs.python.org/pt-br/3/library/

Núcleo de Educação a Distância | Faculdade Impacta


Linguagem de Programação

A importação é feita com o comando import. A Codificação 6.1 tem um


exemplo de importação do módulo de matemática math, que contém diversas funções e
constantes matemáticas.
>>> import math
>>> math.pi
3.141592653589793
>>> math.log(10)
2.302585092994046
Codificação 6.1: Exemplo de importação do módulo math e uso de seus conteúdos.

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.

6.4. Funções definidas pelo programador


Para criarmos novas funções é preciso defini-las. Em Python, para definir uma
função, utiliza-se o comando def, conforme a estrutura exposta na Codificação 6.2.
def <nome da função>(<parâmetros>):
<bloco de código>
Codificação 6.2: Estrutura das funções definidas pelo programador.

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.

Núcleo de Educação a Distância | Faculdade Impacta


Linguagem de Programação

Após o nome da função, deve-se colocar um par de parênteses e, entre eles, os


parâmetros, se existirem. Os parâmetros são variáveis internas da função que recebem
os argumentos passados quando a função é chamada. Note que o par de parênteses é
obrigatório, mas, dependendo da função, pode não haver parâmetros, se houver mais de
um, devem ser separados por vírgulas.
Na linha seguinte aos dois pontos, que sinalizam o fim da assinatura2 da função,
está o bloco de código, indentado para indicar que pertence à função, assim como é feito
nas estruturas de seleção. Nesse bloco de código, pode-se inserir qualquer instrução
válida, inclusive chamadas a outras funções integradas, importadas ou definidas pelo
próprio programador. É possível definir funções dentro de outra, mesmo não sendo uma
prática frequente. No entanto, chamar funções dentro de outra é algo bastante utilizado!
Escreva a Codificação 6.3 no editor, salve o arquivo como “funcao.py” e
execute-o pressionando [F5] ou clicando em “Run > Run Module” na janela do editor.
def soma(n1, n2):
s = n1 + n2
return s
Codificação 6.3: Função que retorna como resposta a soma dos dois parâmetros.

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.

Se obteve resultado similar, provavelmente sua função foi definida corretamente.


Ao inserir apenas o nome da função na Shell, ou seja, sem os parênteses para chamá-la,
o que vemos é o endereço de memória onde está o código da função, inclusive o
endereço é um número natural que está representado em base hexadecimal (0x).
Note que soma é o nome da função e, como mencionamos, em Python o nome
da função é uma variável que faz referência ao seu código. A referência é feita por meio
do endereço da função, ou seja, a variável guarda o endereço de memória onde está o
código da função para ser executado. Isso permite, por exemplo, que outra variável
também receba o endereço e possamos chamar a função por meio dessa nova variável.
Quer testar? Execute a Codificação 6.5 na Shell e se surpreenda!

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.

Núcleo de Educação a Distância | Faculdade Impacta


Linguagem de Programação

>>> imprime = print


>>> imprime('Programando em português!')
Programando em português!
Codificação 6.5: Atribuição da referência de uma função à outra variável.

Retomando à criação da função, repare que existem dois momentos distintos ao


lidarmos com funções definidas pelo próprio programador: (I) a definição da função e;
(II) a invocação da função, também conhecido como “chamar a função”.
O comando def só indica que a função deve ser definida e, após ser executado,
disponibiliza a função na memória e a associa ao nome escolhido, porém não a chama
automaticamente. Obviamente, toda função deve estar definida antes de ser chamada.
Após a função estar definida, para executá-la basta chamá-la de modo idêntico às
funções integradas e importadas, usando parênteses após seu nome e com os
argumentos entre eles. Veja exemplos de chamadas à soma na Codificação 6.6.
>>> soma(3, 5)
8
>>> soma(7, -9)
-2
Codificação 6.6: Chamadas à função soma definida pelo programador.

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.

Núcleo de Educação a Distância | Faculdade Impacta


Linguagem de Programação

Figura 6.1: Relação entre argumentos e parâmetros. Fonte: Elaborado pelo autor.

def soma(n1, n2): #1


print('Início do bloco da função') #2
s = n1 + n2 #3
return s #4
print('Fim do bloco da função') #5

print('Fora da função!') #6
Codificação 6.7: “funcao.py” alterado para melhor visualização do fluxo de execução.

Após a execução da Codificação 6.7, o resultado é o ilustrado na Figura 6.2.


Novamente, a função soma foi definida, mas como ela não foi chamada, seu bloco de
código ainda não foi executado. Agora que está carregada na memória, vamos refazer as
chamadas à soma e observar o resultado, como consta na Codificação 6.8.

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.

Núcleo de Educação a Distância | Faculdade Impacta


Linguagem de Programação

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.

👁️‍🗨️ VOCÊ SABIA?


Código inalcançável (do inglês unreachable code) é uma parte do código-fonte de um
programa que jamais será executada, pois o fluxo de execução jamais a alcançará.
Geralmente códigos inalcançáveis são produzidos por erros de lógica ou provenientes de
código legado. Em geral, queremos evitá-lo, mas existem algumas razões que podem
justificar sua existência. Veja mais em: https://en.wikipedia.org/wiki/Unreachable_code

É 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(f'{4} é par? {par(4)}')


print(f'{7} é par? {par(7)}')
Codificação 6.9: Exemplo de função com dois comandos return.

6.5. Retorno de valor vs. exibição de valor


Na Codificação 6.3, poderíamos ficar tentados a afirmar que a função soma
exibe o resultado da soma, mas ao olharmos para o seu bloco de código, vemos que ela
retorna o valor da variável s ao invés de exibi-lo. Nem mesmo há print na função!
O retorno da função soma só foi exibido devido ao comportamento interativo da
Shell, que exibe o resultado de qualquer expressão, o que inclui os valores retornados
por funções. Para visualizar a diferença, faremos as mesmas chamadas, porém agora no
editor do IDLE. Altere novamente o arquivo “funcao.py”, conforme a Codificação 6.10.
O que acontece ao executar a Codificação 6.10? As somas serão exibidas? Tente
prever antes de visualizar a resposta na Figura 6.3.

Núcleo de Educação a Distância | Faculdade Impacta


Linguagem de Programação

def soma(n1, n2): #1


print('Início do bloco da função') #2
s = n1 + n2 #3
return s #4

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.

A função retornou, normalmente, um valor a cada chamada, porém esse valor


não foi aproveitado para outra operação como, por exemplo, uma exibição. O mesmo
ocorre ao criar uma instrução que seja unicamaente uma expressão, como na segunda
linha da Codificação 6.11 e que o resultado da execução está ilustrado na Figura 6.4.
print('Começo do programa!')
2 * 3 + 4
print('Fim do programa!')
Codificação 6.11: Exemplo de expressão avaliada, mas com resultado não aproveitado.

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.

Núcleo de Educação a Distância | Faculdade Impacta


Linguagem de Programação

Para entender as opções, faça duas cópias da última versão de “funcao.py”


(Codificação 6.10) e renomeie-os para “funcao_return.py” e “funcao_print.py”.
Em “funcao_return.py”, substitua o código após a definição da função pela
Codificação 6.12. Execute e veja o resultado na Figura 6.5.
print('Fora da função!') #5
resultado1 = soma(3, 5) #6
print('1ª Soma é:', resultado1) #7
print('2ª Soma é:', soma(7, -9)) #8
Codificação 6.12: Atribuição e exibição do valor retornado pela função soma.

Figura 6.5: Execução do arquivo “funcao_return.py”. Fonte: Elaborado pelo autor.

Em “funcao_print.py”, substitua apenas o código da definição da função pela


Codificação 6.13. Execute e veja o resultado na Figura 6.6.
def soma(n1, n2): #1
print('Início do bloco da função') #2
s = n1 + n2 #3
print('A soma é:', s) #4
Codificação 6.13: Alteração da função soma que deixa de retornar a soma, apenas a exibe.

Figura 6.6: Execução do arquivo “funcao_print.py”. Fonte: Elaborado pelo autor.

Após executar o arquivo “funcao_print.py”, chame soma na Shell, e atribua seu


valor de retorno a uma variável, como exemplificado na Codificação 6.14.

Núcleo de Educação a Distância | Faculdade Impacta


Linguagem de Programação

>>> teste = soma(2, 3)


A soma é: 5
Codificação 6.14: Atribuição do valor retornado por uma função sem return explícito.

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.

Exatamente! Em Python, uma função que não executa um comando return, ou


que o executa sem um valor à direita para ser retornado, devolve como resposta um
valor especial em Python, o None. Em Python, esse valor é um indicativo do “nada”.
Essa é uma característica de Python. Em outras linguagens, tentar atribuir a uma variável
o valor de retorno de uma função que não retorna explicitamente algo pode gerar erro.

👁️‍🗨️
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 pergunta final é: “quando usar return e quando usar print?”. A resposta é:


“depende”. Depende porque é necessário saber qual o objetivo da função que será criada.
Na maioria das vezes, utilizaremos return, pois em geral construímos funções
que respondem uma pergunta: “qual a soma de dois números?”, “qual a raiz quadrada
de um número?”, “quantos caracteres uma string possui?”, “este número é par?”.
Fazemos uma pergunta e esperamos um valor como resposta, que pode ser um número,
um valor booleano, uma string ou qualquer outro tipo de dado do Python.
Já o print será usado quando dermos uma ordem à função para exibir algo na
tela, sem esperar uma resposta, como: “exiba o menu de opções!”, “mostre uma
mensagem de saudação!”, e assim por diante. Nestes casos não há necessidade de
retorno de valor. Esse tipo de função faz parte de uma classe de funções conhecidas
como funções nulas, que são funções que executam alguma ação (como exibir algo na
tela ou outros efeitos) e, em Python, sempre retornam o valor None.

Núcleo de Educação a Distância | Faculdade Impacta


Linguagem de Programação

Portanto, em Python, toda função retorna um valor: (a) explicitamente, quando


está à direita de um comando return ou; (b) implicitamente, quando não há return
ou quando este não está acompanhado de um valor a ser retornado, neste caso retorna-se
None. Note que há funções que não precisam retornar um valor, o algoritmo não exige,
mas por questão de projeto da linguagem Python retornarão automaticamente None.

6.6. Escopo de variáveis


De modo simplificado, podemos pensar no escopo como a região do programa
na qual a ligação de um identificador com um determinado valor na memória seja
válida. Por exemplo, após executar a instrução n = 2, o escopo de n é toda parte do
código na qual podemos acessar exatamente essa variável usando o identificador n. Se
tentarmos acessar n fora do seu escopo, teremos acesso a outro espaço de memória ou
um erro de identificador não definido.
Em Python, variáveis criadas na raíz do código, isto é, fora de funções, terão
escopo global, e serão acessíveis em todo o código-fonte onde foram criadas, a partir de
sua instrução de criação. Já variáveis criadas no interior de funções terão escopo local e,
portanto, acessíveis apenas dentro da função em que foram criadas. Insira a Codificação
6.16 no editor e a execute.
def diga_ola():
print(f'Olá {nome}!') # acessa a variável global

nome = 'Megan' # variável global


diga_ola()
Codificação 6.16: Exemplo de função que acessa uma variável global.

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

nome = 'Megan' # variável global


diga_ola('Maria')
print(f'Tchau {nome}!') # acessa a variável global
Codificação 6.17: Exemplo de função que acessa sua variável local.

Núcleo de Educação a Distância | Faculdade Impacta


Linguagem de Programação

Veja no Python Tutor, ilustrado na Figura 6.7, uma representação da separação


na memória do escopo global e do escopo local na execução da Codificação 6.17.

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

titulo = 'Dra.' # variável global


nome = 'Maria' # variável global
diga_ola()
print(f'Tchau {titulo} {nome}!')
Codificação 6.18: Exemplo de função que modifica variáveis globais.

O uso de variáveis globais em funções é desencorajado, pois viola o


encapsulamento3 das funções, dificulta a leitura do código e correções. Note que as
funções que usam variáveis globais dependem de recursos que podem ser alterados por
outros trechos de código, o que pode gerar caos em programas mais complexos.
Por isso, caso precise informar valores externos às funções, use os parâmetros.
Desta forma a função terá os dados necessários para executar seu algoritmo sem violar o
encapsulamento.

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.

Núcleo de Educação a Distância | Faculdade Impacta


Linguagem de Programação

6.7. Texto de documentação de funções


Ao definirmos novas funções, é comum acoplarmos comentários para explicar o
funcionamento para quem as usará, que podem ser outros programadores ou nós
mesmos. Se feito conforme um padrão, muitos editores de código poderão identificar
essa documentação e mostrá-la de modo conveniente, para isso existem as docstrings.
Vamos criar uma documentação para nossa função soma. Altere o arquivo “funcao.py”
para que fique conforme a Codificação 6.19, incluindo a string de documentação.
def soma(n1, n2):
"""Retorna a soma de dois números.

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.

A primeira linha é um resumo sobre o que a função faz, em seguida podemos


adicionar outras informações sobre a função, como seus parâmetros e valor de retorno.
Aqui utilizamos o guia de docstrings criado pelo NUMPY (2021). O guia recomenda
que tanto o código quanto as strings de documentação estejam em inglês, mas como
toda recomendação de guia de estilo, o mais importante é manter a coerência interna em
um projeto, por isso fizemos o exemplo em português.
Após inserir uma docstring na função, podemos visualizar seu conteúdo de
algumas formas diferentes, dentre as quais abordaremos duas: (I) digitando o nome da
função seguida da abertura de parênteses, tanto na Shell quanto no editor, aguardando
alguns segundos para a exibição de um balão com a assinatura da função, junto com a
primeira linha da docstring, como podemos ver na Figura 6.8; (II) usando a função
help, passando como argumento o nome da função que contém a docstring, desta
forma a documentação completa será exibida, conforme a Figura 6.9.

Núcleo de Educação a Distância | Faculdade Impacta


Linguagem de Programaçã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.

6.8. Organização do código-fonte


Aprendemos a importar módulos da biblioteca padrão e a criar nossas próprias
funções, agora veremos algumas recomendações de organização para melhorar a
legibilidade, rastreamento de erros e manutenção de nossos códigos-fonte:
1) As importações de bibliotecas devem ser feitas no início do código-fonte;
2) Após as importações, se definem as funções;
3) Por último, escreve-se o código principal que chamará as funções e resolverá
o problema. É comum denominar o código principal de programa principal.
Veja um exemplo de aplicação dessas recomendações na Codificação 6.20.

Núcleo de Educação a Distância | Faculdade Impacta


Linguagem de Programação

# 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

Docstring Guide. Numpydoc 2021. Disponível em: <https://numpydoc.readthedocs.io/


en/latest/format.html>. Acesso em: 25 jan. 2021.
DOWNEY, A. B. Pense em Python. 1 ed. São Paulo: Novatec Editora Ltda., 2016.
PSF. math: Mathematical Functions. 2021. Disponível em: <https://docs.python.org/
3/library/math.html>. Acesso em: 26 jan. 2021.
STURTZ, J. Defining Your Own Python Functions. Real Python, 2020. Disponível em:
<https://realpython.com/defining-your-own-python-function/>. Acesso em: 25 jan. 2021.

Núcleo de Educação a Distância | Faculdade Impacta


Proceedings of the XII SIBGRAPI (October 1999) 101-104
Linguagem de Programação

Texto base

7
Estrutura de repetição indefinida (while)

Prof. Me. Lucio Nunes de Lira


Prof. MSc. Rafael Maximo Carreira Ribeiro

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/

Núcleo de Educação a Distância | Faculdade Impacta


Linguagem de Programação

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.

Figura 7.1: Representação em fluxograma das estruturas básicas de controle de fluxo.


Fonte: Elaborado pelo autor.

As estruturas de repetição também são conhecidas como estruturas de iteração,


malhas de repetição, laços e loops. Há um conjunto de problemas que não seriam
possíveis de serem resolvidos – ou seriam resolvidos de modo bastante inconveniente –
sem o uso de tais estruturas, veremos alguns exemplos no decorrer da leitura.
Os loops são aplicados quando criamos algoritmos que demandam a repetição de
uma sequência de instruções várias vezes. Vamos iniciar com a análise de um problema
simples que desperta a reflexão sobre a necessidade das estruturas de repetição: criar um
programa que exiba os cinco primeiros números naturais positivos. O conhecimento que
temos permite criação da solução que está na Codificação 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.

Núcleo de Educação a Distância | Faculdade Impacta


Linguagem de Programação

A solução para este problema é simples, no entanto, imagine que ao invés de


exibir os cinco primeiros números, o problema exigisse os cinquenta primeiros. Por
mais que a lógica aplicada ao novo cenário seja equivalente, a repetição de tantas
instruções tornaria o trabalho do programador lento e tedioso, além de aumentar a
chance de inserção de instruções incorretas, consequência do aumento do código-fonte.
Agora, modificaremos um pouco o enunciado do problema original. Suponha
que devemos exibir os cinco números naturais posteriores ao natural dado pelo usuário
como entrada. A Codificação 7.2 é uma solução válida para esse enunciado.

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.

A solução pode ser melhorada, tornando todas as instruções de exibição iguais e


potencialmente reduzindo erros de digitação do programador, permitindo replicação das
instruções apenas com o “copiar e colar”. Veja a alteração na Codificação 7.3, em que
incrementamos o valor da variável x antes de cada exibição.
x = int(input('Valor: '))

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,

Núcleo de Educação a Distância | Faculdade Impacta


Linguagem de Programação

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!

7.3. Tipos de estruturas de repetição


Essencialmente as estruturas de repetição podem ser classificadas em dois tipos:

1) Estruturas de repetição com quantidade de repetições indefinida: não é


possível determinar quantas vezes as instruções do loop serão executadas
antes dele ser iniciado;
2) Estruturas de repetição com quantidade de repetições definida: é possível
determinar quantas vezes as instruções do loop serão executadas antes
mesmo dele ser iniciado.

As linguagens de programação podem ter mais de um loop de cada tipo,


permitindo ao programador decidir qual o mais adequado de acordo com o contexto,
porém a razão de existência de todos os laços é a mesma: executar uma sequência de
instruções repetidas vezes. Em Python temos um único laço de cada tipo: while e for.

7.4. Estrutura de repetição while


Inicialmente abordaremos somente o representante da estrutura de repetição com
quantidade de repetições indefinida, que em Python é representada pelo comando
while. A estrutura do comando while é similar à do if, conforme a Codificação 7.4.

Núcleo de Educação a Distância | Faculdade Impacta


Linguagem de Programação

while <condição>:
<bloco de código>
Codificação 7.4: Estrutura do laço de repetição while.

As mesmas regras que aprendemos para as estruturas de seleção se aplicam ao


loop while, portanto toda instrução que estiver indentada no <bloco de código>
pertence ao loop e só será executada caso <condição> resulte em True, caso resulte
em False o laço é encerrado e a instrução imediatamente seguinte ao seu bloco será a
próxima a ser executada. Aparentemente, um fluxo de execução semelhante ao do if.

Porém, o while possui uma notável diferença em relação ao if. No while,


após a execução da última instrução de seu bloco de código, o fluxo de execução é
deslocado novamente para sua condição, que será avaliada mais uma vez, repetindo todo
o procedimento explicado no parágrafo anterior. Podemos visualizar o funcionamento
da estrutura de repetição while no fluxograma ilustrado Figura 7.2.

Figura 7.2: Representação em fluxograma da estrutura de repetição while.


Fonte: Elaborado pelo autor.

Para exemplificar o funcionamento do while, reescreveremos com um laço a


solução para a primeira variação do problema de números naturais (Codificação 7.1).
Para ampliar o entendimento, acrescentaremos a exibição da frase “Tchau!” como uma
instrução imediatamente posterior ao fim do loop, conforme Codificação 7.5.

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.

Núcleo de Educação a Distância | Faculdade Impacta


Linguagem de Programação

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.

Na Codificação 7.6 a variável qtd_exibidos é usada para verificar quantos


naturais foram exibidos. Ela começa em zero, pois antes do laço nenhum número foi
exibido, e é incrementada a cada execução do bloco do laço. Veja que para exibir os
cinquenta naturais posteriores ao da entrada, basta alterar a condição para
qtd_exibidos < 50. Se o enunciado solicitasse os primeiros cinquenta mil números
posteriores, bastaria alterar uma única instrução. Você entendeu o poder dos laços!
Por fim, você deve se recordar da terceira variação do problema de números
naturais, em que dois valores (início e fim) são dados pelo usuário como entrada e que
devemos exibir todos os valores do intervalo de naturais [início..fim]. Agora, com o uso
de um laço, é possível resolvê-lo facilmente, como consta na Codificação 7.7.

inicio = int(input('Início: '))


fim = int(input('Fim: '))
x = inicio
while x <= fim:
print(x)
x += 1
Codificação 7.7: Exibição de todos os naturais de início até fim, inclusive os extremos.

Observe que agora colocamos o incremento da variável x no final do bloco do


laço, isso é comum, pois ela agora é a variável que controlará a execução do laço e uma
forma de interpretar este laço é: (a) primeiro fazemos as operações necessárias com o x

Núcleo de Educação a Distância | Faculdade Impacta


Linguagem de Programação

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.

7.5. Variável contadora


Para construir laços de repetição muitas vezes utilizamos uma variável
contadora, que é útil para contar quantas vezes o bloco de instruções do laço foi
executado e/ou controlar quantas vezes ele será executado. A variável contadora é
incrementada/decrementada por um valor constante, geralmente de um em um.
A Codificação 7.8 é um exemplo de programa que usa uma variável contadora
apenas para contabilizar a quantidade de vezes que o bloco de instruções do loop foi
executado. Note que a variável contador não influencia o número de repetições do
while, isto é, ela não é usada como variável de controle deste laço.

executa = input('Executar o bloco do laço: ')


contador = 0
while executa == 'sim':
contador += 1
executa = input('Executar o bloco do laço de novo: ')
print(f'O bloco do laço foi executado {contador} vezes')
Codificação 7.8: Programa com variável contadora apenas para contagem de repetições.

Núcleo de Educação a Distância | Faculdade Impacta


Linguagem de Programação

A Codificação 7.9 é um exemplo de programa que usa uma variável contadora


para controlar a quantidade de vezes que o bloco de instruções do laço será executado.
Note que a variável contador influencia o número de repetições do while, isto é,
agora ela é usada também como variável de controle deste laço.

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.

7.6. Variável acumuladora


Uma variável acumuladora é utilizada para acumular valores que, em geral, não
são constantes, ou seja, seu incremento ou decremento pode ser variável.
Vejamos um exemplo de problema que precisa de uma variável acumuladora:
“crie um programa que receba como entrada os preços de itens comprados em um
supermercado por um cliente, no final o programa deverá exibir o total da compra. Para
informar que não há mais itens a serem comprados, o cliente digitará o valor -1”. A
Codificação 7.10 é uma solução válida para esse enunciado.

total = 0 # variável acumuladora

preco = float(input('Preço do item: '))


while preco != -1:
total += preco
preco = float(input('Preço do item: '))

print(f'Total da compra: R$ {total:.2f}')


Codificação 7.10: Programa que utiliza uma variável acumuladora.

Vejamos outro exemplo de problema com duas variáveis acumuladoras, porém


com uma delas sendo decrementada: “crie um programa que receba como entrada o
crédito de um cliente e depois o preço de itens comprados por esse cliente, o programa

Núcleo de Educação a Distância | Faculdade Impacta


Linguagem de Programação

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.

credito = float(input('Seu crédito: ')) # variável acumuladora


total = 0 # variável acumuladora

preco = float(input('Preço do item: '))


while credito >= preco:
total += preco
credito -= preco
preco = float(input('Preço do item: '))

print(f'Total da compra: R$ {total:.2f}')


print(f'Crédito restante: R$ {credito:.2f}')
Codificação 7.11: Programa que utiliza duas variáveis acumuladoras.

Observe que a variável credito, além de acumuladora, também é usada como


variável de controle do laço. Neste exemplo, temos duas variáveis usadas para controlar
a execução do loop.

🏹 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.

7.7. Variável flag booleana


As vezes criamos laços em que a condição é dada por uma variável com valor
booleano. Nestes casos dizemos que a variável usada para controlar o laço é uma flag
booleana, pois ela sinaliza se o laço deve ou não ser encerrado. Também é possível criar
uma condição com mais de uma flag booleana. Veja um exemplo na Codificação 7.12.

Núcleo de Educação a Distância | Faculdade Impacta


Linguagem de Programaçã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

print(f'Total da compra: R$ {total:.2f}')


Codificação 7.12: Programa com loop controlado por flag booleana.

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.

7.8. Laço infinito


Um laço infinito é uma estrutura de repetição que nunca é encerrada, ou que é
encerrada apenas por um eventual erro no programa ou por um comando externo, como
um pedido de finalização feito por meio do gerenciador de tarefas do sistema
operacional. Geralmente laços infinitos são gerados por erros de lógica, como uma
instrução esquecida pelo programador que faz com que a condição do laço jamais
resulte em False. Portanto, se quisermos gerar um while infinito devemos garantir
que sua condição sempre resulte em True.

Veja na Codificação 7.13, dois exemplos de laços infinitos, em que o fluxo de


execução, uma vez no laço, jamais atingirá a instrução print('Acabou!').

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.

Núcleo de Educação a Distância | Faculdade Impacta


Linguagem de Programação

🏹 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].

👁️‍🗨️ VOCÊ SABIA?


Laços infinitos podem ser úteis em programas que devem repetir continuamente uma
sequência de instruções e, em certo ponto, reiniciar o procedimento, por exemplo, em
um relógio. Veja mais em: https://en.wikipedia.org/wiki/Infinite_loop

📚 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.

7.9. Combinação de estruturas de controle de fluxo


Como visto na Codificação 7.12, é bastante comum combinarmos estruturas de
repetição com estruturas de seleção. Em algoritmos mais complexos a combinação de
estruturas de controle de fluxo ocorre frequentemente.
Um exemplo de combinação de estruturas de controle de seleção e repetição está
no seguinte problema: “Crie uma função que exiba apenas as 21 consoantes minúsculas
do alfabeto latino.”. A Codificação 7.14 é uma solução válida para esse enunciado.

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.

Outro exemplo de combinação de estruturas de controle de seleção e repetição


ocorre na solução para o seguinte enunciado: “Crie uma função que receba como
argumento um número natural n e devolva um valor booleano indicando se n é primo”.
Um número natural é considerado primo se possui exatamente dois divisores naturais, o
número 1 e o próprio n. A Codificação 7.15 é uma solução válida para esse problema.

Núcleo de Educação a Distância | Faculdade Impacta


Linguagem de Programaçã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.

Núcleo de Educação a Distância | Faculdade Impacta


Proceedings of the XII SIBGRAPI (October 1999) 101-104
Linguagem de Programação

Texto base

8
Laços aninhados e interrupção de laços

Prof. Me. Lucio Nunes de Lira


Prof. MSc. Rafael Maximo Carreira Ribeiro

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

Núcleo de Educação a Distância | Faculdade Impacta


Linguagem de Programação

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.

Nesta aula nos aprofundaremos nas estruturas de repetição. Veremos como


encerrar um loop por meio do comando break e como interromper apenas uma rodada
com o comando continue. Com esses novos recursos, conseguimos facilmente simular
uma estrutura de repetição que em outras linguagens de programação necessitam de
comandos específicos, a repeat…until. O conceito desta estrutura pode ser aplicado, por
exemplo, para a validação de dados de entrada, isto é, para verificar se os dados
fornecidos como entrada pelo usuário são válidos de acordo com o requisitado pelo
programa. Por fim, usaremos estruturas de repetição aninhadas, que são laços dentro do
bloco de instruções de outros laços, algo recorrente em soluções para problemas mais
complexos, em que um laço precisa ser repetido diversas vezes.

8.2. Comando break


A execução de um comando break encerra o loop mais interno em que está
contido. O comando break geralmente está condicionado à um if, de modo que só
seja executado quando algo específico ocorrer. A Figura 8.1 ilustra simplificadamente o
deslocamento do fluxo de execução proporcionado por esse comando.

Figura 8.1: Representação simplificada do comando break em um fluxograma.


Fonte: Elaborado pelo autor.

Um problema simples para observarmos o funcionamento do comando break é


este: “crie um programa que receba como entrada o crédito de um cliente e depois o
preço de itens comprados por esse cliente, o programa deverá parar de solicitar novos

Núcleo de Educação a Distância | Faculdade Impacta


Linguagem de Programaçã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.

Núcleo de Educação a Distância | Faculdade Impacta


Linguagem de Programação

8.3. Comando continue


A execução de um comando continue interrompe a rodada atual do loop mais
interno em que está contido. O comando continue geralmente está condicionado à um
if, de modo que só seja executado quando algo específico ocorrer. A Figura 8.2 ilustra
simplificadamente o deslocamento do fluxo de execução gerado por esse comando.

Figura 8.2: Representação simplificada do comando continue em um fluxograma.


Fonte: Elaborado pelo autor.

Observaremos o funcionamento do comando continue na Codificação 8.3 que


resolve o seguinte problema: “crie um programa que receba como entrada o crédito de
um cliente e os preços de itens comprados por ele. Se o preço de um item for superior
ao valor do crédito, negue a compra do item, exiba o crédito restante e continue a
compra. O programa deve parar de solicitar novos itens quando o crédito for zerado”.

credito = float(input('Seu crédito: R$ '))


while credito > 0:
item = float(input('Preço do item: R$ '))
if item > credito:
print(f'Compra negada! Restam: R$ {credito:.2f}')
continue
credito -= item
Codificação 8.3: Exemplo de funcionamento do comando continue.

No exemplo da calculadora simplificada, feito na Codificação 8.2, ao invés de


encerrar o programa ao identificar uma divisão por zero, seria mais interessante
informar o erro e reiniciar a calculadora. Uma forma simples de obter este resultado é
simplesmente trocar o comando break por continue. Faça a modificação e teste!

Núcleo de Educação a Distância | Faculdade Impacta


Linguagem de Programação

Em geral, principalmente em programas mais simples, break e continue


podem ser substituídos por uma adequação da condição do laço e por uma melhor
organização de seu bloco de código. Portanto, evite o uso generalizado destes recursos,
pois podem tornar o código mais confuso, uma vez que criam desvios do fluxo de
execução que podem não ser óbvios à primeira vista, consequentemente dificultando a
leitura e manutenção do código. Existem, no entanto, situações pertinentes para uso
desses comandos, como a evidenciada em nosso próximo tópico.

8.4. Simulação da estrutura de repetição repeat…until


Python tem duas estruturas de repetição, while e for (ainda não abordamos).
Porém, há problemas que são solucionados de forma mais elegante com uma terceira
estrutura de repetição, conhecida como repeat…until ou, em português, repita…até que.
Essa estrutura obriga que o bloco de instruções do laço seja executado pelo menos uma
vez, pois, diferente do while, a condição para repetição está no fim do laço e não no
início. Logo, para que o fluxo de execução atinja a condição deste laço, é necessário que
primeiramente passe por seu bloco de código. A Figura 8.3 ilustra esse funcionamento.

Figura 8.3: Representação do laço repeat…until. Fonte: Elaborado pelo autor.

O funcionamento do repeat…until justifica o nome da estrutura, indicando que o


laço “repetirá as instruções até que sua condição resulte em verdadeiro”. Como já
mencionado, o Python não possui esse laço, mas podemos simulá-lo facilmente com a
combinação dos comandos while, if e break, conforme a Codificação 8.4.
while True:
<bloco de instruções>
if <condição>: break

Codificação 8.4: Modelo de estrutura simulando o laço repeat…until.

Núcleo de Educação a Distância | Faculdade Impacta


Linguagem de Programação

Observe que na Codificação 8.4 definimos a condição do while como True,


logo sempre será verdadeira. A princípio, poderíamos imaginar que isso tornaria o loop
infinito, porém, no final do bloco de instruções, temos um if com uma condição que se
resultar em verdadeiro executará o comando break e, como consequência, encerrará o
laço. Veja na Codificação 8.5 um exemplo de solução com essa estrutura para o
problema a seguir: crie uma função que receba como argumento um número natural e
exiba seus dígitos na ordem do último para o primeiro.

def invertido(n):
while True:
print(n % 10, end='')
n = n // 10
if n == 0: break

Codificação 8.5: Exemplo de aplicação do laço repeat…until.

🏹 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.

8.5. Validação de dados de entrada


Quando os programas solicitam entradas ao usuário, é comum que ocorram
inconsistências entre os dados esperados e aquilo que é inserido, erros cometidos pelo
usuário propositalmente ou não. Quando é fornecido um dado inesperado, o programa
pode falhar, encerrando inesperadamente ou produzindo resultado inconsistente. Para
minimizar esse tipo de ocorrência, é útil realizar validações de dados de entrada.
Geralmente, validações de entrada são feitas com loops, solicitando um valor
para o usuário repetidamente, até que seja inserido algum válido de acordo com o
esperado pelo algoritmo que é estipulado pelo programador. Existem várias abordagens,
uma das mais simples é com a estrutura repeat…until. Veja na Codificação 8.6 a solução
para o seguinte problema: “crie um programa que receba como entrada um número
natural n no intervalo [0..100], e exiba a soma dos n primeiros naturais positivos. A
soma deverá ser feita por uma função, que a devolverá como resposta. Note que o
programa deverá validar a entrada e só chamará a função quando n for válido”.

Núcleo de Educação a Distância | Faculdade Impacta


Linguagem de Programação

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

print(f'A soma dos {n} primeiros naturais é {somatorio(n)}.')

Codificação 8.6: Programa com validação de dados de entrada usando repeat…until.

Note que na Codificação 8.6 a chamada à função somatorio só ocorrerá caso o


fluxo de execução ultrapasse a validação de entrada feita com o laço while (usado para
simular a estrutura repeat…until). Portanto, o programa impede a ocorrência de um erro
na função somatorio caso seja inserido um valor negativo na entrada, pois se isso
ocorrer, será solicitado outro valor de entrada. A Codificação 8.7 exibe uma versão
alternativa, em que a estrutura de repetição repeat…until é trocada por um laço while
convencional, note que apenas o trecho alterado está escrito.

...
n = int(input('Natural entre 0 e 100: '))

while n < 0 or n > 100:


n = int(input('Número inválido, digite novamente: '))
...
Codificação 8.7: Programa com validação de dados de entrada usando while tradicional.

Uma vantagem desta abordagem é que podemos personalizar a mensagem


exibida quando o usuário digita um valor incorreto, no entanto, temos a desvantagem de
precisar inserir duas vezes a instrução que pede o dado ao usuário, algo que pode ser
evitado quando a personalização não é necessária.

Na Codificação 8.7, a condição do while é complementar à Codificação 8.6,


pois na estrutura repeat..until a estratégia é “repita algo até que a condição seja
atendida”, e a condição é o número ser válido. Já na estrutura tradicional do while, a
estratégia é “repita enquanto a condição for atendida”, logo o laço só deve ser repetido
enquanto o número for inválido, que é condição complementar à de número válido.

Núcleo de Educação a Distância | Faculdade Impacta


Linguagem de Programação

Para obter a condição complementar à utilizada na Codificação 8.6, podemos


simplesmente negá-la com o operador not, ou simplificá-la a negação, como a seguir:

not (0 <= n <= 100) ⇒ not (0 <= n and n <= 100)


⇒ not(0 <= n) or not(n <= 100) ⇒ 0 > n or n > 100
⇒ n < 0 or n > 100
Todas as expressões do exemplo acima são equivalentes, então podemos escolher
qualquer uma como condição para o laço de validação que o resultado será exatamente o
mesmo. Consequentemente, podemos escolher aquela que fizer mais sentido para nós.

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

while not n_valido:


n = int(input('Natural entre 0 e 100: '))
if 0 <= n <= 100:
n_valido = True
...
Codificação 8.8: Programa com validação de dados de entrada usando flag booleana.

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).

Núcleo de Educação a Distância | Faculdade Impacta


Linguagem de Programação

8.6. Estruturas de repetição aninhadas


Há problemas complexos que exigem soluções contendo estruturas de repetição
aninhadas, ou seja, um loop dentro do bloco de código de outro loop. Veja na Figura 8.4
um trecho de fluxograma em que há um laço aninhado e a correspondência em Python.

Figura 8.4: Laço aninhado em fluxograma e em Python. Fonte: Elaborado pelo autor.

Para compreender a necessidade do uso de laços aninhados, começaremos por


um problema bastante simples que evoluirá seu nível de complexidade à medida em que
surgirem mais exigências no enunciado. Criaremos um relógio!
De início, a função que simulará um relógio é dada pelo enunciado: “Crie uma
função que exibe todos os segundos de um minuto. Lembre-se que o primeiro segundo é
0 e o último é 59.”. A Codificação 8.9 é uma solução válida para esse enunciado.
def relogio():
s = 0
while s < 60:
print(s)
s += 1
Codificação 8.9: Primeira versão da função que simula um relógio.

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.

Núcleo de Educação a Distância | Faculdade Impacta


Linguagem de Programação

from time import sleep

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.

Agora ampliaremos nosso relógio conforme o enunciado: “Altere a função para


que a cada 60 segundos o mostrador de minutos seja incrementado e o de segundos
zerado. Repita o procedimento até completar 00:59:59”. Precisaremos usar um loop
aninhado, pois a cada um minuto executaremos o loop de segundos completamente,
lembre-se que temos 60 minutos! A Codificação 8.11 uma solução válida 1.

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.

Vamos ao próximo enunciado: “Altere a função para que a cada 60 minutos o


mostrador de horas seja incrementado e o de minutos zerado. Repita o procedimento
até completar 23:59:59”. Agora precisaremos de mais um loop para as horas, veja que
com isso teremos três níveis de repetição! A Codificação 8.12 é uma solução válida.

E, em nossa última atualização da função, seguiremos o enunciado: “Altere a


função para que a cada 24 horas os três mostradores sejam zerados e o relógio reinicie
a contagem.”. Novamente, precisaremos de mais um loop em que todos os anteriores
estarão contidos, porém agora será um laço infinito para que o relógio nunca pare. A
Codificação 8.13 é uma solução válida para esse novo enunciado.

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).

Núcleo de Educação a Distância | Faculdade Impacta


Linguagem de Programação

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).

Núcleo de Educação a Distância | Faculdade Impacta


Linguagem de Programação

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.

Núcleo de Educação a Distância | Faculdade Impacta


Linguagem de Programaçã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.
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.

Núcleo de Educação a Distância | Faculdade Impacta


Proceedings of the XII SIBGRAPI (October 1999) 101-104
Linguagem de Programação

Texto base

9
Sequências e estrutura de repetição definida (for)

Prof. Me. Lucio Nunes de Lira


Prof. MSc. Rafael Maximo Carreira Ribeiro

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

Núcleo de Educação a Distância | Faculdade Impacta


Linguagem de Programação

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.

Núcleo de Educação a Distância | Faculdade Impacta


Linguagem de Programação

Para acessar um item específico da sequência basta indexá-la adequadamente


com a posição em que está o item desejado. A indexação é feita colocando o índice
desejado entre colchetes, após o nome da variável. Por exemplo, utilizando a sequência
ilustrada na Figura 9.1, podemos acessar o quarto item, que corresponde ao número 80,
tanto com a instrução idades[3] quanto com a instrução idades[-2]. A escolha de
qual índice usar dependerá do algoritmo, o uso do índice natural é mais comum, mas
quando, por algum motivo, for necessário acessar o penúltimo item, é mais fácil usar o
índice -2, pois torna desnecessário conhecer o tamanho da sequência.
Em Python, há um hábito de que erros não devem ocorrer silenciosamente, pois
isso pode acarretar comportamentos inesperados e difíceis de prever, então se uma
sequência for indexada com índice inválido, isto é, inexistente na sequência, será gerado
um erro de execução.
O Python possui diversas funções e métodos que nos ajudam a trabalhar com
sequências, por exemplo a função len, que recebe como argumento uma sequência e
devolve um número natural indicando o tamanho desta sequência. Com base na Figura
9.1, ao executar a instrução len(idades), obtemos como retorno o valor 5. Neste
capítulo não focaremos nessas funções e métodos, porém aprenderemos sobre as
características e conceitos associados aos diferentes tipos de sequências do Python.

9.3.1. Sequências mutáveis e imutáveis


Existem sequências mutáveis e imutáveis. Nas sequências mutáveis cada item se
comporta de modo semelhante a uma variável comum, portanto pode receber
atribuições, sobrescrevendo seus valores iniciais. Nas sequências imutáveis, uma vez
definida a sequência, seus itens não podem mais ser modificados. Veja na Figura 9.2
uma atribuição de valor a um item de uma sequência mutável e sua consequente
mudança de estado em decorrência desta operação.

Figura 9.2: Modificação de item de uma sequência mutável. Fonte: Elaborado pelo autor.

Núcleo de Educação a Distância | Faculdade Impacta


Linguagem de Programação

Na Figura 9.3, há uma tentativa de atribuição de valor a um item de uma


sequência imutável, o que irá gerar um erro por ser uma operação inválida.

Figura 9.3: Tentativa de modificação de item de uma sequência imutá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.

Figura 9.4: Modificação da variável que referencia uma sequência.


Fonte: Elaborado pelo autor.

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.

Núcleo de Educação a Distância | Faculdade Impacta


Linguagem de Programação

9.3.2. Sequências homogêneas e heterogêneas


Existem sequências homogêneas e sequências potencialmente heterogêneas,
característica que indica a flexibilidade em armazenar itens de tipos distintos
simultaneamente. Em sequências homogêneas, todos os itens devem ser do mesmo tipo,
portanto, caso um item seja um número inteiro, todos os demais deverão ser números
inteiros, caso seja um caractere, todos os demais também serão caracteres, e assim por
diante. Em sequências potencialmente heterogêneas os itens podem ser de tipos
distintos, logo uma mesma sequência pode conter itens float, int, bool etc. A Figura 9.5
ilustra esse conceito.

Figura 9.5: Exemplos de sequências homogênea e heterogênea.


Fonte: Elaborado pelo autor.

9.3.3. Tipos de sequências em Python


O Python possui três tipos básicos de sequências: listas (list), tuplas (tuple) e
intervalos (range). Também há um tipo de sequência específica para armazenar
caracteres (strings) e outros tipos de sequência dedicadas para manipulação de dados
binários. Nesta aula abordaremos os tipos string, lista, tupla e intervalo.

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:

1) Pares de apóstrofos: 'a casa amarela';


2) Pares de aspas: "a casa amarela";
3) Pares de três apóstrofos ou aspas, permitindo que tenham múltiplas linhas:
'''a casa amarela''' ou """a casa amarela""";
4) Com o construtor de tipo str(), que permite também a conversão de outros
tipos de dados para string.

Núcleo de Educação a Distância | Faculdade Impacta


Linguagem de Programação

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:

1) Apenas um par de colchetes, para indicar lista vazia: [];


2) Itens separados por vírgulas entre um par de colchetes: [4, True, 1.5];
3) Com compreensão/abrangência de lista (não será abordado por enquanto);
4) Com o construtor de tipo list(), que pode ser usado também para
converter outros tipos de sequências em listas.

Figura 9.7: Exemplos de operações com lista. Fonte: Elaborado pelo autor.

Núcleo de Educação a Distância | Faculdade Impacta


Linguagem de Programação

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:

1) Apenas um par de parênteses para indicar tupla vazia: ();


2) Uma vírgula à direita, indicando um tupla de só um item: 7, ou (7,);
3) Itens separados por vírgulas: 4, True, 1.5 ou (4, True, 1.5);
4) Com o construtor de tipo tuple(), que pode ser usado também para
converter outros tipos de sequências em tuplas.

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:

1) Três argumentos: range(início, fim, passo), gera uma sequência


com itens no intervalo [início..fim[, variando de passo em passo;
2) Dois argumentos: range(início, fim), gera uma sequência crescente
com itens no intervalo [início..fim[, variando de 1 em 1;

Núcleo de Educação a Distância | Faculdade Impacta


Linguagem de Programação

3) Um argumento: range(fim): gera uma sequência crescente com itens no


intervalo [0..fim[, variando de 1 em 1.

Observe algumas implicações da forma como sequências range funcionam:

a) O intervalo é aberto à direita, portanto fim não pertence à sequência;


b) Dependendo de como o construtor é especificado, pode-se gerar um intervalo
vazio, por exemplo em range(10, 10), pois não existem inteiros no
intervalo [10..10[. Ficou em dúvida? Isso equivale a solicitar um intervalo
que comece em 10 e termine antes de 10;
c) No construtor com três argumentos, range(início, fim, passo), se
passo for um número negativo, a sequência será decrescente, desde que
início seja maior do que fim, caso contrário será gerado um intervalo
vazio. Logicamente, passo zero é inválido;
d) Podemos interpretar range como uma função que retorna um intervalo com
base em uma progressão aritmética cuja razão entre os termos é o passo.

Veja alguns exemplos de manipulação desse tipo de sequência na Figura 9.9.

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

Núcleo de Educação a Distância | Faculdade Impacta


Linguagem de Programação

9.3.4. Percorrendo sequências


Percorrer, iterar ou “varrer” uma sequência equivale a acessar sistematicamente
diversos de seus itens seguindo alguma ordem. Frequentemente, percorre-se uma
sequência para acessar seus itens visando exibição, alteração ou utilização dos valores.
Percursos em sequências costumam ser feitos com uso de laços de repetição.
Para entender a necessidade das sequências e a razão de percorrê-las, pensaremos
sobre o seguinte enunciado: “Crie um programa que receba como entrada quatro
salários, exiba a média salarial e os salários abaixo da média”. A Codificação 9.1
contém uma solução válida para esse problema, porém sem o uso de sequências.
soma = 0

salario_0 = float(input('Salário: R$ '))


soma += salario_0

salario_1 = float(input('Salário: R$ '))


soma += salario_1

salario_2 = float(input('Salário: R$ '))


soma += salario_2

salario_3 = float(input('Salário: R$ '))


soma += salario_3

media = soma / 4
print(f'Média = R$ {media:.2f}')

if salario_0 < media:


print(f'Abaixo da média: R$ {salario_0:.2f}')
if salario_1 < media:
print(f'Abaixo da média: R$ {salario_1:.2f}')
if salario_2 < media:
print(f'Abaixo da média: R$ {salario_2:.2f}')
if salario_3 < media:
print(f'Abaixo da média: R$ {salario_3:.2f}')
Codificação 9.1: Programa que exibe os salários abaixo da média (sem sequências).

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.

Núcleo de Educação a Distância | Faculdade Impacta


Linguagem de Programação

salarios = [0, 0, 0, 0]
soma = 0

salarios[0] = float(input('Salário: R$ '))


soma += salarios[0]

salarios[1] = float(input('Salário: R$ '))


soma += salarios[1]

salarios[2] = float(input('Salário: R$ '))


soma += salarios[2]

salarios[3] = float(input('Salário: R$ '))


soma += salarios[3]

media = soma / 4
print(f'Média = R$ {media:.2f}')

if salarios[0] < media:


print(f'Abaixo da média: R$ {salarios[0]:.2f}')
if salarios[1] < media:
print(f'Abaixo da média: R$ {salarios[1]:.2f}')
if salarios[2] < media:
print(f'Abaixo da média: R$ {salarios[2]:.2f}')
if salarios[3] < media:
print(f'Abaixo da média: R$ {salarios[3]:.2f}')
Codificação 9.2: Programa que exibe os salários abaixo da média (com uso de lista).

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.

Núcleo de Educação a Distância | Faculdade Impacta


Linguagem de Programação

salarios = [0, 0, 0, 0]
soma = 0

i = 0 # variável que será usada como índice.


while i < 4:
salarios[i] = float(input('Salário: R$ '))
soma += salarios[i]
i += 1

media = soma / 4
print(f'Média = R$ {media:.2f}')

i = 0 # variável que será usada como índice.


while i < 4:
if salarios[i] < media:
print(f'Abaixo da média: R$ {salarios[i]:.2f}')
i += 1
Codificação 9.3: Programa que exibe os salários abaixo da média (com while).

Assim, reduziu-se o código e, principalmente, o programa tornou-se mais


flexível, pois caso seja necessário armazenar mais salários, basta realizar pequenas
mudanças na codificação, algo potencialmente inviável se estivéssemos limitados às
variáveis simples ou sem laços para percorrer sequências.
Agora nossos programas podem trabalhar com grandes quantidades de dados
com o uso de sequências e laços de repetição para percorrê-las. No entanto, a linguagem
Python possui um segundo loop que pode tornar nossos códigos ainda mais concisos e
elegantes, principalmente ao lidarmos com sequências, é a estrutura de repetição for.

9.4. Estrutura de repetição for


Python tem uma estrutura de repetição com quantidade de repetições definida, o
for. Geralmente, esse laço é usado quando sabemos antecipadamente a quantidade de
vezes que o bloco de código deve ser repetido, ou para percorrer sequências.

Em muitas linguagens, for é “atalho” para um while com número pré-definido


de iterações, agrupando em uma mesma linha as três partes que controlam o laço: (I)
inicialização da variável de controle; (II) condição de repetição e (III) incremento da
variável de controle. Na Codificação 9.4 há um exemplo deste tipo de laço for.

Núcleo de Educação a Distância | Faculdade Impacta


Linguagem de Programação

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.

for <variável> in <sequência>:


<bloco de código>
Codificação 9.5: Estrutura do laço de repetição for do Python.

Simplificadamente, podemos entender <sequência> como uma sequência de


qualquer tipo e tamanho, até vazia. Já <variável> é uma variável de controle que, caso
não exista previamente, será criada no início da execução do laço, se a sequência não for
vazia, pois o primeiro item será atribuído a ela dando início à primeira execução do
<bloco de código>. Caso exista variável com identificador igual à <variável>,
seu valor será sobrescrito pela atribuição do primeiro item de <sequência>.
O for executa seu bloco de código uma vez para cada item de <sequência>
atribuído à <variável>, tornando a quantidade de rodadas previsível. Não é
recomendado alterar <sequência> durante a execução do for, pois pode gerar efeitos
inesperados. A Figura 9.10 ilustra o comportamento deste laço.

Figura 9.10: Representação em fluxograma da estrutura de repetição for.


Fonte: Elaborado pelo autor.

Núcleo de Educação a Distância | Faculdade Impacta


Linguagem de Programação

Reescreveremos a Codificação 9.3 usando for. Usaremos também um método


muito requisitado quando trabalhamos com listas: o append(), que anexa ao final da
própria lista um novo item. Assim, é desnecessário que a lista inicie já com todas as
posições necessárias2, deixando o Python gerenciar a alocação de memória da forma que
ele julgar mais eficiente. Veja na Codificação 9.6 uma possível solução com for.

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}')

for salario in salarios:


if salario < media:
print(f'Abaixo da média: R$ {salario:.2f}')
Codificação 9.6: Programa que exibe os salários abaixo da média (com for).

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.

No primeiro laço, usamos como identificador da variável de controle apenas um


sublinhado (_), o que pode parecer estranho a princípio, porém indica ao leitor do
código que não há interesse nos valores assumidos por essa variável. Poderíamos ter
usado qualquer outro nome válido, mas esse identificador é comum quando o objetivo
do laço é apenas ser executado um número fixo de vezes, sem pretensão de usar os
valores da sequência atribuídos à variável de controle.

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.

Núcleo de Educação a Distância | Faculdade Impacta


Linguagem de Programação

No segundo laço, percorremos a lista de salários, independente do seu tamanho,


e os valores dos itens serão usados para algo, no caso uma exibição condicionada a uma
verificação. Portanto, foi adotado um identificador que faz referência ao conteúdo da
lista, só que agora no singular, pois está representando apenas um item da lista por vez.
Essas convenções adotadas na Codificação 9.6 não são obrigatórias, mas
facilitam a leitura do código ao dar maior expressividade às instruções, tornando nosso
trabalho como programadores mais fácil e agradável.

9.4.1. Exemplos de aplicação do laço for


Para melhorar a compreensão sobre o laço de repetiçao for, teste os programas
das Codificações 9.7 a 9.10.

# Crie um programa que exiba o alfabeto minúsculo e maiúsculo.

for letra in 'abcdefghijklmnopqrstuvwxyz':


print(letra)

print()

for letra in 'ABCDEFGHIJKLMNOPQRSTUVWXYZ':


print(letra)
Codificação 9.7: Programa que exibe as letras do alfabeto (com for).

# Crie um programa que leia cinco nomes e exiba a quantidade de


# nomes que começam com vogal.

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(f'{qtd} dos nomes começam com vogal')


Codificação 9.8: Programa que conta nomes que começam com vogal (com for).

Núcleo de Educação a Distância | Faculdade Impacta


Linguagem de Programação

# Crie um programa que exiba os sete dias da semana.

dias = ('domingo', 'segunda', 'terça', 'quarta', 'quinta',


'sexta', 'sábado')
for dia in dias:
print(dia)
Codificação 9.9: Programa que exibe os setes dias da semana (com for).

# Crie um programa que exiba, em ordem crescente, os pares de 10


# até 100 e, em ordem decrescente, os ímpares de 100 até 10.

for par in range(10, 101, 2):


print(par, end=' ')

print()

for impar in range(99, 10, -2):


print(impar, end=' ')
Codificação 9.10: Programa que exibe os pares e ímpares de um intervalo (com for).

🏹 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

Núcleo de Educação a Distância | Faculdade Impacta


Linguagem de Programação

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.

Núcleo de Educação a Distância | Faculdade Impacta


Proceedings of the XII SIBGRAPI (October 1999) 101-104
Linguagem de Programação

Texto base

10
Operações básicas com sequências

Prof. Me. Lucio Nunes de Lira


Prof. MSc. Rafael Maximo Carreira Ribeiro

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.

Núcleo de Educação a Distância | Faculdade Impacta


Linguagem de Programação

10.3. Métodos e objetos em Python


No início de nossos estudos, aprendemos que a linguagem Python é
multiparadigma, isto é, pode trabalhar simultaneamente com diferentes paradigmas de
programação, como procedural, funcional e orientado a objetos. Também foram
abordados os diferentes tipos de dados (int, float, bool, string, etc.), mas não havíamos
abordado que em Python tudo é um objeto, desde números até listas e funções.
Esse conceito é importante para entendermos duas coisas: 1) o que são métodos
e; 2) como objetos se comportam quando passados como argumentos para funções.
Podemos pensar em um objeto como uma entidade que agrupa características e
comportamentos, e que tem consciência de si mesmo. Os comportamentos são o que
chamamos de métodos, e são análogos às funções. Por hora, aprenderemos apenas sobre
os objetos que existem por padrão em Python. O que precisamos saber sobre o assunto é:
● Cada objeto possui uma identidade única em Python, como um RG ou CPF;
● Podemos usar os métodos de um objeto para acessar ou alterar suas
características, dependendo do tipo do objeto.
É possível obter a identidade de um objeto usando a função integrada id, que
retorna um número inteiro representando a identidade do objeto. Python garante que esse
valor será único enquanto o objeto existir na memória. Na implementação padrão do
Python (CPython), esse valor representa o endereço do objeto na memória (PSF, 2021a).
Os métodos em Python podem ser acessados a partir do objeto usando a notação
de ponto: objeto.metodo(). Talvez você se recorde do método format, que pode ser
usado para formatar uma string de modo semelhante as f-strings. Na Codificação 10.1,
há um exemplo de uso deste método e da função id.
>>> texto = 'Olá {}!'
>>> id(texto)
3124235308912
>>> texto
'Olá {}!'
>>> texto.format('mundo')
'Olá mundo!'

Codificação 10.1: Exemplo de utilização da função id e do método format.

Usamos a função integrada id para obter a identidade do objeto string passado


como argumento e, em seguida, o método format para acessar o conteúdo desse objeto,
através da referência a ele que foi salva na variável texto, e retornar uma nova string
formatada, que foi exibida na Shell. Note que há criação de uma nova string, pois
strings são objetos imutáveis, isto é, depois de criadas na memória, não podem ser
alteradas.

Núcleo de Educação a Distância | Faculdade Impacta


Linguagem de Programação

Confirmamos que a variável texto continua referenciando a string inicial


inspecionando a identidade de seu conteúdo, assim como é possível verificar que o objeto
não foi modificado por format visualizando o próprio conteúdo, que continua o mesmo.
>>> id(texto)
3124235308912
>>> texto
'Olá {}!'

Codificação 10.2: Visualização do objeto de string inalterado.

Refaça a Codificação 10.1, porém atribuindo o valor de retorno de format a


outra variável. Aproveite para verificar a identidade do conteúdo da nova variável.
Sabemos que listas são objetos mutáveis, logo suas características podem ser
alteradas no mesmo local da memória, sem a necessidade de gerar um novo objeto,
diferente da string da Codificação 10.1. Por ser mutável, podemos modificar seus itens,
ou usar o método append para incluir um novo item ao final da lista. Em ambas as
situações, podemos verificar que a lista continua o mesmo objeto na memória, apenas
com valores diferentes, ou seja, alteramos suas características, mas não sua identidade.
Esse comportamento pode ser visto na Codificação 10.3.
>>> lista = [1, 2, 3]
>>> lista
[1, 2, 3]
>>> id(lista)
3124235308003
>>> lista[0] = 25
>>> lista
[25, 2, 3]
>>> lista.append(4)
>>> lista
[25, 2, 3, 4]
>>> id(lista)
3124235308003

Codificação 10.3: Criação, alterações e inspeções em um objeto list.

10.4. Operações comuns de sequências


As operações comuns de sequências (PSF, 2021b) são aquelas que podem ser
aplicadas a todos os tipos de sequência do Python, independentemente de serem
mutáveis ou não. Portanto, as operações desse grupo se aplicam as listas, tuplas,
intervalos e strings, e podem ser agrupadas da seguinte forma:

Núcleo de Educação a Distância | Faculdade Impacta


Linguagem de Programação

● 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

Codificação 10.4: Utilização do operador de pertencimento.

E para strings, há um funcionamento especial deste operador, que pode ser


usado também para testes de subsequências. Veja na Codificação 10.5.
>>> 'ana' in 'banana'
True
>>> 'ara' not in 'Araraquara'
False

Codificação 10.5: Utilização do operador de pertencimento em strings.

10.4.2. Concatenação e repetição


A concatenação é feita com o operador + e a repetição com o operador *.
Devido à natureza dos intervalos (range), estas operações não são aplicáveis a eles. Veja
na Codificação 10.6 e Codificação 10.7 exemplos de uso desses operadores.
>>> (1, 2) + (3, 4)
(1, 2, 3, 4)
>>> 'abc' + 'xyz'
'abcxyz'
>>> [30, 40] + [10, 20, 50]
[30, 40, 10, 20, 50]

Codificação 10.6: Exemplos de concatenação de sequências.

Núcleo de Educação a Distância | Faculdade Impacta


Linguagem de Programação

>>> 3 * 'Abc'
'AbcAbcAbc'
>>> 5 * [0]
[0, 0, 0, 0, 0]
>>> 4 * (123,)
(123, 123, 123, 123)

Codificação 10.7: Exemplos de repetição de sequências.

A concatenação e a repetição não alteram os objetos, geram um novo. Portanto,


não é a forma mais eficiente para adicionar itens a uma sequência. Uma abordagem
melhor é usar o método append em uma lista, e em seguida, convertê-la para outro tipo
de sequência, se necessário.
Tanto a concatenação quanto à repetição passam para a nova sequência as
referências aos itens das sequências iniciais, e não uma cópia dos objetos em si. Isso não
é um problema quando os itens são imutáveis, mas pode surpreender quem está
começando a programar em Python quando os itens são listas. Veja a Codificação 10.8.
>>> s = [[2]] * 3
>>> s
[[2], [2], [2]]

Codificação 10.8: Repetição de uma lista aninhada.

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]]

Codificação 10.9: Alteração de um item de uma das listas internas.

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.

10.4.3. Indexação e fatiamento


A indexação é a forma como acessamos um item de uma sequência, e é feita
sempre com o uso de colchetes. Os índices devem obrigatoriamente ser números inteiros
e, como já vimos, há dois tipos de índices: os naturais e os negativos. Recordemos por
meio da Figura 10.1 como Python define a indexação dos itens de uma sequência e com
a Codificação 10.10 como acessar itens específicos de uma sequência.

Núcleo de Educação a Distância | Faculdade Impacta


Linguagem de Programação

Figura 10.1: Visualização dos índices de uma lista de strings em Python.


Fonte: Elaborado pelo autor.

>>> texto = 'uma frase qualquer'


>>> texto[4]
'f'
>>> intervalo = range(10, 20)
>>> intervalo[3]
13
>>> tupla = (3.1, 5.8, 7.0)
>>> tupla[0]
3.1

Codificação 10.10: Indexação de sequências.

A indexação funciona tanto para acessar um item de uma sequência referenciada


por uma variável quanto para acessar o item diretamente da sequência literal, como
podemos visualizar na Codificação 10.11.
>>> 'abc'[0]
'a'
>>> range(2, 5)[1]
3
>>> [40, 50, 60][2]
60

Codificação 10.11: Indexação de sequências literais.

Além de acessar um único item de uma sequência, o Python também permite o


que chamamos de fatiamento, quando definimos um intervalo de índices entre colchetes
para obter uma “fatia” da sequência, ou seja, uma subsequência.
E, para realizar o fatiamento, devemos passar os índices separados por dois
pontos entre os colchetes, seguindo uma notação semelhante à usada em intervalos
(range): sequencia[início:fim:passo]. A Codificação 10.12 contém exemplos
de fatiamento com listas, porém se aplicam aos demais tipos de sequências igualmente.
Um fatiamento não altera a sequência fatiada, apenas gera uma nova sequência
do mesmo tipo da original e contendo apenas os itens (uma cópia das referências a eles)
conforme a definição dada. Observe que, assim como na criação de intervalos, o item
correspondente ao índice fim não será incluído no fatiamento.

Núcleo de Educação a Distância | Faculdade Impacta


Linguagem de Programação

>>> 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]

Codificação 10.12: Fatiamento de sequências.

10.4.4. Tamanho, soma, mínimo e máximo


Python disponibiliza quatro funções integradas bastante úteis para lidar com
sequências, veja na Tabela 10.1 quais são e alguns exemplos na Codificação 10.13.

Tabela 10.1: Funções integradas len, sum, min e max.

Função Descrição

len(s) Retorna o comprimento (tamanho) da sequência s.

sum(s) Retorna a soma dos itens da sequência s.

min(s) Retorna o item de valor mínimo da sequência s.

max(s) Retorna o item de valor máximo da sequência s.

>>> 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).

Núcleo de Educação a Distância | Faculdade Impacta


Linguagem de Programação

10.4.5. Busca por valor


Para buscar o índice de um valor em uma sequência, podemos usar o método
index, que recebe como argumento o valor buscado e retorna a posição da primeira
ocorrência encontrada. Caso não encontre, levanta um erro de valor, indicando que o
valor não foi encontrado. Veja exemplos na Codificação 10.14.
>>> texto = 'Python Para Pessoas!'
>>> texto.index('P')
0
>>> texto.index('p')
(...)
ValueError: substring not found2

Codificação 10.14: Utilização do método index.

10.4.6. Contagem de ocorrências


Para contar quantas vezes um valor ocorre em uma sequência, usamos o método
count, que recebe o valor buscado e percorre a sequência inteira, retornando no final
quantas vezes aquele valor foi encontrado. Veja exemplos na Codificação 10.15.
>>> lista = [1, 2, 3, 41, 5, 7, 3, 41, 41]
>>> lista.count(41)
3
>>> lista.count(200)
0

Codificação 10.15: Utilização do método count.

10.5. Laços implícitos


Você deve ter reparado que várias funções e métodos aplicados em sequências
precisam, para executar corretamente sua funcionalidade, percorrer a sequência. Isso
evidencia que há um laço implícito, que são invisíveis por não acessarmos diretamente o
código dessas funções e métodos, apenas a usamos. Logo, são operações custosas!
Note que esse custo de percorrer a sequência não ocorre em operações onde um
item da sequência é acessado diretamente como, por exemplo, quando um índice é
passado para uma função/método indicando a exata e única posição que será acessada.
Para problemas simples, executar algumas operações com laços implícitos não é
tão relevante, mas quando trabalhamos com sequências grandes ou quando essas
operações são repetidas diversas vezes, esse descuido pode acarretar em um programa
que consome recursos desnecessariamente, como tempo de processamento e memória.

2
O texto na mensagem de erro varia de acordo com o tipo de sequência.

Núcleo de Educação a Distância | Faculdade Impacta


Linguagem de Programação

10.6. Operações de sequências mutáveis


Além das operações comuns, nas sequências mutáveis existem mais operações
possíveis (PSF, 2021c):
● Substituição de itens da sequência;
● Inclusão de itens na sequência;
● Remoção de itens da sequência;
● Inversão da sequência.
Das sequências que estudamos até agora, as operações desse tópico se aplicam
apenas às listas. É importante observar que estes métodos operam por efeito colateral,
isto é, eles alteram o objeto da sequência internamente e não retornam valor útil.

10.6.1. Substituição de itens da sequência


A substituição de itens em uma sequência é feita por meio da atribuição direta à
uma de suas posições. Veja exemplos na Codificação 10.16.
>>> lista = [41, 5, 7]
>>> lista[1] = 200
>>> lista
[41, 200, 7]

Codificação 10.16: Alteração de um item em uma sequência mutável.

10.6.2. Inclusão de itens na sequência


A inclusão de itens em uma sequência pode ser feita de três formas, cada uma
mais adequada a depender da localização e quantidade de itens que devem ser incluídos.
● s.insert(i, x) - insere x como item na posição i da sequência s;
● s.append(x) - adiciona x como item no final da sequência s;
● s.extend(t) - adiciona, um a um, os itens da sequência t ao final de s.
Note na Codificação 10.17 que o inteiro 25 foi incluído na posição 1, deslocando
o antigo item desta posição, e os demais itens sucessores, uma posição para a direita.
>>> lista = [5, 3, 78]
>>> lista.insert(1, 25)
>>> lista
[5, 25, 3, 78]

Codificação 10.17: Inserção se um item em uma posição específica da 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.

Núcleo de Educação a Distância | Faculdade Impacta


Linguagem de Programação

>>> lista = [15, 10, 99]


>>> lista.append(100)
>>> lista
[15, 10, 99, 100]
>>> lista.append([1, 2, 3])
[15, 10, 99, 100, [1, 2, 3]]

Codificação 10.18: Adição de um item ao final da sequência.

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.

10.6.3. Remoção de itens da sequência


Há três formas de um item ser removido de uma sequência:
● del s[i] - exclui o item de índice i na sequência s;
● s.pop(i) - exclui o item de índice i na sequência s, e retorna seu valor;
● s.remove(x) - remove o primeiro item de valor x da sequência s.
Veja na Codificação 10.20 o funcionamento do comando del.
>>> lista = [15, 10, 99]
>>> del lista[2]
>>> lista
[15, 10]

Codificação 10.20: Exclusão de item de uma sequência com o comando del.

Observe na Codificação 10.21 que, diferentemente do comando del, o método


pop retorna o item excluído da lista, que é exibido na Shell do Python.
>>> lista = [15, 10, 99]
>>> lista.pop(0)
15
>>> lista
[10, 99]

Codificação 10.21: Exclusão de item de uma sequência com método pop.

E na Codificação 10.22, usamos o método remove para buscar e remover um


item em função do seu valor.

Núcleo de Educação a Distância | Faculdade Impacta


Linguagem de Programação

>>> lista = [15, 10, 99]


>>> lista.remove(10)
>>> lista
[15, 99]

Codificação 10.22: Exclusão de itens de uma sequência com método remove.

É 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.

10.6.4. Inversão da sequência


Para inverter os itens de uma sequência alterando os dados no lugar, isto é, no
próprio objeto, usamos o método reverse, como mostrado na Codificação 10.23.
>>> lista = [10, 20, 30, 40]
>>> lista.reverse()
>>> lista
[40, 30, 20, 10]

Codificação 10.23: Inversão de uma sequência com o método reverse.

10.7. Operações comuns em strings


As strings, assim como as listas e tuplas, possuem seu próprio conjunto de
métodos. A lista completa pode ser vista na documentação do Python (PSF, 2021d),
aqui veremos apenas os dois métodos para converter strings em listas e vice-versa.
Para separar uma string em uma lista de strings usamos o método split, que
recebe um separador (string) e retorna uma lista, como mostrado na Codificação 10.24.
>>> '1;2;3;4;5'.split(';')
['1', '2', '3', '4', '5']

Codificação 10.24: Utilização do método split com um argumento.

Caso o método seja chamado sem argumentos, irá apresentar um comportamento


especial, separando a string em todos os caracteres que representam espaços em branco
(espaço, tabulação e quebra de linha) e eliminando espaços em branco consecutivos.
Observe na Codificação 10.25 que ao passarmos um espaço em branco como
argumento, Python coloca uma string vazia entre cada espaço adjacente na string, mas
quando não passamos argumento, ele automaticamente desconsidera os espaços extras.

Núcleo de Educação a Distância | Faculdade Impacta


Linguagem de Programação

>>> 'frase com espaços '.split(' ')


['frase', '', 'com', '', '', '', '', 'espaços', '', '']
>>> 'frase com espaços '.split()
['frase', 'com', 'espaços']

Codificação 10.25: Utilização do método split sem argumentos.

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.

10.8. Desempacotamento de sequências


O desempacotamento ocorre quando os itens de uma sequência são atribuídos a
diversas variáveis em uma única atribuição. Por isso, também é uma operação conhecida
como atribuição paralela. Há na Codificação 10.27 exemplos de desempacotamento.
>>> x, y, z = [15, 10, 99]
>>> print(f'x = {x} | y = {y} | z = {z}')
x = 15 | y = 10 | z = 99
>>> a, b, c = (10, 'Impacta', True)
>>> print(f'a = {a} | b = {b} | c = {c}')
a = 10 | b = Impacta | c = True

Codificação 10.27: Desempacotamento de uma sequência em variáveis.

O desempacotamento também funciona com os demais tipos de sequência que


vimos, como tuplas, intervalos e strings. Será gerado um erro de execução se o número
de variáveis à esquerda for diferente do número de itens à direita.
Uma forma simples de realizar a leitura de diversos valores dispostos em uma
única linha é com a combinação do método split e do desempacotamento.
nome, idade, peso = input().split() # desempacotamento de lista.
idade, peso = int(idade), float(peso) # desempacotamento de tupla.

Codificação 10.28: Leitura de diversos valores dispostos em apenas uma linha.

Núcleo de Educação a Distância | Faculdade Impacta


Linguagem de Programação

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}')

lista = [1, 'banana', True]


teste(*lista)

Codificação 10.29: Desempacotamento para parâmetros de uma função.

O desempacotamento de lista na chamada à teste equivale à Codificação


10.30, em que cada item da lista é passado individualmente como argumento.
teste(lista[0], lista[1], lista[2])

Codificação 10.30: Equivalência ao desempacotamento da Codificação 10.29.

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)

10.9. Tipos de passagem de argumentos


Ao chamar uma função é comum que existam duas alternativas para como os
argumentos serão passados: (1) passando o valor diretamente, desta forma o parâmetro
da função receberá uma cópia do valor original ou (2) passando uma referência para o
local da memória onde o valor está, assim o parâmetro da função receberá o endereço
do valor original. Na Figura 10.2 há uma representação simplificada de como um
programa “enxerga” as variáveis criadas na memória com base na Codificação 10.31.

Núcleo de Educação a Distância | Faculdade Impacta


Linguagem de Programação

n = 7
valido = True
letra = 'r'

Codificação 10.31: Criação de variáveis em um programa em Python.

Programa Memória do computador


Endereço na Endereço na Conteúdo no
Identificadores
memória memória endereço
n 0x45c1f3 0x45c1f3 7
valido 0x0034cc 0x0034cc True
letra 0x14faf1 0x14faf1 'r'

Figura 10.2: Representação simplificada dos dados na memória do computador.


Fonte: Elaborado pelo autor.

Há linguagens em que é possível escolher a forma como os argumentos serão


passados à função, por valor ou por referência. Veja a Codificação 10.32, que define uma
função com um parâmetro x, e em seguida faz uma chamada a essa função colocando
como argumento a variável n, criada na Codificação 10.31. O bloco de código da função
é irrelevante para nossa análise, por isso foi suprimido.
def my_function(x):
# bloco de código da função
my_function(n)

Codificação 10.32: Chamada à uma função com passagem de argumento por valor.

Se a passagem do argumento na chamada da Codificação 10.32 fosse por valor, o


parâmetro x da função receberia uma cópia do valor que está no argumento n. A cópia
seria armazenada em outro local na memória e, consequentemente, teria outro endereço.
Lembre-se que x está em uma “tabela” local da função. Veja a Figura 10.3.

Escopo local: my_function Memória do computador


Endereço na Endereço na Conteúdo no
Identificadores
memória memória endereço
x 0x230f7a 0x230f7a 7

Figura 10.3: Representação simplificada do escopo local da função e da memória do


computador na passagem por valor. Fonte: Elaborado pelo autor.

Observe que o conteúdo associado à x seria idêntico ao conteúdo associado à n,


mas os endereços dos conteúdos seriam diferentes. Portanto, ao alterarmos o valor
associado à variável local x, isso não afetaria o conteúdo associado à variável global n.

Núcleo de Educação a Distância | Faculdade Impacta


Linguagem de Programação

Agora, se a passagem desse argumento for por referência, o parâmetro x


receberá uma referência ao conteúdo original, e não será feita uma cópia do conteúdo
original na memória. Veja a representação na Figura 10.4.

Escopo local: my_function Memória do computador


Endereço na
Identificadores
memória
x 0x45c1f3

Figura 10.4: Representação simplificada do escopo local da função e da memória do


computador na passagem por referência. Fonte: Elaborado pelo autor.

Esse tipo de passagem tem a vantagem de economizar recursos, tanto memória


quanto processamento, por evitar a criação de cópias de objetos. Porém, ao alterarmos o
conteúdo associado à variável x, implicitamente alteramos o valor associado à variável
global n. A isso damos o nome de efeito colateral, pois ao executar a função, valores
externos a ela serão modificados. Comportamento útil quando bem aplicado, mas que
pode gerar erros difíceis de rastrear se ser mal utilizado.

10.9.1. Passagem de argumentos em Python


Por uma decisão de projeto, Python não permite a escolha de como argumentos
são passados às funções. Visando a economia de recursos, dentre outras possíveis
razões, em Python todos os argumentos são passados por referência, mas isso não
significa que sempre teremos o comportamento explicado anteriormente3. Há um misto
de passagem por valor e por referência que dependerá da mutabilidade do argumento.
Quando um objeto é imutável, Python não permite que o conteúdo do objeto
naquele endereço de memória seja alterado, então ao modificarmos o valor da variável,
ao invés de alterar o conteúdo do “espaço” na memória, Python criará uma nova entrada
na memória e associará essa nova referência à variável. Portanto, objetos imutáveis têm
um comportamento análogo à passagem de argumentos por valor.
Já em objetos mutáveis, o conteúdo do objeto pode ser alterado a partir de
qualquer variável que o referencie, inclusive os parâmetros da função. Caso o objeto
alterado precise de mais memória, por exemplo uma lista à qual adicionamos novos
itens, o Python gerenciará essa alocação de memória conforme necessário, sem trocar a
associação entre o identificador e a referência para o objeto alterado4. Sendo assim,

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.

Núcleo de Educação a Distância | Faculdade Impacta


Linguagem de Programação

objetos mutáveis se comportam de modo análogo à passagem de argumentos por


referência5. A Figura 10.5 exibe a mutabilidade dos principais tipos em Python.

Figura 10.5: Mutabilidade dos principais tipos em Python. Fonte: Elaborado pelo autor.

A passagem de argumentos adotada pelo Python é também conhecida como


“passagem por objeto”. Lembre-se que vimos no início do capítulo que tudo em Python
é um objeto. Então, ao chamarmos uma função, Python passará as referências dos
objetos dispostos como argumentos para os parâmetros da função, sem criar cópias.
Veja na Codificação 10.33 uma função que recebe um inteiro n e uma sequência
mutável s como argumentos e realiza operações nesses objetos. Execute o código no
Python Tutor para observar o que acontece com os objetos na memória a cada instrução.
Para deixar mais claro o tratamento dos números inteiros como objetos, antes de
executar o código altere a opção de renderização, conforme a Figura 10.6.
def teste(n, s):
n = n + 2
s[0] = s[0] + 1

n1 = 1
lista = [1]

teste(n1, lista)

print('fim')

Codificação 10.33: Código para exemplificação do comportamento do Python em relação


a passagem de argumentos.

Veja na Figura 10.7 a comparação entre o estado da memória antes, durante e


após a execução da função teste. O valor da variável n1 não foi alterado, mas o item
da lista foi alterado de 1 para 2 e essa alteração persiste após a execução da função.

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.

Núcleo de Educação a Distância | Faculdade Impacta


Linguagem de Programação

Figura 10.6: Alteração da renderização dos objetos no Python Tutor.


Fonte: Elaborado pelo autor.

Figura 10.7: Progressão dos estados de memória no decorrer da execução da


Codificação 10.33 no Python Tutor. Fonte: Elaborado pelo autor.

Execute o código da Codificação 10.34 no Python Tutor. Se ainda estiver na


mesma janela em que testou a Codificação 10.33, ao editar o código e antes de
visualizar a execução, retorne a renderização dos objetos na memória para a
configuração padrão: inline primitives, don’t nest objects [default]. Mesmo que omita
alguns detalhes, essa configuração torna mais simples a visualização.
Observe na Codificação 10.34 que na função com efeitos colaterais não há
retorno de valor, pois as alterações são feitas no objeto original, enquanto na função sem
efeitos colaterais, atribuímos o valor retornado a uma variável para guardá-lo e usá-lo.

Núcleo de Educação a Distância | Faculdade Impacta


Linguagem de Programação

# Função com efeitos colaterais


def converte_1(lista):
for i in range(len(lista)):
lista[i] = int(lista[i])

# Função sem efeitos colaterais


def converte_2(lista):
nova_lista = []
for item in lista:
nova_lista.append(int(item))
return nova_lista

numeros_1 = ['10', '20', '30', '40']


numeros_2 = ['10', '20', '30', '40']

converte_1(numeros_1)
numeros_3 = converte_2(numeros_2)

print(f'lista 1: {numeros_1}', f'lista 2: {numeros_2}',


f'lista 3: {numeros_3}', sep='\n')

Codificação 10.34: Programa para visualização de funções com e sem efeitos colaterais.

Bibliografia e referências

Docstring Guide. Numpydoc 2021. Disponível em: <https://numpydoc.readthedocs.io/


en/latest/format.html>. Acesso em: 25 jan. 2021.
DOWNEY, A. B. Pense em Python. 1 ed. São Paulo: Novatec Editora Ltda., 2016.
MASTROMATTEO, D. Python args and kwargs: Demystified. Real Python, 2019.
Disponível em: <https://realpython.com/python-kwargs-and-args/>. Acesso em: 29
mar. 2021.
PSF. Built-in Functions: id. 2021a. Disponível em: <https://docs.python.org/pt-br/3/
library/functions.html#id>. Acesso em: 13 mar. 2021.
PSF. Built-in Types: Common Sequence Operations. 2021b. Disponível em:
<https://docs.python.org/3/library/stdtypes.html#common-sequence-operations>.
Acesso em: 03 fev. 2021.
PSF. Built-in Types: Mutable Sequence Types. 2021c. Disponível em:
<https://docs.python.org/3/library/stdtypes.html#mutable-sequence-types>. Acesso
em: 03 fev. 2021.
PSF. Built-in Types: String Methods. 2021d. Disponível em: <https://docs.
python.org/3/library/stdtypes.html#string-methods>. Acesso em: 03 fev. 2021.

Núcleo de Educação a Distância | Faculdade Impacta


Proceedings of the XII SIBGRAPI (October 1999) 101-104
Linguagem de Programação

Texto base

11
Ordenação e busca em sequências

Prof. Me. Lucio Nunes de Lira


Prof. MSc. Rafael Maximo Carreira Ribeiro

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/

Núcleo de Educação a Distância | Faculdade Impacta


Linguagem de Programação

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.

11.2.1. Troca entre itens da sequência


Uma das principais operações feitas durante a ordenação é a troca entre itens.
Para exemplificar, criaremos uma lista e trocaremos dois de seus itens, de modo que
após a troca a lista esteja ordenada. Veja a Codificação 11.1.
>>> lista = [10, 40, 30, 20, 50]
>>> temp = lista[1]
>>> lista[1] = lista[3]
>>> lista[3] = temp
>>> lista
[10, 20, 30, 40, 50]

Codificação 11.1: Troca entre itens de uma lista usando uma variável temporária.

O uso de uma variável temporária em um procedimento de troca é uma


abordagem comum e natural. Porém, em Python, podemos melhorar o código utilizando
a atribuição paralela, que pode ser feita com o uso de desempacotamento.
Veja na Codificação 11.2 a troca entre dois itens da lista sem a necessidade de
uma variável temporária e lembre-se que a expressão à direita da atribuição é uma tupla,
porém sem parênteses, e será avaliada antes que os valores sejam atribuídos. Portanto,
só após os valores à direita estarem definidos que as atribuições são executadas.
>>> lista = [10, 40, 30, 20, 50]
>>> lista[1], lista[3] = lista[3], lista[1]
>>> lista
[10, 20, 30, 40, 50]

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.

Núcleo de Educação a Distância | Faculdade Impacta


Linguagem de Programação

>>> lista = [10, 40, 30, 20, 50]


>>> troca(lista, 1, 3)
>>> lista
[10, 20, 30, 40, 50]

Codificação 11.4: Troca entre itens de uma lista usando a função troca.

11.2.2. Empurrando o item máximo da sequência


Como preparação para o algoritmo de ordenação por flutuação, veremos o
funcionamento de sua operação núcleo, baseada em empurrar (flutuar) o item máximo de
uma sequência para sua última posição.
O algoritmo se baseia em comparar todos os pares de itens adjacentes (vizinhos)
da sequência e, se o 1º item do par for maior que o 2º, realizar uma troca entre eles. Veja
a Codificação 11.5 que realiza esse procedimento com uma lista.
lista = [40, 30, 20, 50, 10]
for i in range(len(lista)-1):
if lista[i] > lista[i+1]:
troca(lista, i, i+1)
print(lista)

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.

Tabela 11.1: Teste de mesa da Codificação 11.5.

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

Núcleo de Educação a Distância | Faculdade Impacta


Linguagem de Programação

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.

>>> lista = [40, 30, 20, 50, 10]


>>> empurra(lista, len(lista))
>>> lista
[30, 20, 40, 10, 50]

Codificação 11.7: Empurrando o item máximo da sequência usando a função empurra.

11.2.3. Ordenação por flutuação (Bubble Sort)


Na Codificação 11.7, após a chamada à empurra, você observou que a lista
passada como argumento não ficou ordenada. Entretanto, notou que o último item ficou
na exata posição onde deveria estar se a lista estivesse ordenada. Isso ocorreu porque
empurra deslocou o item máximo da lista de tamanho cinco para sua 5ª posição.
Assim, a lista que inicialmente estava com cinco itens não ordenados, após a 1ª
chamada à função empurra(lista, 5)1, ordenou um item e manteve quatro não
ordenados. Logo, se chamássemos pela segunda vez a função empurra, mas definindo
o tamanho lógico da lista como quatro, ela empurraria o item máximo da lista com
quatro itens para a 4ª posição, aumentando a parte ordenada (agora com dois itens) e
reduzindo a parte não ordenada (agora com três itens).
Seguindo esse raciocínio, poderíamos chamar a função empurra diversas vezes,
sempre com um tamanho lógico menor, que corresponderia à quantidade de itens da
parte não ordenada que, naturalmente, é reduzida a cada item máximo empurrado para o
fim da sequência. Veja essa estratégia ilustrada na Figura 11.1.
Analisando a Figura 11.1, notamos que ao chamar empurra quatro vezes, a
sequência tornou-se ordenada, pois a parte não ordenada foi sequencialmente reduzida
até ficar vazia. Pense o porquê não faz sentido chamar empurra com n <= 1.

1
Lembre-se que na Codificação 11.7, len(lista) resultou no valor 5, que era o tamanho físico da lista.

Núcleo de Educação a Distância | Faculdade Impacta


Linguagem de Programação

Figura 11.1: Sequência de chamadas à função empurra. Fonte: Elaborado pelo autor.

Agora é possível criar a função de ordenação bubble_sort, que empurra os


itens da sequência mutável s para deixá-la ordenada, conforme Codificação 11.8. Veja
um teste de execução desta função na Codificação 11.9, teste-a no Python Tutor.
def bubble_sort(s):
n = len(s)
while n > 1:
empurra(s, n)
n -= 1

Codificação 11.8: Definição da função bubble_sort.

>>> lista = [40, 30, 20, 50, 10]


>>> lista
[40, 30, 20, 50, 10]
>>> bubble_sort(lista)
>>> lista
[10, 20, 30, 40, 50]

Codificação 11.9: Exemplo de utilização da função bubble_sort.

A Codificação 11.8 é um exemplo de função com efeito colateral, pois altera o


valor referenciado pelo parâmetro, mas também poderíamos criar uma versão sem
efeitos colaterais, isto é, ao invés de ordenar a lista passada como argumento, retornaria
como resposta uma cópia ordenada da lista original. Veja como na Codificação 11.10 e
um exemplo de utilização na Codificação 11.11.

Núcleo de Educação a Distância | Faculdade Impacta


Linguagem de Programação

def bubble_sort_2(lista):
lista = lista[:]
n = len(lista)
while n > 1:
empurra(lista, n)
n -= 1
return lista

Codificação 11.10: Função bubble_sort_2, ordenação sem efeitos colaterais.

>>> lista = [40, 30, 20, 50, 10]


>>> lista
[40, 30, 20, 50, 10]
>>> nova_lista = bubble_sort_2(lista)
>>> nova_lista
[10, 20, 30, 40, 50]
>>> lista
[40, 30, 20, 50, 10]

Codificação 11.11: Exemplo de utilização da função bubble_sort_2.

Em Python é comum a criação de funções que não possuam efeitos colaterais, e


essa abordagem pode ser vista em diversas funções integradas do Python, como por
exemplo na função integrada para ordenação que será apresentada na seção 11.2.6. Em
Python, geralmente, observam-se as seguintes recomendações:
● Métodos de objetos mutáveis tem efeito colateral, alterando as características
do próprio objeto, como a ordem dos itens em uma lista;
● Funções que recebem objetos mutáveis como argumentos, não alteram os
objetos, retornando um novo objeto gerado a partir do original.
Caso seja necessário criar uma função com efeitos colaterais, seja por questões
de otimização de desempenho ou por não ser necessário preservar o argumento, é
importante que isso esteja bem documentado para os potenciais usuários da função.

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.

Núcleo de Educação a Distância | Faculdade Impacta


Linguagem de Programação

11.2.5. Algoritmos de ordenação populares


Existem dezenas de algoritmos de ordenação desenvolvidos e aprimorados por
matemáticos e cientistas de diferentes países. Muitos algoritmos são aperfeiçoamentos
de anteriores, para consumir menos recursos ou para lidar com problemas específicos.
Sabemos que um algoritmo indica os passos para resolver um problema, portanto
ao escrever um programa, instruímos ao computador como executar os passos de um
algoritmo. Assim, podem existir diversas implementações de um mesmo algoritmo, que
podem apresentar desempenhos diferentes, mesmo que gerem o mesmo resultado.
Essa disciplina não busca analisar a eficiência de algoritmos, apenas introduzir o
conceito e funcionamento de ordenação, porém é útil expor outros algoritmos de
ordenação populares, que podem ter desempenho de execução superior ao Bubble Sort:
● Selection Sort (ordenação por seleção): consiste em percorrer a parte não
ordenada de uma sequência buscando o maior item (ou menor) e trocando-o
com o último (ou primeiro) da parte não ordenada. Assim, aumenta-se a
parte ordenada e reduz-se a não ordenada. Repete-se o procedimento até a
sequência estar integralmente ordenada. É como normalmente ordenamos
uma “mão” de cartas, selecionando uma carta por vez e deslocando-a para
sua posição final. É um algoritmo estável e eficiente no consumo de
memória, porém lento para sequências grandes, como o Bubble Sort;
● Insertion Sort (ordenação por inserção): assume-se que a parte ordenada da
sequência é composta inicialmente só pelo primeiro item, todos os demais
pertencem a parte não ordenada. Então, percorre-se a sequência a partir do
primeiro item não ordenado, inserindo-o na parte ordenada, de modo que ela
permaneça assim, até que a parte não ordenada fique vazia. Essa é a forma
que normalmente ordenamos cartas ao “comprarmos” uma de cada vez,
inserindo-a em ordem dentre as cartas que já estão em ordem na mão. Como
o Selection Sort, a ordenação por inserção é estável e eficiente no uso de
memória, mas igualmente impraticável em sequências grandes;
● Merge Sort (ordenação por mistura/intercalação): consiste em dividir a
sequência sucessivamente até chegar a subsequências de um item. Então,
faz-se o processo inverso, unindo (misturando) as sequências duas a duas,
comparando seus itens e colocando-os em ordem. Este algoritmo é estável e
muito mais eficiente em quantidade de comparações quando contrastado com
Selection Sort e Insertion Sort, mas requer mais memória para ser executado;
● Quicksort (ordenação rápida): consiste na escolha de um item arbitrário da
sequência como pivô, então divide-se a sequência em duas partes, uma com
os itens menores que o pivô e outra com itens maiores que ele. Assim, o pivô
estará na sua posição ordenada, mas teremos duas subsequências não
ordenadas. Portanto, repete-se o processo em cada subsequência até que

Núcleo de Educação a Distância | Faculdade Impacta


Linguagem de Programação

estejam ordenadas. Algoritmo bastante eficiente no número de comparações


e uso de memória, mas, por padrão, não garante estabilidade na ordenação.
Com sequências pequenas, de algumas dezenas de itens, os algoritmos Selection
Sort e Insertion Sort têm melhor desempenho por terem instruções mais simples e que
demandam menos recursos computacionais. Porém, se a sequência é grande, o número de
comparações torna-os inviáveis. Nestes casos, aplicam-se algoritmos mais sofisticados
como Merge Sort e Quick Sort, que requerem mais recursos, mas são mais eficientes.
Visando melhor desempenho, alguns algoritmos de ordenação integrados às
linguagens de programação são híbridos. Por exemplo, começam a ordenação com o
Merge Sort, mas ao atingir subsequências pequenas, alternam para Insertion Sort.

11.2.6. Funções e métodos integrados de ordenação


Uma forma simples de ordenar sequências em Python é com a função integrada
sorted. Essa função recebe qualquer tipo de sequência como argumento e retorna uma
nova lista com os itens em ordem crescente. Veja um exemplo na Codificação 11.12.
>>> lista = [40, 30, 20, 50, 10]
>>> nova_lista = sorted(lista)
>>> nova_lista
[10, 20, 30, 40, 50]
>>> lista
[40, 30, 20, 50, 10]

Codificação 11.12: Ordenação de uma sequência com a função sorted.

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]

Codificação 11.13: Ordenação de uma lista com o método sort.

👁️‍🗨️
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.

Núcleo de Educação a Distância | Faculdade Impacta


Linguagem de Programação

O algoritmo garante a estabilidade da ordenação e seu código-fonte, feito em


linguagem C, pode ser visto no GitHub (Rossum, 2021), na implementação dos objetos
list. Recebeu contribuições de mais de 70 membros da comunidade Python ao redor do
mundo, desde que se tornou padrão na linguagem, a partir do Python 2.3.
Veja a documentação do Python (PSF, 2021a) para aprender outros usos da
função e método de ordenação integrados. Para saber mais sobre o algoritmo Timsort no
Python, leia a documentação de Tim Peters, disponível no GitHub (Peters, 2021).

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”.

Núcleo de Educação a Distância | Faculdade Impacta


Linguagem de Programação

11.3.1. Busca linear


Vimos que em Python é possível buscar um item, em alguns tipos de sequência,
com o método index, que recebe como argumento um valor e, caso encontre-o, retorna
o índice da primeira ocorrência. Veja a Codificação 11.14 para relembrar como usá-lo.
>>> lista = [3, 6, 5, 8, 0, 8, 2]
>>> lista.index(8)
3

Codificação 11.14: Utilização do método index.

Para compreender o algoritmo da busca linear, criaremos uma função semelhante


ao método index. A função deverá receber um valor e uma sequência e retornar o
índice da primeira ocorrência do valor na sequência, se encontrado. Veja uma solução
válida na Codificação 11.15 e tente elaborar outra versão da mesma função.
def busca_linear(valor, sequencia):
for i, item in enumerate(sequencia):
if item == valor:
return i
return None

Codificação 11.15: Exemplo de implementação da busca linear.

Na Codificação 11.15, usamos a função integrada enumerate para gerar uma


nova sequência a partir da sequência argumento. Cada item da nova sequência é uma
tupla com dois valores, simplificadamente: o 1º é um índice e o 2º é o item da sequência
argumento com aquele índice. Assim, podemos usar o desempacotamento de sequências
no laço for para, a cada iteração, ter acesso tanto ao índice quanto ao valor do item,
sem precisar gerenciar a contagem dos índices manualmente.
Ao encontrar um item igual ao valor procurado, a função é encerrada com o
retorno do índice do item na sequência, que corresponde à variável i. Caso a sequência
seja completamente percorrida e nenhum item corresponda ao valor procurado, será
retornado o valor None. Essa foi uma decisão do programador, pois não está estipulada
na especificação do algoritmo, porém poderíamos seguir o comportamento do método
index e levantar um erro de execução, mas isso não está no escopo desta disciplina.

🏹 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.

Núcleo de Educação a Distância | Faculdade Impacta


Linguagem de Programação

11.3.2. Busca binária


A implementação da busca binária é mais elaborada que a da busca linear, mas
quando sabemos que a lista está ordenada, este método proporciona desempenho muito
superior, que fica mais evidente conforme a busca é realizada em sequências maiores.
Por exemplo, para encontrar um item em uma sequência de tamanho 1 bilhão, a
busca binária precisa de, no máximo 31, comparações. Em uma sequência com 2 bilhões
de itens, no máximo 32. Isso mesmo! Dobrando o tamanho da sequência aumenta-se
apenas uma comparação, pois a cada item comparado, o intervalo de busca pode ser
reduzido pela metade.
A busca binária se baseia no seguinte algoritmo, que também está implementado
em Python na Codificação 11.16:
1) A função recebe um valor que deve ser buscado na sequencia;
2) Atribuímos a inicio o índice do primeiro item da sequência, que é zero;
3) Atribuímos a fim o índice do último item da sequência, dado pelo tamanho
da sequência - 1 (não usaremos indexação com índices negativos);
4) As variáveis inicio e fim definem o intervalo de busca, a princípio
equivale à sequência integral, ou seja, os índices no intervalo [inicio..fim]
são todos aqueles de sequencia. Repare que só faz sentido realizar uma
busca em um intervalo em que inicio <= fim, caso contrário o intervalo
seria vazio e, evidentemente, o item buscado não estaria nele;
5) O valor buscado será comparado com o item que está no índice do meio do
intervalo de busca. Esse índice é obtido pela média aritmética dos extremos
(inicio+fim)//2. Note que o resultado será um número natural;
6) Caso sejam iguais, a função é encerrada com o retorno do índice deste item;
7) Caso não sejam iguais, o valor pode estar entre os itens menores que o do
meio ou entre os itens maiores que ele;
8) Caso o valor buscado seja menor que o item do meio, o procuraremos dentre
os menores, atualizando o intervalo de busca para [inicio..meio-1];
9) Caso o valor buscado seja maior que o item do meio, o procuraremos dentre
os maiores, atualizando o intervalo de busca para [meio+1..fim];
10) O procedimento continuará voltando ao passo 5, enquanto o valor buscado
não for encontrado e o intervalo de busca [inicio..fim] não for vazio;
11) Se [inicio..fim] ficou vazio, isto é, inicio > fim, o valor não foi

👁️‍🗨️
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

Núcleo de Educação a Distância | Faculdade Impacta


Linguagem de Programação

def busca_binaria(valor, sequencia):


inicio = 0
fim = len(sequencia) - 1

while inicio <= fim:


meio = (inicio + fim) // 2

if valor == sequencia[meio]:
return meio
elif valor < sequencia[meio]:
fim = meio - 1
else:
inicio = meio + 1

return None

Codificação 11.16: Exemplo de implementação da busca binária.

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.

Núcleo de Educação a Distância | Faculdade Impacta


Proceedings of the XII SIBGRAPI (October 1999) 101-104
Linguagem de Programação

Texto base

12
Sequências aninhadas e cópias de objetos

Prof. Me. Lucio Nunes de Lira


Prof. MSc. Rafael Maximo Carreira Ribeiro

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/

Núcleo de Educação a Distância | Faculdade Impacta


Linguagem de Programação

12.2. Sequências aninhadas


Em Python, listas e tuplas são sequências que podem conter itens de qualquer
tipo, inclusive outras sequências1, portanto é comum trabalharmos com listas de listas,
tuplas de tuplas, listas de tuplas etc. Para que uma sequência seja aninhada, basta
colocá-la como item de outra sequência, como mostrado na Codificação 12.1.
>>> s = [('pt', 'arroz'), ('en', 'rice'), ('fr', 'riz')]

Codificação 12.1: Exemplo de uma lista de tuplas.

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]

Codificação 12.2: Exemplo de uma lista de listas.

Sabemos que ao indexar uma sequência acessamos um de seus itens. Portanto,


para acessar um item de uma sequência aninhada, basta encadear índices, como visto na
Codificação 12.3. As funções e métodos vistos até agora continuam válidos para as
sequências aninhadas da mesma forma como eram para sequências não aninhadas.
>>> t[0] # 1º item da sequência t é uma lista.
[10, 11, 12]
>>> t[0][2] # 3º item do 1º item da sequência t é um inteiro.
12

Codificação 12.3: Acesso a um item de uma sequência aninhada.

12.3. Representação de matrizes e tabelas


Matrizes e tabelas bidimensionais podem ser representadas como sequências
aninhadas. Matrizes geralmente são numéricas e formadas por itens dispostos em linhas
e colunas, para representá-las em Python precisamos definir a sequência principal e as
sequências aninhadas, que representam as linhas da matriz e seus itens as colunas.
Tabelas são semelhantes às matrizes, porém não precisam ser numéricas ou com itens do
mesmo tipo, suas linhas são chamadas de registros e cada coluna da linha é um campo
daquele registro. Note que a diferença entre as duas estruturas é conceitual, ambas são
compostas por linhas e colunas, e são representadas em Python de forma semelhante.

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.

Núcleo de Educação a Distância | Faculdade Impacta


Linguagem de Programação

Na Figura 12.1 há um exemplo de matriz que contém as notas de 4 alunos em 3


atividades. Em Python, podemos criar uma variável para cada aluno-nota (12 variáveis),
ou uma lista para cada aluno (4 variáveis), como na Codificação 12.4 e Codificação 12.5,
respectivamente. Por concisão, utilizamos atribuição paralela na Codificação 12.4.

Figura 12.1: Exemplo de matriz numérica. Fonte: Elaborado pelo autor.

aluno_0_0, aluno_0_1, aluno_0_2 = 5.5, 7.0, 8.7


aluno_1_0, aluno_1_1, aluno_1_2 = 8.0, 6.0, 9.2
aluno_2_0, aluno_2_1, aluno_2_2 = 7.8, 8.3, 8.5
aluno_3_0, aluno_3_1, aluno_3_2 = 0.0, 9.9, 9.1

Codificação 12.4: Representação da matriz da Figura 12.1 com variáveis simples.

aluno_0 = [5.5, 7.0, 8.7]


aluno_1 = [8.0, 6.0, 9.2]
aluno_2 = [7.8, 8.3, 8.5]
aluno_3 = [0.0, 9.9, 9.1]

Codificação 12.5: Representação da matriz da Figura 12.1 com listas simples.

Com a representação da Codificação 12.5, poderíamos calcular a média dos


alunos com quatro chamadas à função calcula_media da Codificação 12.6, passando
uma lista por vez como argumento, conforme a Codificação 12.7.
def calcula_media(aluno):
soma = 0
for nota in aluno:
soma += nota
return soma / len(aluno)

Codificação 12.6: Função para cálculo da média dos valores de uma sequência.

Núcleo de Educação a Distância | Faculdade Impacta


Linguagem de Programação

media_0 = calcula_media(aluno_0)
media_1 = calcula_media(aluno_1)
media_2 = calcula_media(aluno_2)
media_3 = calcula_media(aluno_3)

Codificação 12.7: Cálculo da média dos quatro alunos da Figura 12.1.

É 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]]

Codificação 12.8: Representação de uma matriz com listas aninhadas.

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.

Figura 12.2: Exemplo de tabela. Fonte: Elaborado pelo autor.

produtos = [['smartphone', 100, 1199.00, True ],


['televisão', 5, 2599.00, False],
['notebook', 20, 4500.00, True ]]

Codificação 12.9: Representação de uma tabela com listas aninhadas.

Núcleo de Educação a Distância | Faculdade Impacta


Linguagem de Programação

12.4. Percorrer matrizes e tabelas


Podemos usar um laço para percorrer as linhas de uma matriz/tabela (que são
listas aninhadas) e para cada linha usar um laço para percorrer suas colunas (que são
itens da lista aninhada). Assim, podemos, por exemplo, calcular todas as médias da
matriz da Codificação 12.8 com apenas dois laços, conforme a Codificação 12.10.
medias = []
for aluno in alunos:
soma = 0
for nota in aluno:
soma += nota
media = soma / len(aluno)
medias.append(media)

Codificação 12.10: Cálculo da média em uma lista aninhada.

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.

Note que na Codificação 12.11 usamos a função calcula_media para calcular


a média de cada aluno, e por conta disso não há laços aninhados explicitamente. Essa
estratégia simplifica a codificação e é uma das vantagens de usar funções, mas é
importante observar que Python ainda executará um laço aninhado, só que implícito.
Quando criamos uma sequência, é comum nomeá-la com o plural de seus itens,
mas às vezes o nome da sequência torna-se mais significativo ao se considerar o contexto.
Por exemplo, uma sequência de notas pode se tornar “aluno”, pois são as notas de um
aluno, uma sequência de alunos pode se tornar “turma”, pois são os alunos de uma
turma. Não existe regra, use a forma que tornar a solução mais clara, coerente e legível.
Como já discutido, é uma abordagem comum em Python evitar a alteração dos
argumentos recebidos pelas funções, porém isso depende do problema a ser resolvido,
pois a solução se adequa ao problema e não o problema à solução. Para exemplificação,
leia o enunciado: “crie uma função que receba como argumento uma matriz de notas

Núcleo de Educação a Distância | Faculdade Impacta


Linguagem de Programação

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

Codificação 12.12: Atualização das notas sem modificar o argumento.

Porém, existem situações onde é desnecessário preservar o argumento ou em que


o objetivo é exatamente alterá-lo. Nesses casos, a Codificação 12.13 pode ser usada.
def atualiza_notas_2(turma):
for aluno in turma:
for i in range(len(aluno)):
aluno[i] *= 2

Codificação 12.13: Atualização das notas modificando o argumento.

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.

12.5. Preencher matrizes e tabelas


É importante saber como preencher matrizes e tabelas com entradas dadas pelo
usuário. Na Codificação 12.15 há um programa em que a função coleta_notas é
responsável por preencher e retornar uma lista de notas de um aluno e a função
preenche_turma chama diversas vezes a função coleta_notas para preencher uma
matriz de notas de uma turma com qtd_alunos linhas e, ao final, retorna a matriz
criada. Há na Figura 12.3 um exemplo de execução. Faça testes no Python Tutor.

Núcleo de Educação a Distância | Faculdade Impacta


Linguagem de Programação

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}')

qtd_alunos = int(input('Quantidade: '))


turma = preenche_turma(qtd_alunos)
resumo_turma(turma)

Codificação 12.15: Programa completo para coleta de notas e exibição de médias.

Figura 12.3: Exemplo de execução da Codificação 12.15. Fonte: Elaborado pelo autor.

Núcleo de Educação a Distância | Faculdade Impacta


Linguagem de Programação

🏹 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.

12.6. Cópia de objetos em Python


Em Python a atribuição de um objeto a uma variável não gera uma cópia do
objeto, pois a variável receberá apenas uma referência (endereço) ao objeto que já foi
criado e está na memória. Veja na Figura 12.4 um exemplo no Python Tutor em que uma
lista é atribuída a diversas variáveis, o conceito se aplica a qualquer objeto da linguagem.
Note que a lista foi criada apenas uma vez, na primeira atribuição, porém outras
variáveis também a referenciam, logo qualquer uma das variáveis poderá ser usada para
acessar e alterar o conteúdo dessa lista, e isso será refletido nas demais, pois todas
apontam para o mesmo objeto na memória e este objeto é mutável.
Caso seja necessário criar uma cópia do objeto que está na memória para que,
por exemplo, a alteração feita na cópia não afete o objeto original, deve-se solicitar uma
cópia explicitamente. Aprenderemos como fazer isso, no entanto é útil ressaltar que

Núcleo de Educação a Distância | Faculdade Impacta


Linguagem de Programação

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.4: Mecanismo de atribuição em Python. Fonte: Elaborado pelo autor.

12.6.1. Cópia rasa


A cópia rasa, ou cópia simples, ocorre quando apenas o primeiro nível do objeto
é copiado e os demais níveis continuam referenciando os objetos originais.
Em Python há diversas formas de conseguirmos uma cópia rasa de objetos
mutáveis, que podem variar dependendo do tipo de objeto. A forma genérica, que
funciona para todos os objetos mutáveis, é com a função copy do módulo homônimo
copy, como mostra a Figura 12.5.

Figura 12.5: Cópia rasa de um objeto mutável com copy. Fonte: Elaborado pelo autor.

Núcleo de Educação a Distância | Faculdade Impacta


Linguagem de Programação

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.

12.6.2. Cópia profunda


Ao copiar uma lista que não contém listas aninhadas, uma cópia rasa gera um
novo objeto totalmente independente do original, mas veja na Figura 12.7 o que ocorre
ao criarmos cópias rasas de uma lista de listas.
As formas que vimos para criar cópias rasas geram apenas uma cópia da lista
principal, a mais externa, mas não das listas aninhadas. Desta forma, a cópia da lista
original referencia as mesmas listas aninhadas que a lista original!
Portanto, nas situações em que é necessário uma cópia totalmente desvinculada
do objeto original, é preciso criar uma cópia profunda, isto é, uma cópia do objeto
principal e dos objetos internos a ele em todos os níveis. Veja na Figura 12.8 uma cópia
profunda com base no exemplo da Figura 12.7. Em Python, cópias profundas são
realizadas com a função deepcopy do módulo copy.

Núcleo de Educação a Distância | Faculdade Impacta


Linguagem de Programação

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.

12.6.3. Cópia de objetos imutáveis


Como sabemos, é dispensável copiar objetos imutáveis, pois eles não podem ser
alterados. Porém, cópias profundas de objetos imutáveis que contenham objetos mutáveis
podem ser úteis, como tuplas de listas, porém não abordaremos essa situação aqui.
Veja na Figura 12.10 um exemplo no Python Tutor sobre o que ocorre quando
atribuímos um novo objeto a uma variável. Para reproduzir este exemplo com objetos
simples como números, é preciso alterar a forma padrão com a qual o Python Tutor
representa os objetos que estão na memória. Ajuste-o conforme a Figura 12.9.

Figura 12.9: Alteração da renderização do Python Tutor. Fonte: Elaborado pelo autor.

Núcleo de Educação a Distância | Faculdade Impacta


Linguagem de Programação

Figura 12.10: Estado da memória após a 3ª instrução. Fonte: Elaborado pelo autor.

Na 1ª instrução, Python cria um objeto na memória do tipo inteiro com o valor 1


e atribui a referência à variável n. Na 2ª instrução, a mesma referência é atribuída à
variável n2, por meio de n. Na 3ª instrução, a referência ao mesmo objeto é atribuída à
n3, por meio de n2. Ou seja, três variáveis que referenciam o mesmo objeto.
Na Figura 12.11 vemos que, após a 4ª instrução, o valor de n2, que antes era a
referência ao objeto 1 na memória, foi sobrescrito pela pela referência ao objeto 2, recém
criado e atribuído à n2. Todo objeto é criado na primeira vez que ocorre no código.

Figura 12.11: Estado da memória após a 4ª instrução. Fonte: Elaborado pelo autor.

12.7. Argumentos nomeados e argumentos com valor padrão


Sabemos que é possível alterar o comportamento da função print estipulando a
string separadora de argumentos e a string terminadora de impressão. Por omissão, o
comportamento padrão estabelece um espaço como separador e uma quebra de linha
como terminador. Relembre com os exemplos da Codificação 12.16.
Essa forma de uso é possível porque print foi definida com argumentos com
valor padrão e podemos chamá-la passando argumentos nomeados. Veremos como
incluir esse comportamento em nossas próprias funções.

Núcleo de Educação a Distância | Faculdade Impacta


Linguagem de Programação

>>> print('Ana', 'Bia', 'Clô')


Ana Bia Clô
>>> print('Ana', 'Bia', 'Clô', sep=' & ', end='!')
Ana & Bia & Clô!

Codificação 12.16: Exemplos de chamada à função print.

12.7.1. Argumentos nomeados


Por padrão, argumentos são posicionais, isso significa que ao chamar uma
função os argumentos são associados aos parâmetros de acordo com a posição em que
são escritos. Esse comportamento pode ser alterado com argumentos nomeados. Para
isso, chama-se a função passando os argumentos como valores atribuídos aos nomes dos
parâmetros, por isso é dito que os argumentos são “nomeados”.
Para descobrir os nomes dos parâmetros, podemos: (a) consultar a documentação
da função; (b) usar o editor de código, que pode exibir a assinatura da função quando
digitamos a abertura de parênteses ou; (c) chamar a função integrada help, que recebe
como argumento o nome da função que se deseja consultar. Veja na Codificação 12.17
exemplos de chamadas à uma função usando argumentos nomeados.
def divide(numerador, denominador):
return numerador / denominador

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.17: Chamada à uma função passando argumentos nomeados.

A ordem da passagem dos argumentos nomeados não importa, mas é inválido


passar argumentos nomeados antes de posicionais, pois seria impossível determinar quais
parâmetros receberiam os posicionais. Veja na Codificação 12.18 uma chamada válida e
outra inválida de acordo com a combinação de argumentos posicionais e nomeados.
>>> divide(6, denominador=2) # chamada válida
3
>>> divide(numerador=6, 2) # chamada inválida
SyntaxError: positional argument follows keyword argument

Codificação 12.18: Chamada inválida, argumento nomeado antes de posicional.

Núcleo de Educação a Distância | Faculdade Impacta


Linguagem de Programação

Passar dois argumentos para o mesmo parâmetro, ou seja, um posicional e outro


nomeado, causará erro de execução, como mostra a Codificação 12.19.
>>> divide(6, numerador=2)
...
TypeError: divide() got multiple values for argument 'numerador'

Codificação 12.19: Chamada inválida, dois argumentos para o mesmo parâmetro.

12.7.2. Argumentos com valor padrão


Geralmente, quando uma função é chamada, passa-se a quantidade de argumentos
equivalente à quantidade de parâmetros definidos em sua criação, logo uma função com
três parâmetros é chamada com três argumentos, por exemplo. Porém, é possível criar
funções com argumentos pré-definidos, nestas funções é possível chamá-las omitindo
alguns desses argumentos, tornando-os opcionais.
Para criar funções com argumentos padrão, basta definir parâmetros com uma
atribuição de valor, como senha na Codificação 12.20.
def recepcao_cliente(nome, senha=1):
print(f'Olá {nome}, sua senha é {senha}')

Codificação 12.20: Função com argumento padrão para o parâmetro senha.

O argumento correspondente à senha poderá ser omitido quando a função for


chamada e, se isso ocorrer, assumirá o valor 1, como podemos ver na Codificação 12.21.
>>> recepcao_cliente('Megan')
Olá Megan, sua senha é 1
>>> recepcao_cliente('Megan', 23)
Olá Megan, sua senha é 23

Codificação 12.21: Chamadas à uma função com argumento padrão.

Chamar uma função com argumentos nomeados e a definição de argumentos


padrão são recursos distintos, mas podem ser combinados, como na Codificação 12.22.
>>> recepcao_cliente(nome='Megan')
Olá Megan, sua senha é 1
>>> recepcao_cliente(senha=23, nome='Megan')
Olá Megan, sua senha é 23

Codificação 12.22: Uso combinado de argumento nomeado e argumento padrão.

Na definição da função, parâmetro com argumento padrão não pode anteceder


parâmetro sem argumento padrão. Veja um exemplo de função inválida por violação
desta regra na Codificação 12.23 e o erro resultante na Figura 12.12.

Núcleo de Educação a Distância | Faculdade Impacta


Linguagem de Programação

def desconto(porcentagem=0.05, valor):


return valor * porcentagem

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.

12.7.3. Objetos mutáveis como argumentos padrão


É importante entender o comportamento dos objetos mutáveis em Python quando
são usados como argumentos padrão. Ao definir uma função com argumentos padrão,
Python criará os objetos de cada argumento apenas uma vez, no momento em que a
função for definida. Portanto, mesmo que a função seja chamada várias vezes, os valores
dos argumentos padrão não serão reconstruídos e eventuais alterações serão mantidas.
Vejamos o seguinte problema: “crie uma função que receba como argumentos
um objeto e uma lista, a função deve adicionar o objeto ao final da lista e retorná-la.
Caso não seja fornecida uma lista na chamada, a função deve criar uma nova lista
vazia.”. Após aprender sobre argumentos padrão, algum iniciante em Python poderia
elaborar a solução da Codificação 12.24.
def adiciona_item(x, lista=[]):
lista.append(x)
return lista

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']

Codificação 12.25: Utilização da função adiciona_item com 2 argumentos.

Núcleo de Educação a Distância | Faculdade Impacta


Linguagem de Programação

>>> x = adiciona_item(3)
>>> x
[3]
>>> y = adiciona_item('a')
>>> y
[3, 'a']
>>> z = adiciona_item(1)
>>> z
[3, 'a', 1]

Codificação 12.26: Utilização da função adiciona_item com apenas um argumento.

Observamos que o Python está adicionando os objetos sempre à mesma lista,


pois a cada chamada usa-se o mesmo argumento criado na definição da função.
Para evitar a criação do objeto mutável na definição da função e gerar um novo a
cada execução, podemos usar None como argumento padrão e criar o objeto necessário
caso não seja passado um argumento (PSF, 2021b, PSF, 2021c). Veja essa reformulação
na Codificação 12.27 e exemplos de execução na Codificação 12.28.
def adiciona_item(x, lista=None):
if lista is None:
lista = []

lista.append(x)
return lista

Codificação 12.27: Reformulação da função adiciona_item.

>>> x = adiciona_item(3)
>>> x
[3]
>>> y = adiciona_item('a')
>>> y
['a']
>>> z = adiciona_item(1)
>>> z
[1]

Codificação 12.28: Chamadas à nova versão da função adiciona_item.

O programador deve ficar atento a esse comportamento, pois se mal utilizado


pode gerar problemas difíceis de serem rastreados. Entretanto, há soluções que podem
ser beneficiadas como a criação de uma memória temporária e compartilhada entre as
chamadas de uma função, por exemplo, para implementar a memoização2.
2
Sim, o termo é memoização, uma técnica para otimizar a resolução de problemas repetitivos, pois
armazena as respostas previamente calculadas em um tipo de cache. Não será abordada nesta disciplina.

Núcleo de Educação a Distância | Faculdade Impacta


Linguagem de Programação

Um exemplo de uma função com argumento padrão que é um objeto mutável


pode ser visto na Codificação 12.29, uma função que conta quantas vezes foi executada.
def teste(execucoes=[0]):
execucoes[0] += 1
print(f'execução número: {execucoes[0]}')

Codificação 12.29: Função que guarda a quantidade de vezes que foi executada.

Na documentação da função da Codificação 12.29, é importante avisar aos


programadores que o argumento do parâmetro execucoes não deve ser fornecido na
chamada da função, pois caso seja, a execução não será contabilizada. Faça um teste no
Python Tutor, chamando a função algumas vezes.

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.

Núcleo de Educação a Distância | Faculdade Impacta

Você também pode gostar