O exemplo anterior de abstração procedural chamou uma função do Python chamada sqrt da biblioteca de funções matemáticas (math) para calcular a raiz quadrada. Em geral, podemos ocultar os detalhes de qualquer computação definindo uma função. Uma definição de função requer um nome, um grupo de parâmetros e um corpo. Também pode retornar explicitamente um valor. Por exemplo, a função simples definida abaixo retorna o quadrado do valor que você passa para ele. >>> def quadrado(n): ... return n**2 ... >>> quadrado(3) 9 >>> quadrado(quadrado(3)) 81 >>> A sintaxe para esta definição de função inclui o nome, quadrado, e uma lista de parâmetros formais entre parênteses. Para esta função, n é o único parâmetro formal, que sugere que quadrado precisa de apenas um dado para fazer o seu trabalho. Os detalhes, escondidos “dentro da caixa” simplesmente calcula o resultado de n ** 2 e o retorna. Nós podemos invocar ou chamar a função quadrado pedindo ao ambiente Python para avaliá-lo, passando um valor como argumento, neste caso, 3. Note que a chamada para quadrado retorna um inteiro que por sua vez pode ser passado para outra chamada. Poderíamos implementar nossa própria função de raiz quadrada usando uma técnica chamada “Método de Newton”. O método de Newton para a aproximação de raízes quadradas realiza um cálculo iterativo que converge para o valor correto. A equação \(raiz\_nova = \frac {1}{2} * (raiz\_anterior + \frac {n}{raiz\_anterior})\) assume um valor \(n\) e aproxima repetidamente a raiz quadrada fazendo cada \(raiz\_nova\) igual a \(raiz\_anterior\) na iteração seguinte. O palpite inicial usado aqui é \(\frac {n}{2}\). A Listagem 1 mostra uma definição de função que aceita um valor \(n\) e retorna a raiz quadrada de \(n\) depois de fazer 20 aproximações. Mais uma vez, os detalhes do Método de Newton estão escondidos dentro da definição da função e o usuário não precisa saber nada sobre a implementação para usar a função para o propósito pretendido. A Listagem 1 também mostra o uso do caractere # como um marcador de comentário. Quaisquer caracteres que seguem o # em uma linha são ignorados. Listagem 1 def raiz_quadrada(n): raiz = n/2 # chute inicial será 1/2 de n for k in range(20): raiz = (1/2)*(raiz + (n / raiz)) return raiz >>>raiz_quadrada(9) 3.0 >>>raiz_quadrada(4563) 67.549981495186216 >>>
Auto Avaliação Aqui está um auto-teste que realmente cobre tudo até agora. Você pode ter ouvido falar do teorema do macaco infinito? O teorema afirma que um macaco apertando teclas aleatoriamente em um teclado de máquina de escrever por um período infinito de tempo quase certamente digitará um determinado texto como as obras completas de William Shakespeare. Bem, suponha que substituímos um macaco por uma função Python. Quanto tempo você acha que seria necessário para uma função Python gerar apenas uma frase de Shakespeare? A frase que vamos tentar em inglês é: “methinks it is like a weasel” (“parece que é como uma doninha”). Você não vai querer rodar essa função no navegador, então abra seu IDE Python favorito. A maneira como vamos simular isso é escrever uma função que gera uma cadeia de 28 caracteres, escolhendo letras aleatórias das 26 letras do alfabeto inglês mais o espaço. Vamos escrever outra função que irá avaliar cada string gerada comparando a string gerada aleatoriamente com a meta. Uma terceira função irá chamar repetidamente as funções gerar e avaliar e, quando 100% das letras estiverem corretas, nós terminamos. Se as letras não estiverem corretas, geraremos uma string totalmente nova. Para facilitar o acompanhamento do progresso do programa, essa terceira função deve imprimir a melhor string gerada até agora e sua pontuação a cada 1000 tentativas.
Auto Avaliação - Desafio Veja se você pode melhorar o programa na autoverificação mantendo as letras corretas e modificando apenas um caractere na melhor string até o momento. Este é um tipo de algoritmo da classe ‘Hill Climbing’, ou seja, só mantemos o resultado se for melhor que o anterior. Page 2
Existem dois tipos de erros que normalmente ocorrem ao escrever programas. O primeiro, conhecido como erro de sintaxe, significa simplesmente que programador cometeu um erro na estrutura de um comando ou expressão. Por exemplo, é incorreto escrever um comado for e esquecer os dois pontos. >>> for i in range(10) SyntaxError: invalid syntax (<pyshell#61>, line 1) Nesse caso, o interpretador Python descobriu que não pode concluir o processamento desta instrução, uma vez que não está em conformidade com as regras da linguagem. Erros de sintaxe são geralmente mais frequentes quando você estão aprendendo uma linguagem. O outro tipo de erro, conhecido como erro lógico, denota uma situação onde o programa é executado, mas dá o resultado errado. Isso pode ser devido a um erro no algoritmo ou a um erro na sua tradução do algoritmo. Em alguns casos, erros lógicos levam a situações muito ruins como tentar dividir por zero ou tentar acessar um item de uma lista em que o índice do item está fora dos limites da lista. Neste caso, o erro lógico leva a um erro de execução que faz com que o programa termine. Esses tipos de erro em tempo de execução são geralmente chamados de exceções. Na maioria das vezes, os programadores iniciantes simplesmente pensam em exceções como erros fatais em tempo de execução que causam o fim da execução. No entanto, a maioria das linguagens de programação fornecem uma maneira de lidar com esses erros que permitem ao programador algum tipo de intervenção, se assim desejarem. Além disso, os programadores podem criar suas próprias exceções se detectarem situações na execução do programa que as justifiquem. Quando ocorre uma exceção, dizemos que ela foi “levantada”. Você pode “tratar” a exceção que foi levantada usando um comando try. Por exemplo, considere o seguinte fragmento de código que pergunta ao usuário para digitar um inteiro e, em seguida, chama a função raiz quadrada (sqrt) da biblioteca matemática (math). Se o usuário inserir um valor maior ou igual a para 0, a impressão mostrará a raiz quadrada. No entanto, se o usuário inserir um valor negativo, a função raiz quadrada irá reportar uma exceção do tipo ValueError. >>> num = int(input("Digite um número inteiro: ")) Digite um número inteiro: -23 >>> print(math.sqrt(num)) Traceback (most recent call last): File "<pyshell#102>", line 1, in <module> print(math.sqrt(num)) ValueError: math domain error >>> Podemos lidar com essa exceção chamando a função print de dentro de um bloco try. Um bloco except correspondente “captura” a exceção e imprime uma mensagem de volta ao usuário caso uma exceção ocorra. Por exemplo: >>> try: print(math.sqrt(num)) except: print("Valor impróprio para raiz quadrada") print("Usando valor absoluto em seu lugar") print(math.sqrt(abs(num))) Valor impróprio para raiz quadrada Usando valor absoluto em seu lugar 4.79583152331 >>> vai pegar o fato de que uma exceção é levantada por sqrt e vai em vez disso, imprimir as mensagens de volta para o usuário e usar o valor absoluto para ter certeza de que estamos pegando a raiz quadrada de um número não negativo. Isso significa que o programa não terminará, mas continuará a executar os comandos seguintes. Também é possível para um programador causar uma exceção em tempo de execução usando o comando raise. Por exemplo, em vez de chamar a função raiz quadrada com um número negativo, poderíamos primeiro ter verificado o valor e, em seguida, levantar nossa própria exceção. O fragmento de código abaixo mostra o resultado da criação de uma nova exceção do tipo RuntimeError (erro em tempo de execução). Observe que o programa ainda terminaria, mas agora a exceção que causa o término é algo explicitamente criado pelo programador. >>> if num < 0: ... raise RuntimeError("Você não pode usar um valor negativo") ... else: ... print(math.sqrt(num)) ... Traceback (most recent call last): File "<stdin>", line 2, in <module> RuntimeError: Você não pode usar um valor negativo >>> Existem muitos tipos de exceção que podem ser levantados além do RuntimeError mostrado acima. Veja o manual de referência do Python para uma lista de todos os tipos de exceção disponíveis e de como criar suas próprias exceções. Page 3
Como observamos anteriormente, algoritmos exigem duas importantes estruturas de controle: iteração e seleção. Ambas são suportadas pelo Python em várias formas. O programador pode escolher o comando que lhe for mais útil dada uma circunstância. Para a iteração, o Python fornece um comando padrão while e um comando muito poderoso for. O comando while repete um corpo de código enquanto uma condição for verdadeira. Por exemplo, >>> contador = 1 >>> while contador <= 5: ... print("Olá, mundo") ... contador = contador + 1 Olá, mundo Olá, mundo Olá, mundo Olá, mundo Olá, mundo imprime a frase “Olá, mundo” cinco vezes. A condição no comando while é avaliada no início de cada repetição. Se o condição é True (verdadeira), o corpo do comando será executado. É fácil ver a estrutura de um comando while do Python devido ao padrão de tabulação obrigatório imposto pela linguagem. O comando while é uma estrutura iterativa de propósito geral que usaremos em vários algoritmos diferentes. Em muitos casos, uma condição composta controlará a iteração. Um fragmento como while contador <= 10 and not acabou: ... faria com que o corpo da declaração fosse executado apenas no caso onde ambas as partes da condição são satisfeitas. O valor da variável contador precisaria ser menor ou igual a 10 e a variável acabou precisaria ser False (pois not False é True) para que True and True resulte em True. Mesmo que este tipo de construção seja muito útil em uma ampla variedade de situações, outra estrutura iterativa, o comando for, pode ser usado em conjunto com muitas das coleções do Python. O comando for pode ser usado para iterar sobre os membros de uma coleção, desde que a coleção seja uma sequência. Então, por exemplo, >>> for item in [1,3,6,2,5]: ... print(item) ... 1 3 6 2 5 atribui à variável item a cada valor sucessivo na lista [1,3,6,2,5]. O corpo da iteração é então executado. Isso funciona para qualquer coleção que seja uma sequência (listas, tuplas e strings). Um uso comum do comando for é implementar uma iteração definida em um intervalo de valores. O comando >>> for item in range(5): ... print(item**2) ... 0 1 4 9 16 >>> executará a função print cinco vezes. A função range retornará um objeto de intervalo representando a sequência 0,1,2,3,4 e cada valor será atribuído à variável item. Este valor é então elevado ao quadrado e impresso. A outra versão muito útil desta estrutura de iteração é usada para processar cada caractere de uma string. O seguinte fragmento de código itera sobre uma lista de strings e para cada string processa cada caractere, anexando-o no final de uma lista. O resultado é uma lista de todas as letras em todas as palavras. lista_palavras = ['gato','rato','coelho'] lista_letras = [ ] for palavra in lista_palavras: for letra in palavra: lista_letras.append(letra) print(lista_letras) Os comandos de seleção permitem que os programadores façam perguntas e, em seguida, dependendo do resultado, execute ações diferentes. A maioria das linguagens de programação fornecem duas versões desta construção útil: o ifelse (se-senão) e o if (se). Segue um exemplo simples de uma seleção binária usando o comando ifelse. if n<0: print("Desculpe, o valor é negativo") else: print(math.sqrt(n)) Neste exemplo, o objeto referido por n é verificado para ver se é menor que zero. Se for, uma mensagem é impressa informando que é negativo. Se não for, a declaração executa a cláusula else e calcula a raiz quadrada. Construções de seleção, assim como em qualquer construção de controle, podem ser aninhadas de forma que o resultado de uma pergunta ajuda a decidir o que perguntar em seguida. Por exemplo, suponha que nota seja uma variável contendo uma referência a uma pontuação para um teste de ciência da computação. if nota >= 90: print('A') else: if nota >=80: print('B') else: if nota >= 70: print('C') else: if nota >= 60: print('D') else: print('F') Este fragmento irá classificar um valor chamado nota imprimindo uma categoria correspondente de A a F. Se a pontuação for maior ou igual a 90, o código irá imprimir A. Senão (else), a próxima pergunta é feita. Se a pontuação for maior ou igual a 80, então a nota deve estar entre 80 e 89, uma vez que a resposta à primeira questão foi falsa. Neste caso, a categoria B é impressa. Você pode ver que o padrão de tabulação do Python ajuda a dar sentido à associação entre if e else sem precisar de elementos sintáticos adicionais. Uma sintaxe alternativa para esse tipo de seleção aninhada usa o palavra-chave elif. O else e o próximo if são combinados de modo a eliminar a necessidade de níveis adicionais de aninhamento. Note que o else final ainda é necessário para fornecer o caso default se todas as outras condições falharem. if nota >= 90: print('A') elif nota >=80: print('B') elif nota >= 70: print('C') elif nota >= 60: print('D') else: print('F') O Python também possui uma construção de seleção unária, o comando if. Com esse comando, se a condição for verdadeira, uma ação será executada. No caso da condição ser falsa, o processamento simplesmente continua para a próxima instrução após o if. Por exemplo, o seguinte fragmento irá primeiro verificar se o valor de uma variável n é negativo. Se for, então é modificado pela função de valor absoluto. Independentemente disso, a próxima ação é calcular a raiz quadrada. if n<0: n = abs(n) print(math.sqrt(n))
Auto avaliação Teste sua compreensão do que cobrimos até agora resolvendo o seguinte exercício. Modifique o código do Activecode 8 para que a lista final contenha apenas uma única cópia de cada letra. # a resposta é: ['g', 'a', 't', 'o', 'r', 'c', 'e', 'l', 'h'] Voltando às listas, existe um método alternativo para criar uma lista que usa construções de iteração e seleção conhecidas como preenchimento de lista (do inglês list comprehension). Uma construção para preenchimento de lista permite que você crie facilmente uma lista com base em alguns critérios de processamento ou seleção. Por exemplo, se quiséssemos criar uma lista dos primeiros 10 quadrados perfeitos, poderíamos usar um comando for: >>> quadrados=[] >>> for x in range(1,11): quadrados.append(x*x) >>> quadrados [1, 4, 9, 16, 25, 36, 49, 64, 81, 100] >>> Usando preenchimento de lista, podemos fazer isso em um passo apenas >>> quadrados=[x*x for x in range(1,11)] >>> quadrados [1, 4, 9, 16, 25, 36, 49, 64, 81, 100] >>> A variável x assume os valores de 1 a 10, conforme especificado pelo comando for. O valor de x * x é então computado e adicionado à lista que está sendo construída. A sintaxe geral de um preenchimento de lista também permite que um critério de seleção seja adicionado de forma que somente certos itens sejam adicionados. Por exemplo, >>> quadrados=[x*x for x in range(1,11) if x%2 != 0] >>> quadrados [1, 9, 25, 49, 81] >>> Esse preenchimento de lista construiu uma lista que contém apenas quadrados dos números ímpares no intervalo de 1 a 10. Qualquer sequência que suporta iteração pode ser usada dentro de um preenchimento de lista para construir uma nova lista. >>>[c.upper() for c in 'preenchimento' if c not in 'aeiou'] ['P', 'R', 'N', 'C', 'H', 'M', 'N', 'T'] >>>
Auto Avaliação Teste sua compreensão sobre preenchimento de lista refazendo o Activecode 8 usando preenchimentos de lista. Para um desafio extra, veja se você consegue descobrir como remover as duplicatas. # the answer is: ['g', 'a', 't', 'o', 'r', 'a', 't', 'o', 'c', 'o', 'e', 'l', 'h', 'o'] Page 4Geralmente, precisamos interagir com os usuários para obter dados ou fornecer algum tipo de resultado. A maioria dos programas hoje usa uma caixa de diálogo como forma de pedir ao usuário para fornecer algum tipo de entrada. Embora o Python tenha uma maneira de criar caixas de diálogo, há uma função muito mais simples que podemos usar. O Python nos fornece uma função que permite pedir a um usuário para inserir alguns dados e retorna um referência aos dados na forma de uma string. A função é chamada input. A função input do Python recebe um único parâmetro que é uma string. Essa string é frequentemente chamada de prompt porque contém um texto informativo que informa o usuário para digitar alguma coisa. Por exemplo, você pode chamar a função input da seguinte forma: umNome = input('Por favor digite o seu nome: ') Agora, o que o usuário digitar após o prompt será armazenado na variável umNome. Usando a função input, podemos facilmente escrever instruções que solicitarão ao usuário que insira dados e, em seguida, incorporar esses dados no processamento. Por exemplo, nas duas seguintes instruções, a primeira pergunta ao usuário pelo nome e a segunda imprime o resultado de algum processamento simples baseado na string que fornecida. umNome = input('Por favor digite o seu nome: ') print("Seu nome todo em maiúsculas é", umNome.upper(), "e tem comprimento", len(umNome)) É importante notar que o valor retornado pela função input será uma string representando os caracteres exatos que foram inserido após o prompt. Se você quiser que essa string seja interpretada como outro tipo, você deve fornecer a conversão de tipo explicitamente. Nas declarações abaixo, a string inserida pelo usuário é convertida em um float que pode ser usado em seguida para processamento aritmético. raio_str = input("Por favor digite o raio do círculo: ") raio = float(raio_str) diametro = 2 * raio
Nós já vimos que a função print fornece uma maneira muito simples para exibir valores de saída de um programa em Python. O print aceita zero ou mais parâmetros e os exibe usando um único espaço em branco como o separador padrão. É possível alterar o caracter separador, definindo o parâmetro opcional sep. Além disso, cada print termina com um caractere de nova linha por default. Esse comportamento pode ser alterado, definindo o parâmetro opcional end. Estas variações são mostradas a seguir: >>> print("Olá") Olá >>> print("Olá","Mundo") Olá Mundo >>> print("Olá","Mundo", sep="***") Olá***Mundo >>> print("Olá","Mundo", end="***") Olá Mundo*** >>> Geralmente, é útil ter mais controle sobre a aparência da sua saída. Felizmente, o Python nos fornece uma alternativa chamada string de formatação. Uma string de formatação é um modelo (template) no qual palavras ou espaços que permanecerão constantes são combinados com espaços reservados para variáveis que serão inseridas na string. Por exemplo, o comando print(umNome, "tem", anos, "anos de idade.") contém as palavras tem `` e ``anos de idade que são constantes, mas o nome e a idade dependem do valor das variáveis no momento da execução. Usando uma string de formatação, nós escrevemos o comando anterior como print("%s tem %d anos de idade." % (umNome, anos)) Este exemplo simples ilustra uma nova expressão com strings. O operador % é chamado operador de formatação. O lado esquerdo da expressão contém o modelo ou string de formatação, e o lado direito contém uma coleção de valores que serão substituídos na string de formatação. Observe que o número de valores na coleção do lado direito corresponde ao número de caracteres % na string de formatação. Os valores são usados na sequência, da esquerda para a direita da coleção e inseridos na string de formatação. Vamos ver os dois lados dessa expressão de formatação em mais detalhes. A string de formatação pode conter uma ou mais especificações de conversão. Um caractere de conversão informa ao operador de formatação que tipo de dado vai ser inserido nessa posição na string. No exemplo acima, o %s especifica uma string, enquanto o %d especifica um inteiro. Outras especificações possíveis incluem i, u, f, e, g, c ou %. A Tabela 9 resume todos os diversos tipos de caracteres de conversão.
Além do caractere de conversão, você também pode incluir um modificador de formato entre o caractere % e o caractere de conversão. Modificadores de formato podem ser usados para justificar o valor à esquerda ou à direita, dentro de um campo com determinada largura. Modificadores também podem ser usados para especificar a largura do campo bem como um número de dígitos após o ponto decimal. A Tabela 10 explica esses modificadores de formato
O lado direito do operador de formatação é uma coleção de valores que será inserido na string de formatação. A coleção deve ser uma tupla ou um dicionário. Se a coleção for uma tupla, os valores são inseridos na mesma ordem da tupla. Ou seja, o primeiro elemento na tupla corresponde ao primeiro caractere de conversão na string de formatação. Se a coleção for um dicionário, os valores são inseridos de acordo com sua chaves. Neste caso, todos os caracteres de formato devem usar o modificador (nome) para especificar o nome da chave. >>> preço = 24 >>> item = "banana" >>> print("O preço do kilo de %s é %d reais"%(item,preço)) O preço do kilo de banana é 24 reais >>> print("O preço do kilo de %+10s é %5.2d reais"%(item,preço)) O preço do kilo de banana é 24.00 reais >>> print("O preço do kilo de %+10s é %10.2f reais"%(item,preço)) O preço do kilo de banana é 24.00 reais >>> dicio = {"item":"banana","preço":24} >>> print("O preço do kilo de %(item)s é %(preço)7.1f reais"%dicio) O preço do kilo de banana é 24.0 reais >>> Além das strings de formatação que usam caracteres de conversão e modificadores de formato, as strings do Python também incluem um método format que pode ser usado em conjunto com uma nova classe Formatter para implementar formatações mais complexas. Mais sobre esses recursos podem ser encontrados no manual de referência do Python. Page 5Nós afirmamos acima que o Python suporta o paradigma de programação orientada a objetos. Isso significa que o Python considera os dados como o ponto focal do processo de resolução de problemas. Em Python, assim como em qualquer outra linguagem de programação orientada a objetos, definimos uma classe para ser uma descrição de como os dados se parecem (o estado) e o que os dados podem fazer (o comportamento). As classes são análogas aos tipos de dados abstratos porque um usuário de uma classe só vê o estado e o comportamento de um item de dados. Os itens de dados são chamados de objetos no paradigma orientado a objeto. Um objeto é uma instância de uma classe.
Vamos começar nossa revisão considerando os tipos de dados atômicos. O Python tem duas classes numéricas nativas principais que implementam os tipos inteiro e ponto flutuante. Essas classes do Python são chamadas de int e float. As operações aritméticas padrão, +, -, *, / e ** (exponenciação), pode ser usado com parênteses forçando a ordem de operações fora da precedência normal do operador. Outras operações muito úteis são o operador resto de divisão (módulo), %, e divisão inteira, //. Note que quando dois inteiros são divididos, o resultado é um ponto flutuante. O operador de divisão inteira retorna a porção inteira do quociente truncando qualquer parte fracionária. print(2+3*4) print((2+3)*4) print(2**10) print(6/3) print(7/3) print(7//3) print(7%3) print(3/6) print(3//6) print(3%6) print(2**100) O tipo de dado booleano, implementado como a classe bool do Python, será bastante útil para representar valores booleanos. Os possíveis valores de estado para um objeto booleano são True e False com os operadores booleanos, and, or e not. >>> True True >>> False False >>> False or True True >>> not (False or True) False >>> True and True True Objetos de dados booleanos também são usados como resultado para operadores de comparação como igualdade (==) e maior que (\(>\)). Além disso, operadores relacionais e operadores lógicos podem ser combinados para formar questões lógicas complexas. A Tabela 1 mostra os operadores relacionais e lógicos com exemplos mostrados no trecho de código seguinte.
print(5==10) print(10 > 5) print((5 >= 1) and (5 <=> </=> Identificadores são usados em linguagens de programação como nomes. Em Python, identificadores começam com uma letra ou um sublinhado (_), são sensíveis a letras minúsculas e maiúsculas, e podem ser de qualquer comprimento. Lembre-se que é sempre uma boa idéia usar nomes que transmitem significado para que o código de seu programa seja mais fácil de ler e entender. Uma variável em Python é criada quando um nome é usado pela primeira vez do lado esquerdo de um comando de atribuição. Comandos de atribuição fornecem uma maneira de associar um nome a um valor. A variável manterá uma referência a um pedaço dos dados e não os dados em si. Considere o seguinte trecho de código: >>> theSum = 0 >>> theSum 0 >>> theSum = theSum + 1 >>> theSum 1 >>> theSum = True >>> theSum True O comando de atribuição theSum = 0 cria uma variável chamada theSum e permite manter a referência ao objeto de dados 0 (veja Figura 3). Em geral, o lado direito do comando de atribuição é avaliado e uma referência ao objeto de dados resultante é “atribuída” ao nome no lado esquerdo. Neste ponto em nosso exemplo, o tipo da variável é inteiro por ser esse o tipo de dado atualmente sendo referido por theSum. Se o tipo de dado for modificado (veja Figura 4), como mostrado acima com o valor booleano True, o mesmo acontece com o tipo da variável (theSum é agora do tipo booleano). O comando de atribuição altera a referência sendo mantida pela variável. Essa é uma característica dinâmica do Python. A mesma variável pode se referir a muitos tipos diferentes de dados.
Figure 3: Variáveis mantém referências a objetos de dados¶
Figure 4: Atribuição modifica a referência¶
Além das classes numéricas e booleanas, o Python possui várias classes nativas muito poderosas para coleções. Listas (tipo list), strings (tipo str) e tuplas (tipo tuple) são coleções ordenadas que são muito semelhantes na estrutura geral, mas tem diferenças específicas que devem ser entendidas para serem usadas devidamente. Conjuntos (tipo set) e dicionários (tipo dict) são coleções não ordenadas. Uma lista é uma coleção ordenada de zero ou mais referências a objetos de dados em Python. As listas são escritas como valores separados por vírgulas e delimitadas por colchetes. A lista vazia é simplesmente []. Listas são heterogêneas, o que significa que os objetos de dados não precisam ser todos da mesma classe e a coleção pode ser atribuída a uma variável como abaixo. O trecho de código a seguir mostra vários objetos de dados do Python em uma lista. >>> [1,3,True,6.5] [1, 3, True, 6.5] >>> minhaLista = [1,3,True,6.5] >>> minhaLista [1, 3, True, 6.5] Observe que, quando o Python avalia uma lista, a própria lista é retornada. No entanto, para lembrar da lista para processamento posterior, sua referência precisa ser atribuída a uma variável. Como as listas são consideradas sequencialmente ordenadas, elas suportam um número de operações que podem ser aplicadas a qualquer sequência do Python. A Tabela 2 revisa essas operações e o trecho de código seguinte fornece exemplos de seu uso.
Note que os índices das listas (sequências) começam a contar do 0. A operação de fatiamento, minhaLista[1:3], retorna uma lista de itens que começam com o item indexado por 1 até, mas não incluindo o item indexado por 3. Às vezes, você desejará inicializar uma lista. Isso pode ser realizado rapidamente usando repetição. Por exemplo, >>> minhaLista = [0] * 6 >>> minhaLista [0, 0, 0, 0, 0, 0] Um lado muito importante relacionado ao operador de repetição é que o resultado é uma repetição de referências aos objetos de dados na sequência. Isso pode ser melhor visto considerando o seguinte trecho de código: minhaLista = [1,2,3,4] A = [minhaLista]*3 print(A) minhaLista[2]=45 print(A) A variável A contém uma coleção de três referências à lista original chamada minhaLista. Observe que uma alteração em um elemento de minhaLista aparece em todas as três ocorrências em A. As listas suportam vários métodos que serão usados para criar estruturas de dados. A Tabela 3 fornece um resumo. Exemplos são mostrados em seguida.
minhaLista = [1024, 3, True, 6.5] minhaLista.append(False) print(minhaLista) minhaLista.insert(2,4.5) print(minhaLista) print(minhaLista.pop()) print(minhaLista) print(minhaLista.pop(1)) print(minhaLista) minhaLista.pop(2) print(minhaLista) minhaLista.sort() print(minhaLista) minhaLista.reverse() print(minhaLista) print(minhaLista.count(6.5)) print(minhaLista.index(4.5)) minhaLista.remove(6.5) print(minhaLista) del minhaLista[0] print(minhaLista) Você pode ver que alguns dos métodos, como pop, retornam um valor e também modificam a lista. Outros, como reverse, simplesmente modificam a lista sem valor de retorno. pop por default remove e devolve o último item da lista mas também pode remover e devolver um item específico. O intervalo de índices começando de 0 é novamente usado por esses métodos. Você também deve notar a notação familiar usando “ponto” para pedir a um objeto para invocar um método. minhaLista.append(False) pode ser lido como “pergunte ao objeto minhaLista para executar seu método append passando-lhe o valor False”. Mesmo objetos de dados simples, como inteiros, podem invocar métodos dessa maneira. >>> (54).__add__(21) 75 >>> Neste fragmento estamos pedindo ao objeto inteiro 54 para executar seu método add (chamado __add__ em Python) e passando-lhe 21 como valor a adicionar. O resultado é a soma, 75. Claro, nós geralmente escrevemos isto como 54 + 21. Falaremos muito mais sobre esses métodos mais tarde nesta seção. Uma função comum do Python que é frequentemente discutida em conjunto com listas é a função range. A função range produz um objeto de intervalo (range object) que representa uma sequência de valores. Usando a função list, é possível ver o valor do objeto de intervalo como uma lista. Isto é ilustrado abaixo. >>> range(10) range(0, 10) >>> list(range(10)) [0, 1, 2, 3, 4, 5, 6, 7, 8, 9] >>> range(5,10) range(5, 10) >>> list(range(5,10)) [5, 6, 7, 8, 9] >>> list(range(5,10,2)) [5, 7, 9] >>> list(range(10,1,-1)) [10, 9, 8, 7, 6, 5, 4, 3, 2] >>> O objeto de intervalo representa uma sequência de inteiros. Por padrão, ela começa com 0. Se você fornecer mais parâmetros, ela começará e terminará em pontos específicos e pode até mesmo pular itens. Em nosso primeiro exemplo, range(10), a sequência começa com 0 e vai até, mas não inclui, o 10. Em nosso segundo exemplo, range(5,10) começa em 5 e vai até, mas não inclui, o 10. range(5,10,2) tem efeito similar, mas pula de dois em dois (mais uma vez, 10 não está incluído). Strings são coleções sequenciais de zero ou mais letras, números e outros símbolos. Nós chamamos essas letras, números e outros símbolos de caracteres. Valores literais de string são diferenciados dos identificadores usando aspas (simples ou duplas). >>> "David" 'David' >>> meuNome = "David" >>> meuNome[3] 'i' >>> meuNome*2 'DavidDavid' >>> len(meuNome) 5 >>> Como strings são sequências, todas as operações de sequência descritas acima funcionam como seria de se esperar. Além disso, as strings têm vários métodos, alguns dos quais são mostrados na Tabela 4. Por exemplo, >>> meuNome 'Daniel' >>> meuNome.upper() 'DANIEL' >>> meuNome.center(10) ' Daniel ' >>> meuNome.find('n') 2 >>> meuNome.split('n') ['Da', 'iel'] Destes, split será muito útil para processar dados. split pegará uma string e retornará uma lista de strings usando a string de entrada como um ponto de divisão. No exemplo, v é o ponto de divisão. Se nenhuma divisão for especificada, o método de divisão procurará caracteres de espaço em branco, como tabulação, nova linha e espaço.
Uma diferença importante entre listas e strings é que as listas podem ser modificado enquanto strings não podem. Isso é chamado de mutabilidade. As listas são mutáveis; strings são imutáveis. Por exemplo, você pode alterar um item em uma lista usando indexação e atribuição. Com uma string essa mudança não é permitida. >>> minhaLista [1, 3, True, 6.5] >>> minhaLista[0]=2**10 >>> minhaLista [1024, 3, True, 6.5] >>> >>> meuNome 'daniel' >>> meuNome[0]='X' Traceback (most recent call last): File "<pyshell#84>", line 1, in -toplevel- meuNome[0]='X' TypeError: object doesn't support item assignment >>> As tuplas são muito semelhantes às listas por serem sequências de dados heterogêneas. A diferença é que uma tupla é imutável, como uma string. Uma tupla não pode ser alterada. As tuplas são escritas como valores delimitados por vírgula fechados entre parênteses. Como sequências, eles podem usar qualquer operação descrita acima. Por exemplo, >>> minhaTupla = (2,True,4.96) >>> minhaTupla (2, True, 4.96) >>> len(minhaTupla) 3 >>> minhaTupla[0] 2 >>> minhaTupla * 3 (2, True, 4.96, 2, True, 4.96, 2, True, 4.96) >>> minhaTupla[0:2] (2, True) >>> Porém, se você tentar modificar um item em uma tupla, você vai obter um erro. Note que a mensagem de erro fornece o local e a razão do problema. >>> minhaTupla[1]=False Traceback (most recent call last): File "<pyshell#137>", line 1, in -toplevel- minhaTupla[1]=False TypeError: object doesn't support item assignment >>> Um conjunto é uma coleção não ordenada de zero ou mais objetos de dados imutáveis do Python. Conjuntos não permitem duplicatas e são gravados como valores delimitados por vírgula fechados entre chaves. O conjunto vazio é representado por set(). Conjuntos são heterogêneos e a coleção pode ser atribuída a uma variável como abaixo. >>> {3,6,"gato",4.5,False} {False, 4.5, 3, 6, 'gato'} >>> umConjunto = {3,6,"gato",4.5,False} >>> umConjunto {False, 4.5, 3, 6, 'gato'} >>> Mesmo que os conjuntos não sejam considerados sequenciais, eles suportam algumas das operações familiares apresentadas anteriormente. A Tabela 5 descreve estas operações e o trecho de código seguinte dá exemplos de seu uso.
>>> umConjunto {False, 4.5, 3, 6, 'gato'} >>> len(umConjunto) 5 >>> False in umConjunto True >>> "rato" in umConjunto False >>> Os conjuntos suportam vários métodos que devem ser familiares àqueles que já trabalharam com eles em um contexto matemático. A Tabela 6 fornece um resumo e exemplos de seu uso. Note que union, intersection, issubset e difference possuem operadores que pode ser usados também.
>>> umConjunto {False, 4.5, 3, 6, 'gato'} >>> outroConjunto = {99,3,100} >>> umConjunto.union(outroConjunto) {False, 4.5, 3, 100, 6, 'gato', 99} >>> umConjunto | outroConjunto {False, 4.5, 3, 100, 6, 'gato', 99} >>> umConjunto.intersection(outroConjunto) {3} >>> umConjunto & outroConjunto {3} >>> umConjunto.difference(outroConjunto) {False, 4.5, 6, 'gato'} >>> umConjunto - outroConjunto {False, 4.5, 6, 'gato'} >>> {3,100}.issubset(outroConjunto) True >>> {3,100}<=outroConjunto True >>> umConjunto.add("casa") >>> umConjunto {False, 4.5, 3, 6, 'casa', 'gato'} >>> umConjunto.remove(4.5) >>> umConjunto {False, 3, 6, 'casa', 'gato'} >>> umConjunto.pop() False >>> umConjunto {3, 6, 'casa', 'gato'} >>> umConjunto.clear() >>> umConjunto set() >>> Nossa última coleção do Python é uma estrutura desordenada chamada dicionário. Dicionários são coleções de pares associados de itens onde cada par consiste de uma chave e um valor. Este par chave-valor é tipicamente escrito como chave:valor. Dicionários são escritos como pares chave:valor separados por vírgula e delimitados por chaves. Por exemplo, >>> capitais = {'Amazonas':'Manaus','Paraná':'Curitiba'} >>> capitais {'Amazonas': 'Manaus', 'Paraná': 'Curitiba'} >>> Podemos manipular um dicionário acessando um valor por meio de sua chave ou adicionando outro par chave-valor. A sintaxe de acesso se parece muito com um acesso de sequência, exceto que em vez de usar o índice do item nós usamos o valor da chave. Para adicionar um novo valor é semelhante. capitais = {'Amazonas':'Manaus','Paraná':'Curitiba'} print(capitais['Amazonas']) capitais['Minas Gerais']='Belo Horizonte' print(capitais) capitais['Bahia']='Salvador' print(len(capitais)) for k in capitais: print(capitais[k]," é a capital de[a/o] ", k) É importante notar que o dicionário é mantido sem nenhuma ordem em particular com relação às chaves. Assim, apesar do primeiro par adicionado tenha sido 'Amazonas': 'Manaus' e o último 'Bahia': 'Salvador', ao percorrer o dicionário usando o comando for por exemplo (linha 7), a ordem das chaves é incerta. A ordem de uma chave depende da ideia de “hashing” que será explicada em mais detalhes no Capítulo 4. Também mostramos a função length executando o mesmo papel que nas coleções anteriores. Os dicionários têm métodos e operadores, descritos na Tabela 7 e na Tabela 8. A sessão de Python os mostra em ação. Os métodos keys, values e items retornam objetos que contém os valores de interesse. Você pode usar a função list para convertê-los em listas. Observe também que existem duas variações no método get. Se a chave não estiver presente no dicionário, get retornará None. No entanto, um segundo parâmetro opcional pode especificar um outro valor de retorno.
>>> ramal={'daniel':1410,'adriana':1137} >>> ramal {'adriana': 1137, 'daniel': 1410} >>> ramal.keys() dict_keys(['adriana', 'daniel']) >>> list(ramal.keys()) ['adriana', 'daniel'] >>> ramal.values() dict_values([1137, 1410]) >>> list(ramal.values()) [1137, 1410] >>> ramal.items() dict_items([('adriana', 1137), ('daniel', 1410)]) >>> list(ramal.items()) [('adriana', 1137), ('daniel', 1410)] >>> ramal.get("pedro") >>> ramal.get("pedro","SEM RAMAL") 'SEM RAMAL' >>>
Rascunho Essa área de trabalho é fornecida para sua conveniência. Você pode usar essa janela de activecode para testar qualquer coisa que desejar. Page 6
Nesta seção revisaremos a linguagem de programação Python e também forneceremos alguns exemplos mais detalhados das idéias da seção anterior. Se você é iniciante em Python ou acha que precisa de mais informações sobre qualquer um dos tópicos apresentados, recomendamos que você consulte um recurso como a Documentação da Linguagem Python ou um Tutorial do Python. Nosso objetivo aqui é para readaptar você com a linguagem e também reforçar alguns conceitos que serão fundamentais para os capítulos posteriores. O Python é uma linguagem de programação orientada a objetos moderna e fácil de aprender. Ele possui um poderoso conjunto de tipos de dados nativos e instruções de controle fáceis de usar. Como o Python é uma linguagem interpretada, é mais fácil de ser revisada simplesmente olhando e descrevendo sessões interativas. Você deve lembrar que o interpretador exibe o familiar prompt >>> e, em seguida, avalia o comando do Python que você fornece. Por exemplo, >>> print("Algoritmos e Estruturas de Dados") Algoritmos e Estruturas de Dados >>> mostra o prompt, a função print, o resultado, e o próximo prompt. Next Section - 1.8. Primeiros passos com dados |