segunda-feira, 23 de janeiro de 2017

Voltemos aos ciclos: as listas de compreensão

Já vimos antes o que são ciclos e o que são listas, e até podemos usar ciclos para gerar listas como se ilustra no programa seguinte:
lista = []
for i in 'palavra':
    lista += [i]
Bem, aproveito para apresentar um novo conjunto de atribuições: as atribuições aumentadas. Estas operações não atribuem um valor a uma variável; em vez disso, alteram o valor dessa variável com o que estiver à direita da atribuição. Uma maneira muito mais simples de explicar isto é a seguinte:
Atribuições aumentadas: 
a += ba = a + b
a -= ba = a - b
a *= ba = a * b
a /= ba = a / b
a //= ba = a // b
a %= ba = a % b
Assim, o que está à esquerda é equivalente ao que está à direita. Isto explica a última linha do programa. Vamos então ver o que são afinal essas listas de compreensão. Mais uma vez, um exemplo ilustra melhor o que quero dizer:
lista = [i for i in 'palavra']
Muito bem, o que está aqui faz o mesmo que o programa mais acima! Vamos por partes: o facto de envolver  o i for i in 'palavra' com [] quer dizer que vamos obter uma lista. Se tivéssemos {} ou () poderíamos obter conjuntos ou tuplos, respetivamente. De seguida, as listas de compreensão devem ter uma instrução for, para iterar os vários elementos que irão compor a lista (no caso acima, vai iterar as letras da cadeia de caracteres e colocar cada uma como elemento da lista final). E finalmente, o i antes do for indica o que se vai colocar em cada elemento.
Vamos supor agora que queríamos a lista dos quadrados dos primeiros 10 inteiros:
>>> quad = [i*i for i in range(1,11)]
>>> quad
[1, 4, 9, 16, 25, 36, 49, 64, 81, 100]
Repare agora o que ficou no início, i*i em vez de i.
Mas isto pode tornar-se mais complicado:
>>> mult = [i*j for i in [1,2,3] for j in [2,4,6]]
>>> mult
[2, 4, 6, 4, 8, 12, 6, 12, 18]
Repare que aqui temos dois ciclos e o que vai acontecer é que para cada iteração de i, vai multipicar-se por cada um dos j e preencher um elemento, resultando num total de 9 elementos, para este exemplo.
Um exemplo bastante ilustrativo que vem diretamente da documentação do Python:
>>> matriz = [
...     [1, 2, 3, 4],
...     [5, 6, 7, 8],
...     [9, 10, 11, 12],
... ]
>>> [[linha[i] for linha in matriz] for i in range(4)]
[[1, 5, 9], [2, 6, 10], [3, 7, 11], [4, 8, 12]]
O que são aqueles ... à esquerda? É quando o IDLE está a aceitar mais do que uma linha porque deteta que ainda não acabou tudo na primeira linha (o parêntesis reto não fechou). E repare que a matriz é uma lista com 3 listas no seu interior. O que o comando na segunda entrada faz é transpor a matriz: cria 4 listas, e cada lista vai ter os itens i das linhas da matriz. Muito bem pensado, não é?
Vamos complicar ainda mais um bocadinho:
>>> x = [i for i in range(10) if i%3 != 0]
>>> x
[1, 2, 4, 5, 7, 8]
O que aconteceu aqui? Agora adicionámos uma condição com if, e só se esta se verificar é que o elemento é incluído na lista. No exemplo acima, o número só é introduzido se não for um múltiplo de 3.
E vamos terminar com uma função curiosa:
>>> zip([1,2,3],[4,5,6])
<zip object at 0x0423B2D8>
>>> list(zip([1,2,3],[4,5,6]))
[(1, 4), (2, 5), (3, 6)]
>>> list(zip([1,2,3],[4,5,6],[7,8,9]))
[(1, 4, 7), (2, 5, 8), (3, 6, 9)]
Repare como faz uma espécie de transposição...  Na primeira linha serve para demonstrar como o comando zip() cria um objeto; para vermos o seu conteúdo temos de o converter em lista. E o que faz é pegar nos primeiros elementos das listas de argumento e juntá-los em tuplos. Voltaremos a este comando mais tarde!

segunda-feira, 2 de janeiro de 2017

Mais uma estrutura: os conjuntos

Pois é, ainda há mais estruturas em Python para conhecer. Hoje vamos ver o que são os conjuntos e como usá-los. Estes são uma espécie de listas mas, ao contrário destas, não são ordenados (e, consequentemente, não são indexáveis) e não admitem elementos repetidos. Vamos ver como criá-los:
>>> a = set([4, 'batata', 5, 5.0])
>>> a
{'batata', 4, 5}
>>> b = set((5,6,7))
>>> b
{5, 6, 7}
Muito bem, como se percebe, podemos criar um conjunto com o comando set(), em que o argumento deve ser um iterável (pode ser uma lista, um tuplo, uma sequência criada por range()). Se quisermos criar um conjunto vazio, tem de se usar set(), e não {}, pois isto cria um dicionário vazio.
Antes de avançarmos, reparou que o conjunto a só tem um 5? Pois é, 5 e 5.0 são o mesmo número e os conjuntos não aceitam elementos repetidos... E repare que a ordem dos elementos se alterou. Não podemos aceder a elementos através de um índice, como nas listas, nos tuplos ou nos dicionários, pois os conjuntos não são ordenados. Também não podemos fazer slicing com conjuntos pelo mesmo motivo.
Mais uma curiosidade dos conjuntos:
>>> set('abracadabra')
{'r', 'c', 'a', 'b', 'd'}
O que se passou aqui? Antes de mais, quando se usa set() para criar um conjunto,  as cadeias de caracteres são divididas em caracteres individuais. Além disso, como os conjuntos não admitem elementos repetidos, apenas ficamos com as letras que constituem a palavra na cadeia de caracteres. Vamos agora ver operações com conjuntos:
>>> {2,3,5,6,7} | {2,6,7,8,9}
{2, 3, 5, 6, 7, 8, 9}
>>> {2,3,5,6,7} & {2,6,7,8,9}
{2, 6, 7}
>>> {2,3,5,6,7} - {2,6,7,8,9}
{3, 5}
>>> {2,3,5,6,7} ^ {2,6,7,8,9}
{3, 5, 8, 9}
Vejamos... A primeira operação (|), não é mais do que a união de conjuntos, em que o conjunto final é a junção dos elementos de cada conjunto, eliminando as repetições. A segunda (&) corresponde à interseção de conjuntos, em que apenas sobram os elementos que existiam em ambos os conjuntos iniciais. Depois temos a diferença de conjuntos (-), em que retiramos ao primeiro conjunto os elementos que constam no segundo. Finalmente, a última operação é a diferença simétrica (^), que corresponde à diferença entre união e a interseção dos conjuntos, ou por outras palavras, os elementos que apenas pertencem a cada um dos conjuntos mas não a ambos.
Bem, mas como se adicionam elementos a conjuntos? Uma resposta é usar a união de conjuntos:
>>> a = {5,7,8}
>>> a = a | {9}
>>> a
{8, 9, 5, 7}
E continuando o exemplo acima, também podemos retirar elementos a conjuntos usando a diferença:
>>> a = a - {5}
>>> a
{8, 9, 7}
Também podemos ver se um dado elemento pertence a um conjunto usando o operador in, e saber o número de elementos do conjunto usando len():
>>> 9 in a
True
>>> 6 in a
False
>>> len(a)
3
E, se o conjunto contiver apenas números, também podemos usar as funções max(), min() e sum():
>>> max(a)
9
>>> min(a)
7
>>> sum(a)
24
E agora, imagine que queria ver todos os elementos do conjunto um a um. Vamos ver um programa:
a = {1, 4, 5, 6, 3}
for i in a:
    print(i)
Quando executar este programa, vai obter o seguinte resultado:
1
3
4
5
6
Sim, podemos iterar os elementos de um conjunto usando um ciclo for. Já nem deve ser surpreendente, pois não?
Antes de acabarmos, existe um outro comando que cria conjuntos, o comando frozenset(). A diferença entre este e o set() é que no frozenset() os conjuntos criados já não se podem alterar. Mais tarde, quando falarmos em métodos e propriedades de objetos, isto fará mais sentido!
Ok, basta por hoje!

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!