segunda-feira, 26 de dezembro de 2016

Alguns pormenores

Cá estou de volta, depois de uma ausência de alguns meses. Não, não desisti de escrever, apenas deixei de ter tempo. Mas agora vou redimir-me.
E vou aproveitar este regresso para atar algumas pontas soltas, de coisas que aprendi entretanto, aqui e ali. Vamos a isto.
Vamos começar por voltar ao IDLE. Há uma coisa que descobri e que dá muito jeito quando se estão a fazer contas nas máquinas de calcular: usar o último valor calculado. Nas calculadoras, pode ser o "ANS", ou "Recall". Mas vamos ver com exemplos, que é sempre mais fácil de entender:
>>> 2 + 3
5
>>> _ * 5
25
>>> _
25
Como se consegue perceber, usar o símbolo '_' permite-nos obter o último objeto inserido ou obtido no modo interativo. Sim, o último objeto. Se fizermos o mesmo com cadeias de caracteres, também resulta:
>>> 'Texto escrito'
'Texto escrito'
>>> _
'Texto escrito'
Giro, não é?
E outra coisa que descobri: os números inteiros em Python tem precisão infinita!!! Não fazia ideia! O que quer isto dizer? Experimentem:
>>> 2324328643274826487*23428734632846
54456078982808883241978300992002
>>> _ * 55465465465465465464564
3020431768205645866255231705359115636508579632978417128
>>> _ ** 4
83229276179341914052177247922360198310600096860756479458237334163417416895989199680022872164696168962399992046990547315630606883203434211280232750588277764833775698281886726905118556842774846290016959699906061197971456
Ora aqui está: ao contrário de outras linguagens, o cálculo com inteiros apresenta sempre todos os algarismos!
E para finalizar, mais um detalhe. Já tínhamos visto que as cadeias de caracteres se podem definir com ' ' ou com " ". Mas faltava mencionar que também podem ser definidas com 3 aspas (""" """). A vantagem destas é que podemos definir texto ao longo de várias linhas:
>>> """esta linha
vai durar várias
linhas"""
'esta linha\nvai durar várias\nlinhas'
Note que apareceram ali uns '\n' pelo meio. Isto é um código de nova linha que é interpretado quando se usa o comando print(). Ainda voltaremos a ver para que servem estas cadeias...

segunda-feira, 24 de outubro de 2016

Vamos falar de booleanos...

Lembra-se de já termos falado em booleanos? Pois hoje vamos ver ver umas funções especialmente desenhadas para este tipo de variáveis. Vamos começar por all():
>>> all([0,1,0,3,5])
False
>>> all((0,1,0,3,5))
False
>>> all((5,1,10,3,5))
True
>>> all([])
True
>>> all([True,False,True])
False
Bem, o que temos aqui? Pense no all() como uma espécie de operador and com vários argumentos encaixados dentro de um iterável (uma lista ou um tuplo). Esta função resulta em True apenas se todos os elementos forem True (ou se o iterável estiver vazio). Claro que a definição do que é falso tem de ter tida em conta, como vimos aqui. No entanto, temos de notar uma coisa: embora uma lista vazia [] valha como False, isto não é o mesmo que all([]), que testa o conteúdo da lista e não a própria lista.
Se existe uma espécie de and para iteráveis, será que existe uma espécie de or? Claro que sim, e chama-se any():
>>> any([0,1,0,3,5])
True
>>> any((0,0,0,0,0))
False
>>> any([])
False
Como seria de esperar, o any(), resulta em False apenas se todos os elementos forem False. Bem, mas com isto tudo, existe alguma maneira de sabermos qual o valor booleano de algo sem termos de nos lembrar das regras explicitadas no outro dia? Sim, felizmente existe a função bool(), que converte o seu argumento num True ou False, dependendo do que está lá dentro:
>>> bool([])
False
>>> bool(None)
False
>>> bool(0)
False
>>> bool('')
False
>>> bool()
False
Aqui assim podemos ver o que é False em Python com mais facilidade. E por hoje é tudo! Curtinho, mas informativo!

segunda-feira, 17 de outubro de 2016

E finalmente os dicionários

Vamos então falar finalmente sobre dicionários. Estas estruturas são também parecidas com as listas, mas têm a particularidade de serem construídas por pares de chave:valor. A ideia é termos uma correspondência direta entre valores. Nada como um exemplo para ilustrar:
>>> dicio = {0:'zero', 1:'um', 'UM':1, 'ZERO':0}
>>> dicio[0]
'zero'
>>> dicio['UM']
1
Comentários: o dicionário cria-se com chavetas {}, e cada par chave:valor é separado por dois pontos :, enquanto os pares são separados por vírgula. Podemos então aceder ao valor correspondente a cada chave usando a notação dicionário[chave]. As chaves e os valores podem assumir o tipo que quisermos, seja um número inteiro ou com vírgula, uma cadeia de caracteres ou mesmo um tuplo. Mas enquanto as chaves têm que ser imutáveis, não podendo aceitar listas como tipo, os valores podem aceitar listas. Além disso, não podem existir duas chaves iguais, e se ocorrer, a chave que permanece válida é a última a ser inserida. Um exemplo funciona melhor:
>>> dicio = {0:'zero', 1:'um', 0:'ZERO'} # reparar nos dois 0!!
>>> dicio[0]
'ZERO'
Eu estou sempre a apresentar outras coisas: para fazermos um comentário em Python, basta escrever o símbolo de cardinal # e daí para a frente é tudo ignorado nessa linha. Isto também se aplica a programas em Python. E acho que o exemplo acima explica-se a si mesmo.
Também podemos alterar, adicionar e eliminar elementos num dicionário:
>>> dicio2 = {'Nome':'Chico', 'Idade':25}
>>> dicio2['Nome']
'Chico'
>>> dicio2['Nome'] = 'Francisco' # alterar o valor da chave 'Nome'
>>> dicio2['Nome']
>>> dicio2['Apelido'] = 'Santos' # adicionar uma nova chave
>>> dicio2   # ver o conteúdo do dicionário
{'Idade': 25, 'Nome': 'Francisco', 'Apelido': 'Santos'}
>>> del dicio2['Idade'] # apagar o par de chave 'Idade'
>>> dicio2
{'Nome': 'Francisco', 'Apelido': 'Santos'}
>>> del dicio2 # apagar o dicionário todo
Assim já ficamos com uma boa ideia do que se pode fazer com dicionários! De notar que não se pode fazer slicing com dicionários. Mas podemos iterá-los com um ciclo for:
dicio = {'Idade': 25, 'Nome': 'Francisco', 'Apelido': 'Santos'}
for i in dicio:
    print(i, dicio[i])
Isto vai dar:
Idade 25
Nome Francisco
Apelido Santos
Também podemos usar as funções min(), max(), len() e sorted(), e também o operador in. Vejamos como funcionam:
>>> dicio = {'Idade': 25, 'Nome': 'Francisco', 'Apelido': 'Santos'}
>>> min(dicio) # resulta na primeira chave, em ordem alfabética
'Apelido'
>>> max(dicio) # resulta na última chave, em ordem alfabética
'Nome'
>>> len(dicio)
3
>>> sorted(dicio) # ordenar as chaves alfabeticamente
['Apelido', 'Idade', 'Nome']
>>> 'Nome' in dicio # ver se 'Nome' é uma chave
True
>>> 'Santos' in dicio # ver se 'Santos' é uma chave
False
Como deve ter notado, todas estas funções se aplicam às chaves. Para aceder diretamente a valores, teremos de voltar a este tema mais tarde, depois de falarmos de programação por objetos.
Finalmente, vamos falar na função geradora de um dicionário, dict():
>>> dicio = dict(Idade=25, Nome='Francisco', Apelido='Santos')
>>> dicio
{'Apelido': 'Santos', 'Idade': 25, 'Nome': 'Francisco'}
Como se percebe, usa-se como argumento pares chave=valor separados por vírgulas, com a nota de que aqui a chave tem que ser uma cadeia de caracteres.
E pronto, para a semana há mais!

segunda-feira, 10 de outubro de 2016

Mais estruturas de dados: os tuplos

Realmente, já vamos para a 13ª contribuição para este blogue e ainda não falámos em duas estruturas de dados muito importantes em Python: os tuplos e os dicionários. Estas estruturas fazem lembrar as listas, mas têm características próprias. Vamos começar pelos tuplos. Um tuplo é como uma lista, mas os seus elementos são imutáveis. Cria-se como se cria uma lista, só que em vez de parêntesis retos [], usam-se parêntesis curvos (), embora estes sejam opcionais na sua criação:
>>> (1,2,3)
(1, 2, 3)
>>> 1,2,3
(1, 2, 3)
Tal como numa lista, podemos usar slicing para aceder a vários elementos:
>>> a=(1,2,3,4,5,6)
>>> a[1]
2
>>> a[2:4]
(3, 4)
>>> a[::2]
(1, 3, 5)
>>> a[1::2]
(2, 4, 6)
Tal como com as listas, nos tuplos podemos obter o elemento 1 (a contar a partir do elemento 0) e os elementos 2 a 4, excluindo o 4. Aqui aproveito e mostro outra maneira de fazer o slicing: com três parâmetros. Os dois primeiros continuam a ser o primeiro e o último, excluindo o último. Se não indicarmos nenhum, cobre todos os elementos, mas o terceiro parâmetro indica qual o salto entre elementos. Nos exemplos acima, salta de 2 em 2 elementos. Giro, não é? Isto também funciona com cadeias de caracteres:
>>> 'Teste de saltarelos'[::2]
'Tsed atrls'
>>> 'Teste de saltarelos'[::-1]
'soleratlas ed etseT'
>>> 'Teste de saltarelos'[::-3]
'setsdtT'
Bem, mas voltemos aos tuplos. Como já mencionei, ao contrário das listas, os tuplos são imutáveis. Assim, não podemos alterar um elemento de um tuplo, nem apagar um elemento. Só podemos adicionar elementos (que corresponde a criar um tuplo novo):
>>> a = (1,)
>>> a = a + (2,3)
>>> a
(1, 2, 3)
Algumas notas: sim, quando fazemos o tuplo de um só elemento (1,), tem de ter uma vírgula, para não se confundir com um cálculo com parêntesis. Podemos mesmo retirar os parêntesis, mas a vírgula tem de ficar.
E tal como com as listas, também podemos iterar os tuplos:
nums = (1, 4, 5, 6, 3)
for i in nums:
    print(i)
Vai resultar em:
1
4
5
6
3
Em comum com as listas, os tuplos também aceitam as funções max(), min(), sum(), len(), reversed() e o operador in, que funcionam exatamente da mesma maneira. Também podemos aplicar a função sorted() num tuplo, mas o resultado é uma lista. E também temos uma função que cria tuplos, que incrivelmente se chama tuple():
>>> tuple(reversed([6,4,2,7,9]))
(9, 7, 2, 4, 6)
Esta função, tal como a sua análoga list(), também tem outras aplicações, ilustradas a seguir:
>>> tuple('Python')
('P', 'y', 't', 'h', 'o', 'n')
>>> tuple(range(6))
(0, 1, 2, 3, 4, 5)
Voltando atrás à atribuição de variáveis, vemos agora que a atribuição de várias variáveis separadas por vírgulas (um,dois,tres = 1,2,3) não é mais do que usar um tuplo para este fim...
Finalmente, porquê este nome de 'tuplo'? Bem, se pensarmos nos nomes 'duplo', 'triplo', 'quádruplo'... 'múltiplo', vemos que foi daí que veio. As coisas que vamos aprendendo...

segunda-feira, 3 de outubro de 2016

Voltemos aos ciclos

Olá de novo. É altura de voltarmos aos ciclos, seja através de for, seja através de outro tipo de ciclos que vamos já ver. Já mencionei que se usa muito o comando for com a função range(). Vamos ver um programa de exemplo em que apresentamos o comando continue:
for i in range(10):
    if i%2 == 0:
        print(i,'é par!')
        continue
    print(i,'é impar!')
Se tiver experimentado executar este pequeno programa, verá que o resultado é:
0 é par!
1 é impar!
2 é par!
3 é impar!
4 é par!
5 é impar!
6 é par!
7 é impar!
8 é par!
9 é impar!
O que é relevante aqui é que, sempre que se chega a um comando continue dentro de um ciclo for, o ciclo é interrompido e passa automaticamente para a iteração seguinte, ignorando os comandos abaixo de continue. Vamos ver agora o que faz o comando break:
for i in range(10):
    if i > 4:
        print('Estou farto de números!')
        break
    print(i)
Isto vai resultar em:
0
1
2
3
4
Estou farto de números!
Podemos ver que o comando break dentro de um ciclo for vai fazer com que se salte para fora do ciclo. Ainda há mais um comando que podemos incluir:
lim = int(input('Dá-me um número: '))
for i in range(10):
    if i > lim:
        print('Estou farto de números!')
        break
    print(i)
else:
    print('Chegámos mesmo ao fim!')
Este programa vai pedir um número, e se esse número for menor que 9, vai "fartar-se de números", caso contrário vai "chegar mesmo ao fim". Isto é, o else no final do ciclo for só é executado se as iterações do ciclo chegarem ao fim. Curioso, não é?
Mas vale a pena mencionar que existe um outro tipo de ciclo, o que usa o comando while. Neste tipo de ciclos, enquanto a condição em frente a while for verdadeira, o ciclo repete-se infinitamente (o que pode ser verdadeiramente perigoso!):
r = ''
while r != 'por favor':
    r = input('Pede para sair com modos... ')
    if r=='':
        continue
    elif r=='deixa-me!':
        print('Vá, não te irrites!...')
        break
else:
    print('Muito bem educado!')
Ora aqui está um programa que serve para mostrar que todos os comandos acima também se aplicam aos ciclos while. Se experimentar este programa, vai ver que só consegue sair se escrever uma de duas frases: 'por favor' ou 'deixa-me!'. Se não escrever nada, ele usa o continue e pede logo para escrever de novo. Se escrever 'deixa-me!' consegue sair pelo break, e se escrever 'por favor', sai do ciclo através do else. Simples, não é? Mas repito, cuidado com ciclos infinitos!

segunda-feira, 26 de setembro de 2016

Funções e listas

Já andámos a ver várias funções, de conversão ou matemáticas. Mas agora vamos abordar algumas funções que afetam listas. Comecemos por algumas bastante simples e diretas:
>>> max([1,5,3,7,8,2])
8
>>> min([1,5,3,7,8,2])
1
>>> sum([1,5,3,7,8,2])
26
Claro que estas funções apenas resultam se as listas contiverem apenas números. Como seria de esperar, a função max() apresenta o maior número na lista, a função min() apresenta o menor, e a função sum() apresenta a soma de todos os números na lista. As funções max() e min() também podem usar como argumento números ou variáveis separados por vírgulas:
>>> max(1,5,3,7,8,2)
8
>>> min(1,5,3,7,8,2)
1
Também temos um outro operador que funciona tanto em listas como em cadeias de caracteres, o operador in, que testa se algo se encontra numa lista (ou numa cadeia de caracteres) e responde com um booleano:
>>> 4 in [1,2,3,4]
True
>>> 0 in [4,8,7,4,9]
False
>>> 'a' in 'antonio'
True
>>> 'b' in 'antonio'
False
De notar que é a segunda vez que aparece o operador in, pois também é usado de modo diferente nos ciclos for.
E agora vamos ver outra função que é muito comum em várias linguagens: len(). Sendo a abreviatura de length (comprimento), resulta no comprimento de várias estruturas:
>>> len('batata')
6
>>> len([4,8,7,4,9])
5
Aplicado a cadeias de caracteres, diz-nos quantos tem, e em listas, indica o número de elementos da lista mais externa (sim, as listas podem ter listas como elementos).
Ora então chegamos finalmente a range(). No Python 2.x, era uma função que produzia uma lista, mas na versão 3.x produz uma sequência imutável. Antes de mais, o que faz isto? Em Python 2.x, fazia:
>>> range(3)
[0, 1, 2]
>>> range(3,6)
[3, 4, 5]
>>> range(1,11,2)
[1, 3, 5, 7, 9]
Em suma, quando só tem um argumento, range produzia uma lista de zero até esse argumento excluindo-o. Com dois argumentos, indica qual o número de onde começa e até onde vai, excluindo o final. Com 3 argumentos, é como com 2 mas o último argumento indica quanto se soma em cada iteração. Em Python 3.x, é quase a mesma coisa, mas se em 2.x se produzia uma lista que se podia depois modificar, em 3.x produz-se uma "lista-usa-e-deita-fora". Os exemplos acima em Python 3.x vão dar:
>>> range(3)
range(0, 3)
>>> range(3,6)
range(3, 6)
>>> range(1,11,2)
range(1, 11, 2)
Parece confuso, mas na realidade a diferença é subtil. range() é muito usado em conjunto com o comando for, como no programa abaixo:
for i in range(0,15,3):
    print(i)
que vai resultar em:
0
3
6
9
12
Este resultado seria o mesmo em Python 2.x. Mais tarde voltaremos a estas sequências imutáveis...
Falando ainda destas sequências, temos mais uma função que resulta numa destas, a função reversed(). Como o próprio nome indica, esta função inverte a sequência de uma lista que lhe seja fornecida como argumento, e resulta num iterável que corresponde à sequência invertida. Aqui já não há diferença de versões. Para aceder ao resultado, temos de iterar cada um dos elementos, como por exemplo:
for i in reversed([6,4,2,7,9]):
    print(i)
dá o seguinte:
9
7
2
4
6
Se experimentar:
>>> reversed([6,4,2,7,9])
<list_reverseiterator object at 0x02AEFA30>
O que não dá muito jeito... Mas felizmente, temos outra função que trata disto: list(). Esta converte o seu argumento numa lista, o que já dá muito jeito!
>>> list(reversed([6,4,2,7,9]))
[9, 7, 2, 4, 6]
Já agora, a função list() tem outras aplicações, ilustradas a seguir:
>>> list('Python')
['P', 'y', 't', 'h', 'o', 'n']
>>> list(range(6))
[0, 1, 2, 3, 4, 5]
Isto já permite umas coisas engraçadas, se puxar pela imaginação...
E vamos terminar com uma última função, sorted(), que serve para ordenar uma lista. Os exemplos são bastante elucidativos:
>>> sorted([6,4,2,7,9])
[2, 4, 6, 7, 9]
>>> sorted([6,4,2,7,9], reverse=True)
[9, 7, 6, 4, 2]
Como dá para perceber, podemos escolher a ordem, ascendente ou descendente, dependendo do valor que atribuímos ao argumento opcional reverse. E por hoje é tudo!

segunda-feira, 19 de setembro de 2016

Mais funções e operadores que dão jeito

Temos visto algumas funções de Python que já nos permitem obter algumas conversões interessantes. Mas existem outras funções de cariz mais matemático, que vale a pena abordar agora. Comecemos pelas seguintes:
>>> abs(-4)
4
>>> abs(-4.7)
4.7
>>> round(4.567)
5
>>> round(4.567, 2)
4.57
Como já deve ter percebido, a função abs() devolve o absoluto de um número, isto é, o número sem sinal. Na prática, converte números (inteiros ou decimais) negativos em números positivos.
A função round() arredonda números. Se o argumento for só um número, arredonda-o ao inteiro mais próximo. Com dois argumentos, o segundo é o número de casas decimais pretendidas. Nada de especial até aqui.
E continuamos com pow(). Na sua forma mais simples, pow(x, y) é outra maneira de escrever x**y, ou seja, x elevado a y. No entanto, pow(x, y, z) é o mesmo que x**y%z, isto é, x elevado a y, módulo z. Tenho a certeza que vai descobrir alguma utilidade para isto!...
E também temos divmod(). Esta função, como o próprio nome indica, dá o divisor e o módulo de dois números. Um exemplo explica melhor:
>>> 7 // 3
2
>>> 7 % 3
1
>>> divmod(7, 3)
(2, 1)
>>> divmod(4.8, 2)
(2.0, 0.7999999999999998)
Este último exemplo ilustra bem o problema de aproximações imperfeitas de números decimais. Claro que o resto deveria ser 0,8.
E agora temos uma função muito gira: eval(). Esta pega numa cadeia de caracteres e tenta avaliar se o seu conteúdo é uma expressão matemática válida. Mais um exemplo:
>>> eval('2*3 + 5')
11
>>> eval('2**8-1')
255
>>> x=6
>>> eval('x*4 - 7')
17
Se não for uma expressão matemática válida, dá erro... Mas já viu bem o último exemplo? Apesar de o x ser uma variável, dado que lhe foi atribuído um valor, x é corretamente avaliado dentro da expressão e a função consegue usá-lo para calcular o resultado final! Eu acho isto giro...
Muito bem, vamos passar para outro tipo de matemática, a lógica booleana. E vamos confirmar que em Python também existem os três típicos operadores de conjunção (and), disjunção (or) e negação (not):
>>> 1 and 1
1
>>> True or False
True
>>> not False
True
>>> not 0
True
Pois é, aqui temos de fazer uma ressalva muito importante: embora a lógica booleana apenas trabalhe com valores de Falso ou Verdadeiro, em Python há vários valores que são interpretados como Falso:

  • None (um valor especial que se pode atribuir a uma variável, que significa que não tem valor)
  • False (óbvio, não?)
  • zero em qualquer tipo numérico (0, 0.0, 0j)
  • Qualquer sequência vazia: '', [], (), {} (uma cadeia de caracteres vazia, uma lista vazia, e outros tipos de sequência que veremos mais tarde)
Todos os outros valores são considerados Verdadeiros. Mas o melhor mesmo é não inventar muito e tentar usar essencialmente True ou False como operandos destes operadores, para evitar resultados inesperados...
Claro que o local óbvio para usar os operadores acima é num comando if, onde poderá construir condições mais elaboradas com o auxílio destes operadores. Experimente!