Introdução aos vetores em Python

Como estudado no texto a respeito do conceito de vetores, um vetor é uma sequência de dados ocupando posições consecutivas de memória e por isso existe uma ordem natural (o primeiro elemento, o segundo e assim por diante). A grande vantagem de usar vetor é poder trabalhar com um grande número de variáveis utilizando um único nome (para a variável). Para isso existe uma sintaxe especial para pegar elementos do vetor em posições específicas (como o primeiro elemento ou o décimo elemento).

A linguagem Python encapsula bastante o conceito de vetor, agregando funcionalidades e por isso geralmente o material didático sobre vetores em Python utiliza o nome lista.

<bloco1>Neste texto: entradas e saídas; definir/imprimir vetor; definir "string"; referência a listas; definir/imprimir matriz. </bloco1>

1. Como ler vários dados de uma só vez

Em Python entrar vários dados de uma só vez não é tão trivial, é preciso processamento para recuperar os valores, para isso é preciso ler a entrada toda como uma cadeia de caracteres (string). Mais ainda, no Python 2 existe uma função especial para isso, a `raw_input`, que no Python 3 sumiu!

O truque básico é ler como string, quebrar em itens (usando os separadores, geralmente espaço em branco) e por fim converter para o tipo desejado. Por exemplo, para ler dois valores, o primeiro sendo inteiro e o seguindo flutuante, podemos fazer:

Tab. 1. Exemplos de técnicas para ler vários dados com um único ENTER em Python.

Python 2 |  | Python 3
`linha = raw_input(); itens = linha.split(); n, val = int(itens[0]), float(itens[1]); <verm>print</verm>("n=%d, val=%f" % (n,val));` |  | `linha = input(); itens = linha.split(); n, val = int(itens[0]), float(itens[1]); <verm>print</verm>("n=%d, val=%f" % (n,val));`

Note que usamos um formatador especial `%d` para valores inteiros e `%f` para ponto flutuante.

No exemplo acima precisamos converter o primeiro item para inteiro e o segundo para flutuante. Entretanto se todos itens forem de mesmo tipo, existe uma função que realiza automática o laço percorrendo todos os elementos e convertendo um a uma, é o mapeamento `map`, como ilustrado abaixo. Experimente o código com um única linha de entrada de dados: `0.0 1.0 1.5 2.0 3.0 3.5 <ENTER>`.

Tab. 2. Exemplos de técnicas para ler vários dados "reais" com um único ENTER em Python.

Python 2 |  | Python 3

`linha = raw_input();
itens = linha.split();
vetor = map(float, itens); <cyan># Erro se tentar: map(int, itens)</cyan>
tam = len(vetor);
for i in range(tam) :
<verm>print</verm>("%2d | %4.1f" % (i,vetor[i]));`

`linha = input(); itens = linha.split();
vetor = map(float, itens); <cyan># Erro se tentar: map(int, itens)</cyan>
tam = len(vetor);
for i in range(tam) :
<verm>print</verm>("%2d | %4.1f" % (i,vetor[i]));`

Note que os formatadores `%d` e `%f` foram usado com parâmetros, `%2d` e `%4.1f`. O primeiro indica que devemos ajutar 2 posições à direita e o segundo ajustar 4 posições à direito e usar apenas 1 casa decimal. Estes formatadores permitem saídas mais interessantes, em particular a construção de tabelas.

Pegue este código exemplo e "brinque" com ele, altere os formatadores, troque "float" por "int", veja a mensagem de erro, acerte-a. Teste até estar seguro de ter entendido o `raw_input`, o `split`, o `map` e os formatadores `%d` e `%f`.

2. Defininido e imprimindo uma lista

Pode-se construir uma lista em Python fornecendo todos seus elementos, como em: `vet = [21, 22, 23, 24];` (o finalizador `;` só é obrigatório se tiver mais de um comando na mesma linha).

Pode-se também construtir uma lista em Python de modo interativo, agragando novo elemento a cada passo usando a função comando `append(.)`, como em: `vet.append(10+i);`, que anexa o elemento de valor `10+i` à lista.

No exemplo a seguir construimos uma lista com n (usando o valor fixo 10 no código) elementos, iniciando no 10+i e seguindo até o 10+n-1. Nesse exemplo ilustramos o uso de iterador (in) e o anexador de novo elemento em lista (append).

Exemplo 1. Construir uma lista e imprimir seus elementos.

    # Python2 para 'print' sem pular linha : <verm>print</verm>("*" , end = "");
    from __future__ import print_function

    n = 10;

    # Definir vetor/lista com: (10,11,12,13,...10+n-1)
    vet = []; <cyan># define um vetor vazio</cyan>
    for i in range(0, n, 1) : <cyan># ou mais simples neste caso "for i in range(n)"</cyan>
      vet.append(10+i); <cyan># anexe mais um elemento `a lista</cyan>
    <cyan># Imprime o vetor/lista de uma so' vez (na mesma linha)</cyan>
    <verm>print</verm>("\nO vetor: ", end=""); <cyan># informe o que vira impresso a seguir (na mesma linha devido ao parametro end=""</cyan>
    <verm>print</verm>(vet);

    # Imprimir vet em linha unica na forma "( 0,  0) ( 1,   1)..."
    <verm>print</verm>("\nNovamente o vetor impresso de 2 modos: "); <cyan># informe o que vira impresso a seguir (e "quebre" a linha)</cyan>
    i = 0;
    for item in vet : <cyan># "item in vet" e' um iterador, item comeca em vet[0], depois vet[1] e assim por diante</cyan>
      <verm>print</verm>("(%2d, %2d) " % (item, vet[i]), end="");
      i += 1;
    <verm>print</verm>(); <cyan># quebre a linha</cyan>

3. Defininido uma cadeia de caracteres ("string")

Em Python é possível definir um vetor com caracteres, formando uma palavra, ou seja, uma cadeia de caracteres, que abreviadamente é denominada "string". Para isso pode-se fazer uma atribuição como constante ou ler os dados como palavra. Isso é ilustrado no exemplo abaixo.

Exemplo 2. Trabalhando com "strings". Uma "string" fixa e digitar uma frase, imprimindo o número de caracteres nela.

    # Definir "string"
    string = "0123456789abcdefghih"; <cyan># (1)</cyan>
    # Imprimir cada caractere da string com seu codigo ASCII
    <verm>print</verm>("\nImprime caracteres e seus codigos ASCII na forma de tabela\nColuna 1 => caractere; coluna 2 => seu codigo ASCII");
    for char in string : <cyan># iterador</cyan>
       <verm>print</verm>("%20c : %3d" % (char, ord(char))); <cyan># funcao ord(.) devolve codigo ASCII do caractere parametro</cyan>
    <verm>print</verm>(); <cyan># quebre a linha</cyan>

    # Agora um entrada de dados via teclado e pegando uma frase como "string" (o ENTER finaliza a frase)
    <verm>print</verm>("Digite uma frase e tecle ENTER/CR");
    string = raw_input(); <cyan># no Python 2 e' obrigatorio usar a funcao 'raw_input' (que tambem funciona no 3)</cyan>
    <verm>print</verm>("Foi digitada uma frase com %d caracteres: %s" % (len(string), string));

    # Observacao: se definiu um vetor como "string" como em (1) acima, NAO e' possivel alterar a "string", por exemplo,
    # o comando abaixo resulta erro "TypeError: 'str' object does not support item assignment"
    # string[2] = '*'; <cyan># isso resultaria erro! experimente</cyan>

4. Sobre copiar ou referenciar um vetor/lista

Ao declarar uma lista/tupla, significa que associamos um nome de variável à uma sequência de posições de memória, sendo que cada item pode ter um tamanho distinto dos demais, como em `l = [1,[2],"tres"];`. Uma diferença essencial entre lista (como `l = [1,2,3];`) e tupla (como `l = (1,2,3);`) é que uma lista é mutável (que pode ser altedo) e tupla é imutável (impossível de alterar algum elemento).

Por exemplo, ao declarar a tupla com `v = (1,2,3);`, além do espaćo com informaćões sobre a tupla, são reservadas 3 posições de memória, uma para cada elemento da tupla. Deve-se destacar que usar um comando do tipo `aux = v;`, seja v uma lista ou uma tupla, não implica em copiar v, mas sim ter uma nova referência para v. Assim, no caso de lista, que portanto pode ser alterada, ao usar os comandos
`v = [1,2,3]; aux = v; aux[2] = -1; <verm>print</verm>(v);`
será impresso na tela: `[1, 2, -1]`, ou seja, o elemento da posićão 2 de v foi alterado para -1.

Mas existe uma funćão em uma biblioteca Python que copia listas genéricas (`copy`). No exemplo a seguir ilustramos como fazer copia de listas ou tuplas.

Exemplo 3. Construindo um vetor e imprimindo seus elementos via um apontador.

    import copy <cyan># para funcao 'copy(.)'</cyan>
    <verm>def</verm> copiar (lst_tpl) : <cyan># funcao para copiar elementos de lista/tupla gerando lista nova</cyan>
      lista = [];
      for item in lst_tpl : lista.append(item);
      return lista;

    lista = [1,2,[3]]; <cyan># define lista com 3 elementos, sendo o terceiro uma lista com 1 elemento</cyan>
    aux = lista;
    aux[2] = -1; <cyan># alterou lista[2]</cyan>
    <verm>print</verm>("Lista: lista = %s" % lista);

    lista = [1,2,[3]]; <cyan># lista</cyan>
    <verm>print</verm>("Lista: lista = %s" % lista);
    aux1 = copy.copy(lista); <cyan># copiar com funcao "copy(.)" de biblioteca "copy"</cyan>
    aux2 = list(lista);      <cyan># copiar com gerar de lista "list(.)"</cyan>
    aux3 = copiar(lista);    <cyan># copiar com funcao local "copiar(.)"</cyan>

    aux1[2] = -1; <cyan># NAO alterou lista[2]</cyan>
    aux2[2] = -1; <cyan># NAO alterou lista[2]</cyan>
    aux3[2] = -1; <cyan># NAO alterou lista[2]</cyan>
    <verm>print</verm>("aux1=%s, lista=%s" % (aux1, lista));
    <verm>print</verm>("aux2=%s, lista=%s" % (aux2, lista));
    <verm>print</verm>("aux3=%s, lista=%s" % (aux3, lista));

    tupla = [1,2,[3]]; <cyan># tupla</cyan>
    <verm>print</verm>("Tupla: tupla = %s" % tupla);
    aux1 = copy.copy(tupla); <cyan># usar funcao 'copy' de biblioteca 'copy'</cyan>
    aux2 = list(tupla);
    aux3 = copiar(tupla);
    <verm>print</verm>("aux1=%s, tupla=%s" % (aux1, tupla));
    <verm>print</verm>("aux2=%s, tupla=%s" % (aux2, tupla));
    <verm>print</verm>("aux3=%s, tupla=%s" % (aux3, tupla));

Experimente! Esse código produz como saída as seguintes linhas:

Saídas do exemplo 3.

    Lista: lista = [1, 2, -1]
    Lista: lista = [1, 2, [3]]
    aux1=[1, 2, -1], lista=[1, 2, [3]]
    aux2=[1, 2, -1], lista=[1, 2, [3]]
    aux3=[1, 2, -1], lista=[1, 2, [3]]
    Tupla: tupla = [1, 2, [3]]
    aux1=[1, 2, [3]], tupla=[1, 2, [3]]
    aux2=[1, 2, [3]], tupla=[1, 2, [3]]
    aux3=[1, 2, [3]], tupla=[1, 2, [3]]

5. Defininido matriz como lista de lista

Uma vez que um vetor/lista é armazenado consecutivamente na memória, então podemos armazenar vários vetores também consecutivamente para obter algo que represente (e seja tratado como) matriz. Como na imagem abaixo, em que cada alocamos nl vetores consecutivos na memória, cada vetor com nc posições de memória, desde `vet[0]` até `vet[nc-1]`. Assim, podemos ver este agregado de dados como uma matriz `mat[][]`, cuja primeira linha é o primeiro vetor e assim por diante. Isso está ilustrado na imagem abaixo.

Desse modo obtemos uma estrutura com dois índices que pode ser manipulada como uma matriz (como em mat[i][j]). No exemplo abaixo ilustramos isso de dois modos, imprimindo as linhas da matriz e mostrando que pode-se passar como parâmetro uma linha de matriz para uma função que tenha como parâmetro formal um vetor.

Exemplo 3. Construindo matriz como vetor de vetor e ilustrando que cada linha dela é um vetor, podendo portanto ser usado para chamar uma função que tem como parâmetro um vetor.

    <cyan># Vetor de vetor ou matriz</cyan>
    # Definir vetor/lista com: (0,1,2,3,...n-1)
    nl = 3; nc = 4;
    mat = []; <cyan># define um vetor vazio</cyan>
    for i in range(0, nl, 1) : <cyan># ou mais simples neste caso "for i in range(n)"</cyan>
      linha = []; <cyan># define um vetor vazio</cyan>
      for j in range(0, nc, 1) : <cyan># ou mais simples neste caso "for i in range(n)"</cyan>
        linha.append(i*n+j);
      mat.append(linha); <cyan># anexe nova linha `a matriz 'mat'</cyan>
    # Imprime o vetor/lista de uma so' vez (na mesma linha)
    <verm>print</verm>("\nA matriz: ", end=""); <cyan># informe o que vira impresso a seguir (na mesma linha devido ao parametro end=""</cyan>
    <verm>print</verm>(mat);

    # Pegando o primeiro e segundo elemento da terceira linha de mat: mat[2][1]
    prim = mat[2][0]; seg = mat[2][1]; <cyan># pegar primeiro e segundo elemento da linha mat[2]</cyan>
    <verm>print</verm>("O primeiro e segundo elemento da terceira linha da matriz: %d e %d" % (prim, seg));

    <verm>print</verm>("Linhas da matriz: 0=%s ; 1=%s ; 2=%s" % (mat[0], mat[1], mat[2])); <cyan># cada linha e' um vetor!</cyan>
    # <verm>print</verm>(mat[0]); <verm>print</verm>(mat[1]); <verm>print</verm>(mat[2]);

    # Verifique que cada linha da matriz e' de fato um vetor
    # Usar a funcao 'somalinha(...)' que soma elementos em vetor
    for i in range(0, nl) :
      <verm>print</verm>("Soma da linha %2d = %3d" % (i, somalinha(mat[i])));

6. Cuidados com referencias (ou apelidos)

Vocês devem tomar muito cuidado com atribuições envolvendo listas, pois na verdade não ocorre uma cópia, mas comentado na seção 4. Por exemplo, estude e teste em seu computador a sequência de comandos a seguir.

    >>> a = [1, 2, 3];
    >>> b = a;
    >>> b[2] = -9;
    >>> print(a)
    [1, 2, -9]

Cód. 1. Exemplo de uso de atribuição com listas, equivalente a referência, não cópia.

Entretanto o Python oferece um mecanismo que efetivamente realiza uma cópia de cada um dos elementos, portanto envolve um laço de tamanho igual ao número de elementos da lista. Isso é feito usando algo como `[inicio:fim]`, sendo inicio a posição iniciar a partir da qual deseja copiar e fim o último elemento.
Atenção ao fim, não é posição, mas sim número do elemento, então se fim=3, irá copia até o terceiro elemento. Por exemplo, algo como `b = a[4:3];` resultaria em lista vazia, pois o terceiro elemento está antes do elemento da posição 4.

    >>> a = [0, "um", 2, 3, 4, 5, 6, 7, 8];
    >>> v1 = a[4:3]; v2 = a[0:3]; print(v1, v2);
    ([], [0, 'um', 2])

Cód. 2. Exemplo truque para cópiar vários elementos.

Leônidas de Oliveira Brandão
http://line.ime.usp.br

Alterações:
2020/08/12: novo formato, pequenas revisões
sexta, 08 May 2020, 21:00
segunda, 03 May 2018, 16:00