4  Tipos primitivos e objetos básicos

4.1 Introdução

Este capítulo introduz os elementos fundamentais com os quais o Python trabalha: valores, tipos e objetos. Em qualquer programa, manipulamos dados – números, textos, valores lógicos – e precisamos compreender como esses dados são representados, armazenados e transformados. Antes de trabalhar com estruturas mais complexas, é necessário dominar os tipos primitivos e entender que, em Python, tudo é um objeto. Todo grande algoritmo e programa é construído a partir dos mesmos pequenos blocos.

Figura 4.1: Blocos que formam a Pirâmide de Quéops e Pirâmide de Quéfren ao fundo, 2024.

Começaremos discutindo os tipos primitivos, que constituem a base de toda manipulação de dados e determinam a forma como a informação é armazenada, quais operações são permitidas e como o valor ocupa a memória. Em seguida, generalizaremos para a noção de objeto e discutiremos como propriedades e métodos estruturam o comportamento desses valores.

4.1.1 Pré-requisitos

Nesse capítulo utilizaremos essencialmente funções nativas do Python. No entanto, para trabalhar com padrões textuais de forma mais profunda, utilizaremos alguns pacotes externos, em especial para lidar com as chamadas expressões regulares.

  • re
import re

4.2 Tipos primitivos

Antes de introduzirmos a noção geral de objeto, é fundamental compreender os tipos mais elementares de dados disponíveis no Python, a partir dos quais todas as estruturas mais complexas são construídas. Um tipo de dado define a forma como a linguagem armazena e interpreta um valor, isto é, um pedaço de informação que pode representar um número, um texto ou um valor lógico. No caso de dados numéricos, há dois tipos principais: int, utilizado para números inteiros, e float, empregado para números com parte decimal. Informações textuais são armazenadas no tipo str (abreviação de string). Por fim, valores lógicos pertencem ao tipo bool e podem assumir apenas dois estados possíveis: True ou False.

  • int: representa números inteiros, que podem ser positivos, negativos ou zero. Como discutido no sec-fundamentos, é fácil representar um número inteiro no sistema binário. Em um sistema de 8 bits, por exemplo, é possível representar valores entre 0 e 255 no conjunto dos inteiros não negativos. O tamanho de um número inteiro, no entanto, não é fixo em 8 bits, ele pode crescer até 64 bits conforme necessário e a memória disponível.

  • float: representa números com parte inteira e parte decimal. Esses valores são armazenados internamente em ponto flutuante binário, o que implica aproximação computacional na maioria dos casos e demanda um certo número de bits para representar a parte inteira e um outro númro de bits para representar o decimal. Isso ocorre tanto para números aparentemente simples, como \(2.5\) ou \(0.0390625\), quanto para dízimas periódicas, como \(0.333...\), ou números irracionais, como o \(\pi\). Como regra geral, quanto maior a precisão desejada, maior o custo em memória. Essa relação entre precisão, armazenamento e memória será retomada em um capítulo posterior.

  • str: utilizado para armazenar informações textuais, esse tipo representa sequências ordenadas de caracteres. Em outras palavras, a ordem importa: o string “abc” é diferente do string “bca”. No Python, qualquer valor delimitado por aspas simples ou aspas duplas é considerado um string dentro da linguagem.

  • bool: armazena valores lógicos, que frequentemente são resultado de algum tipo de comparação. Valores booleanos serão essenciais quando estudarmos estruturas de controle, como instruções condicionais e métodos de iteração no próximo capítulo.

Compreender os tipos primitivos, suas propriedades e finalidades é essencial para evitar erros comuns, como a tentativa de realizar operações entre tipos incompatíveis. Além disso, o comportamento de um operador depende diretamente do tipo dos valores envolvidos. Lembre-se, o Python é uma linguagem de tipagem forte, o que significa que não realiza conversões automáticas entre strings e números quando a operação é ambígua. Assim, enquanto o operador + na expressão '5' + '5' funciona como um operador de concatenação de strings, no caso de 5 + 5 ele representa uma soma aritmética simples. Tome bastante cuidado com isso.

A seguir, generalizaremos essa discussão ao introduzir a noção de objeto, que permite compreender como o Python organiza internamente todos esses valores.

4.3 Objetos básicos

Em Python, tudo é um objeto. Isso significa que qualquer valor – seja um número, um texto ou um valor lógico – possui uma estrutura interna bem definida. De forma simplificada, um objeto é caracterizado por três elementos: (i) um tipo, que determina sua categoria; (ii) um valor, que corresponde à informação que ele representa; e (iii) um conjunto de métodos, isto é, operações específicas que podem ser aplicadas a ele.

4.3.1 Strings

As strings ilustram de forma clara essa noção. Diferentemente de números inteiros ou de ponto flutuante, que representam quantidades numéricas, uma string representa uma sequência ordenada de caracteres. A ordem dos caracteres é relevante, e cada string é delimitada por aspas simples ou duplas. Relembrando nosso primeiro “programa” da aula passada, podemos atribuir a uma variável a string ‘Hello, World’ usando aspas:

## Uso de aspas simples
str1 = 'Hello, World'

print(str1)
print(type(str1))
Hello, World
<class 'str'>
## Uso de aspas duplas
str2 = "Hello, World"

print(str2)
print(type(str2))
Hello, World
<class 'str'>

Podemos ainda utilizar uma sequência de três aspas duplas e escrever strings que percorram várias linhas. Isso é bastante útil na documentação de funções personalizadas, como veremos no sec-funcoes.

str3 = """Das Utopias

Se as coisas são inatingíveis...ora!
Não é motivo para não querê-las...
Que tristes os caminhos, se não fora
A presença distante das estrelas!
           
Mario Quintana 
"""

print(str3)
print(type(str3))
Das Utopias

Se as coisas são inatingíveis...ora!
Não é motivo para não querê-las...
Que tristes os caminhos, se não fora
A presença distante das estrelas!

Mario Quintana 

<class 'str'>

4.3.1.1 Operações básicas

Por ser uma sequência de caracteres e não um número, operações aritméticas, em geral, não são permitidas, mas outras operações o são. Uma dessas operações é chamada de slicing ou fatiamento, que nos permite acessar um caracter ou uma sequência de caracteres específicos do string utilizando a posição – também chamada de índice – desse(s) caractere(s). No caso do string ‘Hello, World’, para acessar a segunda letra podemos utilizar colchetes após o string e dentro dele o índice referente à posição do ‘e’ no string:

print(str1)
print(str1[2])
Hello, World
l

Ué, mas porque que obtivemos como resposta o caractere ‘l’, que é o 3º na sequência, e não o ‘e’, que é o 2º elemento? Aqui vai uma particularidade da sintaxe do Python: em se tratando de índices, o Python sempre começa a contagem em 0. Dessa forma, para acessar o primeiro caractere de ‘Hello, World’ devemos pedir str1[0], para acessar o segundo é preciso pedir str1[1] e assim por diante. Além disso, podemos acessar contando de trás para frente e usando um índice negativo. Finalmente, é possível que o índice seja substituído por uma expressão, que deve sempre retornar como resultado um número inteiro.

# Extração do segundo caractere do string pelo índice positivo
print(str1[1])

# Extração do segundo caractere do string pelo índice negativo
print(str1[-11])

# Extração do segundo caractere do string através de uma expressão
n = 0
print(str1[n+1])
e
e
e

Para extrair não um único caractere, mas uma sequência (ou fatia) de caracteres do string podemos utilizar um intervalo de índices, que inclui o caractere representado pelo índice inicial mas exclui o caractere representado pelo índice final. Para extrair a primeira palavra do string ‘Hello, World’, por exemplo, devemos utilizar o intervalo que começa em 0 (posição do ‘H’) e termina em 5 (posição da vírgula).

print(str1[0:5])
Hello

Para além do fatiamento, é possível utilizar o operador aritmético + como uma forma de “somar” strings. Formalmente, o que essa operação faz é concatenar os strings, transformando múltiplos strings em um novo objeto do tipo str que replica a ordem do primeiro string, seguido dos caracteres ordenados do segundo (e terceiro, quarto,…) string.

uniao_str = str1 + ' --- ' + str2
print(type(uniao_str))
print(uniao_str)
<class 'str'>
Hello, World --- Hello, World

Note, porém, que strings são imutáveis, de modo que caso queira substituir um dos caracteres dentro de um string é preciso utilizar uma função específica para isso ou criar um novo string derivado do 1º. Apenas tentar substituir um dos caracteres de um string já definido não é permitido.

# Tentemos substituir o 'e' do str1 por 'a'
str1[1] = 'a'
---------------------------------------------------------------------------
TypeError                                 Traceback (most recent call last)
Cell In[9], line 2
      1 # Tentemos substituir o 'e' do str1 por 'a'
----> 2 str1[1] = 'a'

TypeError: 'str' object does not support item assignment

4.3.1.2 Métodos de strings

As strings oferecem métodos que executam várias operações úteis. Um método é em essência uma sequência de instruções encapsuladas dentro de um único comando que recebe argumentos e devolve um valor. Embora a sintaxe seja diferente, a ideia é a mesma quando falamos de funções, objeto do nosso estudo daqui algumas aulas.

No caso dos métodos, temos que passar o nome da string que foi definida anteriormente seguida de ‘.’ e depois do comando relacionado ao método específico. Considere um string de exemplo nomeado como str_example.

str_example=' Hello, World'

Dentre os principais métodos aplicáveis a strings e suas funcionalidades podemos citar:

  1. str_example.upper() e str_example.lower(): devolve a string str_example toda em letras maiúsculas ou minúsculas, respectivamente.
print(str_example.upper())
print(str_example.lower())
 HELLO, WORLD
 hello, world
  1. str_example.strip(): devolve a string retirando possíveis espaços em branco no início e no fim da string.
print(str_example.strip())
Hello, World
  1. str_example.startswith(‘xyz’) e str_example.endswith(‘xyz’): testa se a string começa ou termina, respectivamente, com os caracteres ‘xyz’.
print(str_example.startswith(' '))
print(str_example.endswith('d'))
True
True
  1. str_example.find(‘xyz’): procura a string ‘xyz’ dentro de str_example e retorna o primeiro índice onde ‘xyz’ começa ou retorna -1 se nada for encontrado.
print(str_example.find(','))
6
  1. str_example.replace(‘old’,‘new’): retorna uma string nova onde todas as ocorrências de ‘old’ encontradas serão substituídas por ‘new’.
print(str_example.replace('Hello','World'))
 World, World

Além desses principais, existem outros vários métodos para strings. Esse link é um bom ponto de partida para quem quiser conhecer outros exemplos.

4.3.1.3 Formatação e a instrução print

Um método sobre o qual não falamos, mas que é bastante interessante quando queremos, por exemplo, printar o resultado de determinada operação é s.format(). Com esse método podemos converter uma variável numérica para uma formato específico e printá-la dentro de um string maior. Imagine, por exemplo, que estejamos interessados em printar o valor de \(\pi\) arredondado para 2 casas decimais apenas dentro de um string que diz isso. Podemos implementar isso da seguinte forma

pi = 3.1415926535
string_base='O valor de pi arredondado para 2 casas decimais é {:.2f}. Interessante, não?'

print(string_base.format(pi))
O valor de pi arredondado para 2 casas decimais é 3.14. Interessante, não?

Os colchetes dentro do string mostram onde que o número deve aparecer. Mais do que isso, definimos dentro do colchete o formato do número. Após os ‘:’, o ‘.2’ significa que queremos 2 casas decimais enquanto ‘f’ significa que queremos um formato de ponto fixo. Não vou entrar nos detalhes de todas as formatações possíveis, mas podemos ver um pouco mais disso aqui.

4.3.1.4 Introdução a expressões regulares

Por vezes queremos encontrar um padrão específico de texto (e.g., placas de carro, e-mails ou números de telefone) dentro de um texto maior, para realizar algum tipo de coleta, limpeza ou mesmo substituição que um simples str.replace() não dá conta. Para realizar tal ação podemos utilizar as famosas Expressões Regulares, também conhecidas como Regular Expressions no inglês, ou simplesmente Regex.

As expressões regulares são em essência uma potente linguagem para especificar padrões de texto. De forma mais detalhada, é uma composição dos chamados metacaracteres, caracteres com funções especiais, que, agrupados entre si e em conjunto com caracteres literais, formam uma sequência, uma expressão. Essa expressão é interpretada como uma regra que indicará sucesso se uma entrada de dados qualquer casar com essa regra, ou seja, obedecer exatamente a todas as suas condições.

Imagine que você tenha o string abaixo, que mostra o texto de um trecho de uma notícia da CNN sobre o resultado da Pesquisa Datafolha para presidente divulgada em 18/08/2022 (link para a matéria completa aqui).

pesquisa = """
Pesquisa Datafolha divulgada nesta quinta-feira (18) mostra o ex-presidente Luiz Inácio Lula da Silva (PT) à frente, 
com 47% das intenções de voto na corrida pelo Palácio do Planalto. O presidente Jair Bolsonaro (PL) tem 32%. 
O primeiro turno das eleições acontece em 2 de outubro.

Na sequência, aparecem Ciro Gomes (PDT), com 7%; Simone Tebet (MDB), com 2%, e Vera Lúcia (PSTU), com 1%.
"""
print(pesquisa)

Pesquisa Datafolha divulgada nesta quinta-feira (18) mostra o ex-presidente Luiz Inácio Lula da Silva (PT) à frente, 
com 47% das intenções de voto na corrida pelo Palácio do Planalto. O presidente Jair Bolsonaro (PL) tem 32%. 
O primeiro turno das eleições acontece em 2 de outubro.

Na sequência, aparecem Ciro Gomes (PDT), com 7%; Simone Tebet (MDB), com 2%, e Vera Lúcia (PSTU), com 1%.

E se quiséssemos, por exemplo, substituir todas as porcentagens de intenção de voto por ‘ZZZ’? Note que as porcentagens são diferentes e não há uma repetição dos números que nos permita usar o str.replace() de uma vez só. No entanto, todas as porcentagens são representadas por 1 ou 2 números inteiros seguidos do símbolo \(\%\). Nesse caso, o mais indicado é utilizar as expressões regulares.

Os módulos e funções nativas do Python não nos trazem muito material para trabalhar com expressões regulares. Para operar com elas utilizaremos uma biblioteca de comandos chamada re. Falaremos mais sobre importação de bibliotecas de comandos e funções mais a frente, mas por hora tenha na cabeça que para utilizar um conjunto de instruções disponível em alguma biblioteca importada é preciso utilizar o nome da biblioteca seguido de ponto e do nome da função dessa biblioteca que você quer utilizar. No caso, para realizar a substituição das porcentagens no string pesquisa utilizaremos a função sub() de dentro da biblioteca re. Mas o que devemos colocar como input dessa função?

O mundo das expressões regulares é um mundo gigante e à parte, com conteúdo suficiente para preencher um outro curso. De forma geral, a combinação entre metacaracteres e caracteres literais é o que dá o padrão do texto pelo qual procuramos. Alguns dos metacaracteres-padrão são . ? * + ^ | [ ] { } ( ) \, cada um realizando uma função específica. Para o nosso caso utilizaremos basicamente a expressão regular dada por

[0-9]{1,2}%

Mas o que essa coisa bizarra diz de fato? A função buscará todo e qualquer elemento dentro dos colchetes (no caso os dígitos numéricos) que apareça uma ou duas vezes (código dentro dos colchetes) e que seja seguido pelo símbolo de porcentagem. Note que esse é o padrão de qualquer uma das porcentagens no nosso string pesquisa. Vamos ver o que acontece se usarmos isso dentro de re.sub().

pesquisa2 = re.sub('[0-9]{1,2}%','ZZZ',pesquisa)

print(pesquisa2)

Pesquisa Datafolha divulgada nesta quinta-feira (18) mostra o ex-presidente Luiz Inácio Lula da Silva (PT) à frente, 
com ZZZ das intenções de voto na corrida pelo Palácio do Planalto. O presidente Jair Bolsonaro (PL) tem ZZZ. 
O primeiro turno das eleições acontece em 2 de outubro.

Na sequência, aparecem Ciro Gomes (PDT), com ZZZ; Simone Tebet (MDB), com ZZZ, e Vera Lúcia (PSTU), com ZZZ.

Conseguimos exatamente o que a gente queria. Boa, time!

Pare um pouco e pense sobre a aplicabilidade dessa ferramenta dentro de um mundo repleto de dados não-estruturados, como tweets e notícias. O potencial de uso é gigante! Mas como dissemos, o mundo de regex é muito grande e existem cursos e livros que se dedicam integralmente a estudar essa linguagem de padrões textuais. Uma boa referência introdutória é o livro Expressões Regulares: Uma Abordagem Divertida.

4.3.2 Listas

Como uma string, uma lista é uma sequência de valores. Em uma string, os valores são caracteres; em uma lista, eles podem ser de qualquer tipo. Podemos ter uma lista de strings, uma lista de valores numéricos ou mesmo uma lista de listas, combinando strings, números e mesmo outros tipos de objetos que ainda veremos, como tuplas, dicionários e dataframes.

Uma lista é delimitada por colchetes e os elementos, ou itens, pertencentes a ela são separados por vírgula. Para definir uma lista com 5 números inteiros, ordenados de forma sequencial e começando em 1 devemos escrever a seguinte linha de código:

lista1 = [1,2,3,4,5]

print(type(lista1))
print(lista1)
<class 'list'>
[1, 2, 3, 4, 5]

Podemos definir uma lista de strings, uma lista mista de strings e números, e uma lista composto por outras listas (lista aninhada):

lista2 = ['Danilo Souza','Artur Viaro']
lista3 = ['Turma 2026101',43.0,'Turmas 2026102',25,'Turmas 2026121',39,'Turmas 2026122',45]
lista4 = [lista1, lista2]

print(lista2)
print(lista3)
print(lista4)
['Danilo Souza', 'Artur Viaro']
['Turma 2026101', 43.0, 'Turmas 2026102', 25, 'Turmas 2026121', 39, 'Turmas 2026122', 45]
[[1, 2, 3, 4, 5], ['Danilo Souza', 'Artur Viaro']]

De forma análoga à listas com elementos não vazios, é possível definir uma lista vazia utilizando apenas os colchetes:

lista_vazia = []

print(lista_vazia)
[]

4.3.2.1 Operações básicas

A sintaxe para acessar os elementos de uma lista é a mesma que para acessar os caracteres de uma string. A expressão dentro dos colchetes especifica o índice ou o intervalo de índices. Lembrando que o índice no Python começa em ZERO e não em UM.

print(lista2[0])
print(lista2[1])
print(lista3[-1])
print(lista3[0:2])
Danilo Souza
Artur Viaro
45
['Turma 2026101', 43.0]

Diferente das strings, no entanto, listas são objetos mutáveis, isto é, objetos que podem ser alterados internamente. Quando o operador de colchete aparece do lado esquerdo de uma atribuição, ele identifica o elemento da lista que será atribuído. No exemplo abaixo, O segundo elemento da lista numbers (índice 1), que era 123 na atribuição inicial, passa a ser 5 após a substituição do elemento na lista.

numbers = [42, 123]
numbers[1] = 5

print(numbers)
[42, 5]

Índices de listas funcionam da mesma forma que os índices de strings:

  • Qualquer expressão de números inteiros pode ser usada como índice.

  • Caso você tente extrair ou sobrescrever um elemento que não existe, você receberá um erro do tipo IndexError.

  • Se um índice tiver um valor negativo, ele conta de trás para a frente, a partir do final da lista.

Assim como com strings, é possível calcular o número de elementos em uma lista utilizando a função integrada len() e “concatenar” ou “somar” listas utilizando apenas o operador aritmético +.

produtos_mercado = ['alface','tomate','arroz','ovos','carne moída','sabão líquido']

print(len(produtos_mercado))
6
novos_produtos = ['papel higiênico','pasta de dente']

lista_concatenada = produtos_mercado + novos_produtos
print(lista_concatenada)
['alface', 'tomate', 'arroz', 'ovos', 'carne moída', 'sabão líquido', 'papel higiênico', 'pasta de dente']

4.3.2.2 Métodos de listas

As listas também possuem métodos bastante úteis, que nos facilitam a vida em várias dimensões. Considere uma lista de exemplo nomeada como list_example.

list_example=['a', 'c', 'b']

Dentre os principais métodos aplicáveis a listas e suas funcionalidades podemos citar:

  1. lista_example.append(): adiciona um novo elemento ao fim da lista “lista_example”.
list_example.append('g')

print(list_example)
['a', 'c', 'b', 'g']
  1. lista_example.extend(lista2): toma a lista “lista_example” como argumento e adiciona todos os elementos de lista2 como novos elementos da lista inicial.
l2 = ['e','d','f']
list_example.extend(l2)

print(list_example)
['a', 'c', 'b', 'g', 'e', 'd', 'f']
  1. lista_example.sort(): classifica os elementos de “lista_example” em ordem ascendente.
list_example.sort()

print(list_example)
['a', 'b', 'c', 'd', 'e', 'f', 'g']
  1. lista_example.remove(x): exclui o elemento igual a “x” de “lista_example”. É um método bastante útil quando queremos excluir um elemento específico, mas não sabemos sua posição dentro da lista.
list_example.remove('e')

print(list_example)
['a', 'b', 'c', 'd', 'f', 'g']

Note que a maior parte dos métodos de listas alteram a lista original mas não retornam nenhum valor. Dessa forma, atribuir a um objeto o resultado da aplicação de um método em uma lista não é uma boa prática. Se você escrever ts = t.sort(), por exemplo, ficará desapontado com o resultado.

t = ['d', 'c', 'e', 'b', 'a']
ts = t.sort()

print(ts)
None

Assim como já explicamos, uma lista é uma sequência de valores e uma string é uma sequência de caracteres, mas uma lista de caracteres não é a mesma coisa que uma string. Para converter uma string em uma lista de caracteres, você pode usar o comando list:

s = 'aula'
t = list(s)
print(t)
['a', 'u', 'l', 'a']

A função list quebra uma string em letras individuais. Se você quiser quebrar uma string em palavras, você pode usar o método split(). Esse método admite um argumento adicional, chamado delimiter, que especifica quais caracteres podem ser usados para demonstrar os limites das palavras. Isso é muito útil, por exemplo, quando queremos separar um texto em palavras e fazer a contagem de palavras que mais se repetem. Qual caracter deveríamos passar como argumento em split() nesse caso?

j = 'a pressa é inimiga da perfeição'
s = j.split(' ')

print(s)
['a', 'pressa', 'é', 'inimiga', 'da', 'perfeição']

O método join() é o contrário de split(). Ele toma uma lista de strings e concatena os elementos. join(), no entanto, é um método de string, então é preciso invocá-lo no string delimitador (por exemplo, ” - “) e passar a lista de strings como parâmetro:

s = ['a','pressa','é','inimiga','da','perfeição']
j = ' '.join(s)

print(j)
a pressa é inimiga da perfeição

Além desses principais, existem outros vários métodos para listas. Esse link é um bom ponto de partida para quem quiser conhecer outros exemplos.

4.3.3 Tuplas

Agora falaremos de mais um tipo de objeto básico do Python, a tupla. Uma tupla é uma sequência de valores. Os valores podem ser de qualquer tipo e são indexados por números inteiros, o que faz das tuplas objetos bastante parecidos com as listas. A diferença crucial está no fato de que as tuplas são imutáveis, assim como os strings. Em resumo, uma tupla é uma lista de valores separados por vírgulas e delimitado por parênteses (lembre que listas são delimitadas por colchetes):

t = ('a','b','c','d','e')

print(type(t))
<class 'tuple'>

Note que um único valor entre parênteses não é uma tupla. Para criar uma tupla com um único elemento, é preciso incluir uma vírgula final após o elemento, com ou sem os parênteses.

t1 = ('a')
t2 = 'a',
t3 = ('a',)

print(type(t1))
print(type(t2))
print(type(t3))
<class 'str'>
<class 'tuple'>
<class 'tuple'>

Outra forma de criar uma tupla é com a função integrada tuple. Sem argumentos, cria uma tupla vazia. Se os argumentos forem uma sequência (string, lista ou tupla), o resultado é uma tupla com os elementos da sequência:

t1 = tuple()
t2 = tuple('tupla')

print(t1)
print(t2)
()
('t', 'u', 'p', 'l', 'a')

4.3.3.1 Operações básicas

A maior parte dos operadores de lista também funciona em tuplas. O uso de colchetes após o nome da tupla serve para operações de indexação e fatiamento, selecionando um único elemento ou uma sequência de vários elementos.

t = ('a', 'b', 'c', 'd', 'e')

print(t[0])
print(t[1:3])
a
('b', 'c')

Entretanto, se você tentar alterar um dos elementos da tupla, vai receber um erro. Como tuplas são objetos imutáveis, você não pode alterar seus elementos diretamente. Para “alterar” uma tupla, é preciso criar uma nova tupla.

t[0] = 'A'
---------------------------------------------------------------------------
TypeError                                 Traceback (most recent call last)
Cell In[39], line 1
----> 1 t[0] = 'A'

TypeError: 'tuple' object does not support item assignment

4.3.3.2 Atribuição de tuplas

Muitas vezes, é útil trocar os valores de duas variáveis. Com a atribuição convencional, é preciso usar uma variável temporária. Essa solução é trabalhosa; a atribuição de tuplas é mais elegante. Por exemplo, trocar a e b.

a=5
b=6

temp = a
a = b
b = temp

print(b)
5

Essa solução é trabalhosa; a atribuição de tuplas é mais elegante:

a=5
b=6

a, b = b, a

print(b)
5

O lado esquerdo é uma tupla de variáveis e o lado direito é uma tupla de expressões. Cada valor é atribuído à sua respectiva variável. De forma geral, o lado direito pode ter qualquer tipo de sequência (string, lista ou tupla). Por exemplo, para dividir um endereço de email em um nome de usuário e um domínio, você poderia escrever utilizar o método split. O valor de retorno do split() é uma lista com dois elementos; o primeiro elemento é atribuído a uname, o segundo a domain.

addr = 'monty@python.org'
uname, domain = addr.split('@')

print(uname)
print(domain)
monty
python.org

4.3.3.3 Operações nativas com tuplas

A função nativa zip recebe duas ou mais sequências e devolve uma lista de tuplas onde cada tupla contém um elemento de cada sequência. O nome da função tem a ver com o zíper, que se junta e encaixa duas carreiras de dentes. O resultado é um objeto zip que sabe como percorrer os elementos pareados. O exemplo abaixo encaixa uma string e uma lista:

s = 'abc'
t = [0, 1, 2]
z = zip(s,t)

print(z)
<zip object at 0x000002430EEEF4C0>

Um objeto zip é um tipo de iterador, ou seja, qualquer objeto que percorre ou itera sobre uma sequência. Iteradores são semelhantes a listas em alguns aspectos, mas, ao contrário de listas, não é possível usar um índice para selecionar um elemento de um iterador. Se quiser usar operadores e métodos de lista, você pode usar um objeto zip para fazer uma lista. O resultado é uma lista de tuplas. Neste exemplo, cada tupla contém um caractere da string e o elemento correspondente da lista.

print(list(z))
[('a', 0), ('b', 1), ('c', 2)]

4.3.4 Dicionários

Dicionários são um outro tipo de objeto bastante importante e útil em nossas aplicações. Dicionários são um dos melhores recursos do Python, eles são os blocos de montar de muitos algoritmos eficientes e elegantes.

Um dicionário se parece com uma lista, mas é mais geral. Em uma lista, os índices devem ser números inteiros, em um dicionário eles podem ser de (quase) qualquer tipo. Um dicionário contém uma coleção de índices, que se chamam chaves, e uma coleção de valores. Cada chave é associada com um único valor. A associação de uma chave e um valor chama-se par chave-valor ou item.

Em linguagem matemática, um dicionário representa um mapeamento de chaves a valores, para que você possa dizer que cada chave “mostra o mapa” a um valor. Como exemplo, vamos construir um dicionário que faz o mapa de palavras do inglês ao espanhol, de forma que tanto as chaves quanto os valores sejam strings. A função dict cria um novo dicionário sem itens. Como dict é o nome de uma função integrada, você deve evitar usá-lo como nome de variável.

eng2sp = dict()

eng2sp
{}

As chaves {} representam um dicionário vazio. Para acrescentar itens ao dicionário, você pode usar colchetes.

eng2sp['one'] = 'uno'

Esta linha cria um item que mapeia da chave ‘one’ ao valor ‘uno’. Se imprimirmos o dicionário novamente, vemos um par chave-valor com dois pontos entre a chave e o valor:

eng2sp
{'one': 'uno'}

Este formato de saída também é um formato de entrada. Por exemplo, você pode criar um dicionário com três itens:

eng2sp = {'one': 'uno', 'three': 'tres', 'two': 'dos', 'eleven': 'once'}
eng2sp
{'one': 'uno', 'three': 'tres', 'two': 'dos', 'eleven': 'once'}

Ué, mas a ordem parece diferente daquela que definimos, não? Se você digitar o mesmo exemplo no seu computador, pode receber um resultado diferente. Em geral, a ordem dos itens em um dicionário é imprevisível. No entanto, isso não é um problema porque os elementos de um dicionário nunca são indexados com índices de números inteiros. Em vez disso, você usa as chaves para procurar os valores correspondentes. A chave 'two', por exemplo sempre mapeia ao valor 'dos', assim a ordem dos itens não importa.

eng2sp['two']
'dos'

Se a chave não estiver no dicionário, você recebe uma exceção:

eng2sp['four']
---------------------------------------------------------------------------
KeyError                                  Traceback (most recent call last)
Cell In[50], line 1
----> 1 eng2sp['four']

KeyError: 'four'

4.3.4.1 Operações básicas

Podemos adicionar um novo par de chave e valor ao dicionário usando colchetes. Caso a chave já exista no dicionário, é possível atualizar o valor.

y = {}
y['one'] = 1
y['two'] = 2

print(y)
{'one': 1, 'two': 2}
y['two'] = 'dos'
print(y)
{'one': 1, 'two': 'dos'}

A instrução del pode ser usada para remover uma entrada (par de valor de chave) de um dicionário.

del y['two']
print(y)
{'one': 1}

Tente acessar uma chave que não está em um dicionário e você receberá um erro de exceção do Python. Para lidar com essa exceção, você pode usar o operador in que testa se existe uma chave em um dicionário. Esse operador retorna True se o dicionário tiver um valor armazenado sob a chave fornecida e False caso contrário.

del y['three']
---------------------------------------------------------------------------
KeyError                                  Traceback (most recent call last)
Cell In[54], line 1
----> 1 del y['three']

KeyError: 'three'
'three' in y
False

4.3.4.2 Métodos de dicionários

Considere o dicionário de exemplo abaixo.

dict_example = {'one':1,'two':2,'three':3,'four':4}

Dentre os principais métodos aplicáveis a dicionários e suas funcionalidades podemos citar:

  1. dict_example.update(x): atualiza o dicionário dict_example com todos os pares de valor-chave de um segundo dicionário dict_update. Os valores de chaves, que são comuns a ambos os dicionários, do segundo dicionário irão se sobrepor aos valores das chaves do primeiro dicionário.
dict_update = {'four':4,'five':5,'six':6}

dict_example.update(dict_update)
print(dict_example)
{'one': 1, 'two': 2, 'three': 3, 'four': 4, 'five': 5, 'six': 6}
  1. dict_example.keys(x): permite que você obtenha todas as chaves no dicionário. Muitas vezes é usado dentro de uma série de instruções repetida várias vezes para iterar sobre o conteúdo de um dicionário.
print(dict_example.keys())
dict_keys(['one', 'two', 'three', 'four', 'five', 'six'])
  1. dict_example.items(): retorna todas as chaves do dicionário dict_example e seus valores associados como uma sequência de tuplas.
print(dict_example.items())
dict_items([('one', 1), ('two', 2), ('three', 3), ('four', 4), ('five', 5), ('six', 6)])
  1. dict_example.get(x,y): devolve o valor associado a uma chave “x” se o dicionário contiver essa chave. Caso o dicionário não contenha a chave, você pode especificar um segundo argumento opcional “y” para retornar um valor padrão (se o argumento não estiver incluído o método retornará None).
print(dict_example.get('one'))
1
print(dict_example.get('ten'))
None
print(dict_example.get('ten', 'The key does not exist.'))
The key does not exist.

Além desses principais, existem outros vários métodos para dicionários. Esse link é um bom ponto de partida para quem quiser conhecer outros exemplos.

4.4 Conclusão

Neste capítulo, introduzimos os tipos primitivos do Python – int, float, str e bool – e analisamos como eles representam números, textos e valores lógicos. Discutimos operações fundamentais, indexação, e generalizamos para a noção de objeto, destacando que, em Python, todo valor é um objeto com tipo, identidade e métodos associados. Esse entendimento é pré-requisito para estruturas mais complexas e para o uso de controle de fluxo. A partir daqui, passaremos a trabalhar com coleções de objetos e com operações iterativas, expandindo o escopo dos programas que conseguimos construir.

4.5 Exercícios

  1. Considere as duas listas abaixo. Escreva um programa que as converta em um dicionário em que os itens de keys correspondam às chaves do dicionários e os itens de values os valores.

    keys = ['Ten', 'Twenty', 'Thirty']
    values = [10, 20, 30]
  2. Considere as atribuições abaixo. Sem executar o código, indique o tipo de cada variável. Explique por que a expressão a + d gera um erro. Reescreva-a de forma que a soma aritmética seja realizada corretamente e retorne o valor desejado \(20\).

    a=10
    b=3
    c=10.0
    d=1.25
    e='10'
  3. Considere o string Universidade de São Paulo. Escreva um código que acesse (i) o primeiro caractere; (ii) o último caractere (sem contar manualmente o tamanho do string); (iii) o nome da cidade utilizando fatiamento.

  4. Discorra sobre as principais diferenças entre listas e tuplas. A partir da sua resposta, discuta sobre qual das estruturas é mais indicada para armazenar (i) as notas obtidas por um aluno em determinadas disciplinas e (ii) as coordenadas geográficas de uma cidade (latitude e longitude).