HomePage RecentChanges

Lesson03

Vamos escrever os nossos programas na linguagem de programação Python, também conhecidos como scripts. Um programa em Python pode ser processado e executado por um interpretador de Python, interativamente ou não.

Em processamento interativo, o programador interage com o interpretador introduzindo comandos ou frases da linguagem através do teclado e visualizando o resultado de cada comando no ecrã. Cada comando indica ao interpretador de Python uma tarefa para executar. Depois de cada comando, o programador terá de aguardar pelo resultado do mesmo antes de introduzir o próximo comando. A este ciclo de ler um comando, avaliar um comando e escrever o resultado dá-se o nome de read-eval-print loop.

Quando iniciamos uma sessão interativa do interpretador de Python, o símbolo >>> indica que podemos introduzir o próximo comando. Por exemplo,

Python 3.4.3 (default, Mar 16 2015, 10:22:24) 
[GCC 4.8.2] on linux
Type "help", "copyright", "credits" or "license" for more information.
>>> 2+2
4
>>> print("Olá mundo")
Olá mundo
>>>

Cada comando pode ser uma expressão, uma instrução ou uma definição. Em notação BNF,

<comando> ::= <expressão> | <instrução> | <definição>

No que se segue veremos alguns elementos básicos da programação.

Expressões

Uma expressão tem um valor associado uma vez avaliada. Em Python podemos ter como expressões constantes, nomes, aplicações de funções, e composições de expressões. Em notação BNF,

<expressão> ::= <constante> | <nome> | <aplicação de função> | <expressão composta> 

Em Python temos como constantes números inteiros e reais, valores lógicos e cadeias de caracteres (strings), entre outras entidades que veremos mais tarde. Sempre que avaliamos uma constante no interpretador de Python é retornada a própria constante. Nota-se ainda que a representação das constantes, assim como de outras entidades, apresentada pelo interpretador é apenas uma visualização das mesmas, designada representação externa. Esta visualização é em geral diferente da representação interna, que é em geral uma representação em código binário.

Alguns exemplos:

>>> 3
3
>>> -1
-1
>>> 67.9872871389
67.9872871389
>>> 5e10
50000000000.0
>>> 199219034787493827498734987239748.949823498782374897324
1.9921903478749383e+32
>>> True
True
>>> False
False
>>> 'Olá mundo'
'Olá mundo'
>>> "Olá mundo"
'Olá mundo'
>>>

Temos também expressões compostas que resultam da combinação de expressões através de operações pré-definidas (not, -, +, *, etc.). Um expressão composta resulta portanto da aplicação de uma operação ou operador a um conjunto de argumentos, sendo a sintaxe a seguinte,

<expressão composta> ::=
    <operador> <expressão> |
    <operador> (<expressão>) |
    <expressão> <operador> <expressão> |
    (<expressão> <operador> <expressão>)

Exemplos:

>>> 4+5
9
>>> not(True)
False
>>> -4+7
3
>>> 4*8+5
37
>>> 4*(8+5)
52
>>>

Notar a precedência dos operadores. Como vamos ver mais tarde, esta é uma descrição sucinta das expressões em Python.

Tipos de dados elementares

Cada expressão, ou em geral cada entidade em programação, tem um tipo associado, i.e., um tipo de dados. As entidades de um dado tipo de dados -- o domínio do tipo -- partilham um conjunto de características que definem esse tipo, como por exemplo as operações a que essas entidades podem ser sujeitas.

Os tipos de dados variam de linguagem para linguagem de programação e, de forma geral, dividem-se em tipos elementares e tipos estruturados. Em Python existe um conjunto de tipos elementares (mais detalhes na documentação de Python):

>>> 3
3
>>> type(3)
<class 'int'>
>>> 7//2
3
>>> 7/2
3.5
>>> type(3.5)
<class 'float'>
>>> type(3.)
<class 'float'>
>>> type(.3)
<class 'float'>
>>> type(3)
<class 'int'>
>>> abs(-14)
14
>>> 7%2
1
>>> 2.1+.5
2.6
>>> 2.1/.5
4.2
>>> round(8.3)
8
>>> type(round(8.3))
<class 'int'>
>>> int(3.9)
3
>>> float(3)
3.0
>>> 5e10
50000000000.0
>>> 199219034787493827498734987239748.949823498782374897324
1.9921903478749383e+32
>>> True and False
False
>>> not (True or False)
False
>>>

Notar que 1 e 1.0 não correspondem à mesma entidade, tendo diferentes representações (mesmo internas), no entanto os operadores sobre números sabem lidar com estas diferentes representações através de sobrecarga.

>>> 4+7.2
11.2
>>> type(4+7.2)
<class 'float'>
>>> 1/3
0.3333333333333333
>>> 1//3
0
>>>

Nomes

Em programação é importante designar entidades através do uso de nomes. Neste contexto podemos referirmos-nos às entidades através do seu nome. Como as entidades podem variar durante a execução do programa, os nomes são também conhecidos por variáveis. Os nomes em Python podem ser simples, indexados ou compostos,

<nome> ::= <nome simples> | <nome indexado> | <nome composto>

Para já vamos apenas preocupar-nos com nomes simples,

<nome simples> ::= <inicial> <subsequente>*
<inicial> ::=
    _ | A | B | C | D | E | F | G | H | I | J | K | L | M |
    N | O | P | Q | R | S | T | U | V | W | X | Y | Z |
    a | b | c | d | e | f | g | h | i | j | k | l | m |
    n | o | p | q | r | s | t | u | v | w | x | y | z
<subsequente> ::=
    <inicial> | 0 | 1 | 2 | 3 | 4 | 5 | 6 | 7 | 8 | 9

No Python 3 os nomes podem conter ainda outros caracteres (mas detalhes na documentação do Python), no entanto teremos em conta a definição acima no que se segue.

Alguns nomes estão reservados no Python e não podem ser associados a entidades. Nomes reservados:

False      class      finally    is         return
None       continue   for        lambda     try
True       def        from       nonlocal   while
and        del        global     not        with
as         elif       if         or         yield
assert     else       import     pass
break      except     in         raise

Atribuição

A associação de um nome a um entidade é conseguida através da instrução de atribuição em Python e recorre à operação pré-definida =. Podemos ter atribuições simples ou múltiplas:

<instrução de atribuição> ::=
    <nome> = <expressão> |
    <nome> , <instrução de atribuição>, <expressão>

Após uma instrução de atribuição, se o nome não existir no ambiente, então a nova associação é incluída no ambiente, caso contrário a associação existente no ambiente é substituída pela nova associação. O ambiente consiste num mapa de associação entre nomes e entidades computacionais que é mantido em memória e onde são incluídas todas as associações que o interpretador de Python conhece num dado contexto.

Na execução de uma instrução de atribuição, primeiro é avaliada a expressão (do lado direito) e em segundo é feita uma associação entre o nome (do lado esquerdo) e o valor obtido na avaliação da expressão. No caso de uma atribuição múltipla todas as expressões são avaliadas primeiro (a ordem é irrelevante) e só depois são feitas as respectivas associações.

Exemplo:

>>> not = 9
  File "<stdin>", line 1
    not = 9
        ^
SyntaxError: invalid syntax
>>> x = 8
>>> y
Traceback (most recent call last):
  File "<stdin>", line 1, in <module>
NameError: name 'y' is not defined
>>> x
8
>>> y = x * 2
>>> y
16
>>> x = 7
>>> y
16
>>> x, z = 10, 3
>>> x+z
13
>>> x, z = z, x
>>> x
3
>>> z
10
>>> z , a = a + 3, 1
Traceback (most recent call last):
  File "<stdin>", line 1, in <module>
NameError: name 'a' is not defined
>>>

Predicados e condições

Uma operação cuja avaliação resulta num valor do tipo lógico, i.e., bool, designa-se por predicado. As expressões que tomam um valor lógico designam-se por condições. Nota-se no entanto que em Python qualquer expressão pode ser tomada por condição, caso em que se o valor for zero ou False, a condição é considerada como falsa, e em caso contrário é considerada como verdadeira.

As condições podem ser combinadas através de operações lógicas (not, and, or). Existem também operadores relacionais que nos permitem definir condições (==, !=, >, <, <=).

Exemplos:

>>> x = 8
>>> x < 7
False
>>> x < 7*3
True
>>> x < 7*3 and x > 10
False
>>> x < 7*3 < 25
True
>>> x < 7*3 < 25 > 10
True
>>> x < 7*3 < 25 > 100
False
>>>

Este exemplos incluem também o uso de algum açúcar sintáctico.

Leitura e escrita

A comunicação com o exterior é um ingrediente indispensável em grande parte dos programas. Durante a execução de um programa pode ser necessário obter valores do exterior, fornecidos pelos utilizadores ou obtidos de outros programas ou ficheiros, e comunicar resultados ou enviar dados para outros programas. Nesta secção vamos começar por ver formas simples de ler dados, a função input, e de escrever dados, função print.

Leitura de dados, notação BNF:

<leitura de dados> ::=
   input() |
   input(<informação>)
<informação> ::= <cadeia de caracteres>

A execução da instrução input mostra no ecrã a informação, i.e., a cadeia de caracteres, após o que lê todos os símbolos introduzidos no teclado até que o utilizador carregue na tecla Enter. O valor resultante da execução desta instrução é a cadeia de caracteres obtida da leitura.

A cadeia de caracteres passada como argumento da função input pode conter caracteres de escape, e.g., \n, \r, \t, \v, etc.

Exemplo:

>>> input("Introduza uma expressão:\n\t")
Introduza uma expressão:
	201+1
'201+1'
>>>

Uma vez que o valor obtido na execução da função input é uma cadeia de caracteres, para ler inteiros ou reais vamos utilizar a função pré-definida eval designada por função de avaliação, com a seguinte sintaxe:

<função de avaliação> ::= eval(<cadeia de caracteres>)

Esta função recebe uma cadeia de caracteres e devolve o resultado de avaliar a mesma como sendo uma expressão de Python.

Exemplo:

>>> eval('201+1')
202
>>> type(eval('201+1'))
<class 'int'>
>>>

Combinando ambas as funções:

>>> x = eval(input("Introduza uma expressão:\n\t"))
Introduza uma expressão:
	4+4.5/2
>>> x
6.25
>>> type(x)
<class 'float'>
>>>

Nota-se que o uso de eval neste exemplo apresenta sérios problemas de segurança e que deve ser evitado em produção e/ou programas utilizados por terceiros. O problema reside no facto de não ser feita qualquer verificação da sequência de caracteres lida pela função input antes de proceder à sua avaliação com a função eval, podendo um utilizador malicioso explorar este facto para conseguir fazer uso indevido do software.

A função print tem a seguinte sintaxe, em notação BNF:

<escrita de dados> ::=
    print() |
    print(<expressões>)

<expressões> ::=
    <expressão> |
    <expressão>, <expressões>

A avaliação da instrução print começa pela avaliação das expressões passadas como argumento da função print após o que escreve a representação externa de cada um dos valores resultantes, por ordem, todos na mesma linha, separados por um espaço em branco, e terminando com uma linha em branco. Nota-se que no caso da instrução print() a avaliação resulta apenas na escrita de uma linha em branco.

Exemplo:

>>> x = eval(input("Introduza uma expressão:\n\t"))
Introduza uma expressão:
	4
>>> y = input("Introduza uma string:\n\t")
Introduza uma string:
	olá mundo
>>> print(x, "e", y)
4 e olá mundo
>>>

E qual é o valor resultante de avaliar a instrução print?

>>> v = print(x, "e", y)
4 e olá mundo
>>> print(v)
None
>>>

A avaliação da instrução print não devolve nada. Este é o resultado de várias funções em Python cujo objectivo não é retornar um valor, mas sim ter um efeito.