Mais Ferramentas de Controle de Fluxo

Prévio Próximo

Python Logo4. More Control Flow Tools

Além da declaração while recentemente introduzida, Python reconhece as declarações de fluxo de controle usuais conhecidos de outras línguas, com algumas reviravoltas.

4.1. Instruções if

Talvez o tipo de instrução mais conhecido é o if. Por exemplo:
>>> x = int(raw_input("Please enter an integer: "))
Please enter an integer: 42
>>> if x < 0:
...     x = 0
...     print 'Negative changed to zero'
... elif x == 0:
...     print 'Zero'
... elif x == 1:
...     print 'Single'
... else:
...     print 'More'
...More
Podem haver zero ou mais partes elif, e a parte else é opcional. A palavra-chave ‘elif‘ é a abreviação de ‘else if‘, e é útil para evitar recuo excessivo. Uma seqüência if … elif … elif … é um substituto para o switch e case encontradas em outras linguagens.

4.2. Instruções for

O comando for em Python difere um pouco do que você pode ter usado em C ou Pascal. Ao invés de sempre interagindo sobre uma progressão aritmética de números (como em Pascal), ou dando ao usuário a capacidade de definir tanto o passo iteração e condição de parada (como C), o comando for em Python itera sobre os itens de qualquer sequência (uma lista ou uma string), na ordem em que aparecem na sequência. Por exemplo (sem trocadilhos):
>>> # Measure some strings:
... words = ['cat', 'window', 'defenestrate']
>>> for w in words:
...     print w, len(w)
...cat 3
window 6
defenestrate 12
Se você precisar modificar a seqüência que você está interagindo enquanto no interior do loop (por exemplo, para duplicar itens selecionados), recomenda-se que primeiro voce faça uma cópia. Interagindo sobre uma seqüência não faz implicitamente uma cópia. A notação slice faz isso especialmente conveniente:
>>> for w in words[:]:  # Loop over a slice copy of the entire list.
...     if len(w) > 6:
...         words.insert(0, w)
...
>>> words['defenestrate', 'cat', 'window', 'defenestrate']

4.3. A Função range()

Se você precisa iterar sobre uma seqüência de números, a função interna range() vem a calhar. Ela gera listas contendo progressões aritméticas:
>>> range(10)
[0, 1, 2, 3, 4, 5, 6, 7, 8, 9]
O ponto final fornecido nunca é parte da lista gerada; range(10) gera uma lista de 10 valores, os índices legais para itens de uma sequência de comprimento 10. É possível iniciar o intervalo em outro número, ou para especificar um incremento diferente (mesmo negativo, às vezes isso é chamado de ‘step‘):
>>> range(5, 10)
[5, 6, 7, 8, 9]
>>> range(0, 10, 3)
[0, 3, 6, 9]
>>> range(-10, -100, -30)
[-10, -40, -70]
Para iterar sobre os índices de uma sequência, você pode combinar range() e len() da seguinte forma:
>>> a = ['Mary', 'had', 'a', 'little', 'lamb']
>>> for i in range(len(a)):
...     print i, a[i]
...
0 Mary
1 had
2 a
3 little
4 lamb
Na maior parte destes casos, porém, é conveniente utilizar a função enumerate(), ver Técnicas de looping.

4.4. Instruções break e continue, e Cláusulas else em Loops

A instrução break, como em C, irrompe do menor delimitador de loops for ou while.

Loops podem ter uma cláusula else; ele é executado quando o loop se encerra por exaustão da lista (com for) ou quando a condição se torna falsa (com while), mas não quando o ciclo é finalizado por uma instrução break. Isto é exemplificado pelo seguinte ciclo, que procura por números primos:

>>> for n in range(2, 10):
...     for x in range(2, n):
...         if n % x == 0:
...             print n, 'equals', x, '*', n/x
...             break
...     else:
...         # loop fell through without finding a factor
...         print n, 'is a prime number'
...
2 is a prime number
3 is a prime number
4 equals 2 * 2
5 is a prime number
6 equals 2 * 3
7 is a prime number
8 equals 2 * 4
9 equals 3 * 3
(Sim, este é o código correto. Olhe atentamente: A cláusula else pertence ao loop for, não a instrução if)
Quando usado com um loop, a cláusula else tem mais em comum com a cláusula else de uma instrução try do que a da instrução if: a cláusula else  de um try é executada quando não ocorre exceção, e qualquer outra cláusula de um loop é executada quando não ocorre ruptura . Para saber mais sobre a instrução e exceções try, consulte Manipulação de Exceções.
A instrução continue, também emprestado do C, continua com a próxima iteração do loop:
>>> for num in range(2, 10):
...     if num % 2 == 0:
...         print "Found an even number", num
...         continue
...     print "Found a number", num
Found an even number 2
Found a number 3
Found an even number 4
Found a number 5
Found an even number 6
Found a number 7
Found an even number 8
Found a number 9

4.5. Instrução pass

A instrução pass não faz nada. Ele pode ser usado quando é necessária uma instrução sintaticamente, mas o programa não requer nenhuma ação. Por exemplo
>>> while True:
...     pass  # Busy-wait for keyboard interrupt (Ctrl+C)
...
Isto é comumente usado para a criação de classes mínimas:
>>> class MyEmptyClass:
...     pass
...
Outra lugar em que pass pode ser usado é como um place-holder (marcador de lugar) para uma função ou bloco condicional quando você está trabalhando em um novo código, permitindo que você mantenha o pensamento em um nível mais abstrato. O pass é ignorado:
>>> def initlog(*args):
...     pass   # Remember to implement this!...

4.6. Definindo Funções

Podemos criar uma função que escreve a série de Fibonacci até um limite arbitrário:
>>> def fib(n):    # write Fibonacci series up to n
...     """Print a Fibonacci series up to n."""
...     a, b = 0, 1
...     while a < n:
...         print a,
...         a, b = b, a+b
...
>>> # Now call the function we just defined:
... fib(2000)
0 1 1 2 3 5 8 13 21 34 55 89 144 233 377 610 987 1597
A palavra-chave def introduz uma definição de função. Deve ser seguida pelo nome da função e da lista em parêntesis de parâmetros formais. As instruções que formam o corpo da função começam na próxima linha, e devem ser recuados (identadas).
A primeira instrução do corpo da função pode, opcionalmente, ser uma literal string ; esta literal string  é uma string  de documentação da função, ou docstring. (Mais a respeito de docstrings pode ser encontrado na seção Documentação de Strings.) Existem ferramentas que utilizam docstrings para produzir automaticamente documentação on-line ou impressa, ou para permitir que o usuário navegue interativamente pelo código; é uma boa prática para incluir docstrings no código que você escreve, por isso faça disso um hábito.
A execução de uma função introduz uma nova tabela de símbolos utilizados para as variáveis locais da função. Mais precisamente, todas as variáveis atribuídas em uma função armazenam o valor na tabela de símbolos local; Considerando o primeiro olhar de referências de variáveis na tabela de símbolos local, em seguida, nas tabelas global de símbolos, e, finalmente, na tabela de nomes internos. Assim, não se pode atribuir valores às variáveis globais dentro de uma função (a não ser nomeado em uma instrução global), embora possam ser referenciadas.
Os atuais parâmetros (argumentos) para uma chamada de função são introduzidos na tabela de símbolos local da função chamada quando é chamada; Assim, os argumentos são passados usando call by value (onde o valor é sempre uma referência de objeto, e não o valor do objeto). [1] Quando uma função chama outra função, uma nova tabela de símbolos local é criada para essa chamada.
A definição de uma função introduz o nome da função na tabela de símbolos corrente. O valor do nome da função tem um tipo que é reconhecido pelo interpretador como uma função definida pelo usuário. Este valor pode ser atribuído a outro nome que pode, então, também ser usada como uma função. Isto serve como um mecanismo geral de mudança de nome:
>>> fib
<function fib at 10042ed0>
>>> f = fib
>>> f(100)
0 1 1 2 3 5 8 13 21 34 55 89
Vindo de outros idiomas, você pode argumentar que fib não é uma função, mas um procedimento, uma vez que não retorna um valor. Na verdade, até mesmo funções sem uma instrução return retornam um valor, ainda que meio chato. Esse valor é chamado None (é um nome interno). Escrevendo o valor None é normalmente suprimido pelo interpretador se ele fosse o único valor escrito. Você pode vê-lo, se você realmente quer usar print:
>>> fib(0)
>>> print fib(0)
None
É simples de escrever uma função que retorna uma lista dos números de série de Fibonacci, em vez de imprimi-lo:
>>> def fib2(n): # return Fibonacci series up to n
...     """Return a list containing the Fibonacci series up to n."""
...     result = []
...     a, b = 0, 1
...     while a < n:
...         result.append(a)    # see below
...         a, b = b, a+b
...     return result
...
>>> f100 = fib2(100)    # call it
>>> f100                # write the result
[0, 1, 1, 2, 3, 5, 8, 13, 21, 34, 55, 89]

Este exemplo, como de costume, demonstra algumas novas funcionalidades do Python:

  • A instrução return retorna com um valor de uma função. O return sem um argumento expressão retorna None. Saindo no final de uma função também retorna None.
  • A instrução result.append(a) chama um método do objeto-lista result. Um método é uma função que “pertence” a um objeto e é chamada obj.methodname, onde obj é algum objeto (pode ser uma expressão), e methodname é o nome de um método que é definido pelo tipo do objeto. Diferentes tipos definem métodos diferentes. Métodos de tipos diferentes podem ter o mesmo nome, sem causar ambiguidade. (É possível definir seus próprios tipos de objetos e métodos, utilizando classes, consulte Classes) O método append() mostrado no exemplo é definido por objetos-lista; acrescenta um novo elemento ao fim da lista. Neste exemplo, ele é equivalente a result = result + [a], mas mais eficiente.

4.7. Mais em Definindo Funções

É também possível definir funções com um número variável de argumentos. Existem três formas, que podem ser combinadas.

4.7.1. Valores de Argumento Padrão

A forma mais útil é especificar um valor padrão para um ou mais argumentos. Isso cria uma função que pode ser chamada com menos argumentos do que está definido para uso. Por exemplo:
def ask_ok(prompt, retries=4, complaint='Yes or no, please!'):
    while True:
        ok = raw_input(prompt)
        if ok in ('y', 'ye', 'yes'):
            return True
        if ok in ('n', 'no', 'nop', 'nope'):
            return False
        retries = retries - 1
        if retries < 0:
            raise IOError('refusenik user')
        print complaint

Esta função pode ser chamado de várias maneiras:

dando apenas o argumento obrigatório: ask_ok (‘Você realmente deseja sair?’)
dando um dos argumentos opcionais: ask_ok (‘? OK para substituir o arquivo’, 2)
ou até mesmo dando a todos os argumentos: ask_ok (‘OK para substituir o arquivo?’, 2, ‘Vamos lá, apenas sim ou não!)
Este exemplo também introduz a palavra-chave in. Isto testa se uma sequência contém ou não um determinado valor.

Os valores padrão são avaliados no momento da definição da função no âmbito definindo, de modo que
i = 5

def f(arg=i):
    print arg

i = 6
f()
Imprimirá 5.
Aviso importante: O valor padrão é avaliado apenas uma vez. Isso faz a diferença quando o padrão é um objeto mutável como uma lista, dicionário, ou instâncias da maioria das classes. Por exemplo, a função a seguir acumula os argumentos passados em chamadas subsequentes:
def f(a, L=[]):
    L.append(a)
    return L

print f(1)
print f(2)
print f(3)
Isso imprimirá
[1]
[1, 2]
[1, 2, 3]
Se você não quer que o padrão seja compartilhado entre chamadas subsequentes, você pode escrever a função assim:
def f(a, L=None):
    if L is None:
        L = []
    L.append(a)
    return L

4.7.2. Argumentos Chave (Keyword)

As funções também podem ser chamados usando argumentos da forma kwarg=value. Por exemplo, a seguinte função:
def parrot(voltage, state='a stiff', action='voom', type='Norwegian Blue'):
    print "-- This parrot wouldn't", action,
    print "if you put", voltage, "volts through it."
    print "-- Lovely plumage, the", type
    print "-- It's", state, "!"
aceita um argumento obrigatório (voltagem) e três argumentos opcionais (estado, ação, e tipo). Esta função pode ser chamada em qualquer uma das seguintes formas:
parrot(1000)                                          # 1 positional argument
parrot(voltage=1000)                                  # 1 keyword argument
parrot(voltage=1000000, action='VOOOOOM')             # 2 keyword arguments
parrot(action='VOOOOOM', voltage=1000000)             # 2 keyword arguments
parrot('a million', 'bereft of life', 'jump')         # 3 positional arguments
parrot('a thousand', state='pushing up the daisies')  # 1 positional, 1 keyword
mas todas as seguintes chamadas seriam inválidas:
parrot()                     # required argument missing
parrot(voltage=5.0, 'dead')  # non-keyword argument after a keyword argument
parrot(110, voltage=220)     # duplicate value for the same argument
parrot(actor='John Cleese')  # unknown keyword argument
Em uma chamada de função, argumentos devem seguir argumentos posicionais. Todas os argumentos palavras-chave passados deve corresponder a um dos argumentos aceitos pela função (por exemplo, actor não é um argumento válido para a função parrot), e sua ordem não é importante. Isto também inclui argumentos não-opcionais (por exemplo, parrot(voltage=1000) é válido também). Nenhum argumento pode receber um valor mais de uma vez. Aqui está um exemplo que falha devido a esta restrição:
>>> def function(a):
...     pass
...
>>> function(0, a=0)
Traceback (most recent call last):
  File "<stdin>", line 1, in ?
TypeError: function() got multiple values for keyword argument 'a'
Quando um parâmetro formal final da forma **name está presente, ele recebe um dicionário (consulte Tipos de Mapeamento – dict) contendo todos os argumentos, exceto para aqueles que correspondem a um parâmetro formal. Isto pode ser combinado com um parâmetro formal da forma de *name (descrito na próxima subseção), que recebe uma tuple contendo os argumentos posicionais alem da lista de parâmetros formais. (*name deve ocorrer antes de **name). Por exemplo, se definirmos uma função como esta:
def cheeseshop(kind, *arguments, **keywords):
    print "-- Do you have any", kind, "?"
    print "-- I'm sorry, we're all out of", kind
    for arg in arguments:
        print arg
    print "-" * 40
    keys = sorted(keywords.keys())
    for kw in keys:
        print kw, ":", keywords[kw]
Poderia ser chamada assim:
cheeseshop("Limburger", "It's very runny, sir.",
           "It's really very, VERY runny, sir.",
           shopkeeper='Michael Palin',
           client="John Cleese",
           sketch="Cheese Shop Sketch")
e é claro que iria imprimir:
-- Do you have any Limburger ?
-- I'm sorry, we're all out of Limburger
It's very runny, sir.
It's really very, VERY runny, sir.
----------------------------------------
client : John Cleese
shopkeeper : Michael Palin
sketch : Cheese Shop Sketch
Note-se que a lista de nomes de argumentos de palavras-chave é criada por ordenar o resultado dicionário de palavras-chave método keys() antes de imprimir o seu conteúdo; se isso não for feito, a ordem em que os argumentos são impressas é indefinido.

4.7.3. Lista de Argumentos Arbitrários

Finalmente, a opção menos utilizado freqüentemente é especificar que uma função pode ser chamada com um número arbitrário de argumentos. Esses argumentos serão embrulhado em uma tuple (ver Tuples e Sequências). Antes do número variável de argumentos, pode ocorrer zero ou mais argumentos normais.
def write_multiple_items(file, separator, *args):
    file.write(separator.join(args))

4.7.4. Descompactando Listas de Argumentos

A situação inversa ocorre quando os argumentos já estão em uma list ou tuple, mas precisam ser descompactado para uma chamada de função que exige argumentos posicionais separados. Por exemplo, a função interna range() espera argumentos start e stop separados. Se eles não estão disponíveis separadamente, escreva a chamada de função com o operador *-  para descompactar os argumentos de uma list ou tuple:
>>> range(3, 6)             # normal call with separate arguments
[3, 4, 5]
>>> args = [3, 6]
>>> range(*args)            # call with arguments unpacked from a list
[3, 4, 5]
Da mesma forma, os dicionários podem entregar argumentos com o operador **- :
>>> def parrot(voltage, state='a stiff', action='voom'):
...     print "-- This parrot wouldn't", action,
...     print "if you put", voltage, "volts through it.",
...     print "E's", state, "!"
...
>>> d = {"voltage": "four million", "state": "bleedin' demised", "action": "VOOM"}
>>> parrot(**d)
-- This parrot wouldn't VOOM if you put four million volts through it. E's bleedin' demised !

4.7.5. Expressões Lambda

Pequenas funções anônimas podem ser criadas com a palavra-chave lambda. Esta função retorna a soma de seus dois argumentos: lambda a, b: a + b. Funções lambda podem ser usadas sempre que são objetos de função sπo requeridos. Eles são sintaticamente restritos a uma única expressão. Semanticamente, eles são apenas um açúcar sintático para a definição de função normal. Como as definições de funções aninhadas, funções lambda pode fazer referência a variáveis do escopo que as contém.
>>> def make_incrementor(n):
...     return lambda x: x + n
...
>>> f = make_incrementor(42)
>>> f(0)
42
>>> f(1)
43
O exemplo acima usa uma expressão lambda para retornar uma função. Outro uso é para passar uma pequena função como um argumento:
>>> pairs = [(1, 'one'), (2, 'two'), (3, 'three'), (4, 'four')]
>>> pairs.sort(key=lambda pair: pair[1])
>>> pairs
[(4, 'four'), (1, 'one'), (3, 'three'), (2, 'two')]

4.7.6. Strings de Documentação (docstring)

Existem convenções emergentes sobre o conteúdo e formato de strings de documentação.

A primeira linha deve ser sempre um pequeno resumo e conciso do propósito do objeto. Para abreviar, não deve declarar explicitamente o nome ou o tipo do objeto, uma vez que estes estão disponíveis através de outros meios (exceto se o nome passa a ser um verbo que descreve a operação da função). Esta linha deve começar com uma letra maiúscula e terminar com um ponto.

Se existem mais linhas na string de documentação, a segunda linha deve estar vazia, separando visualmente o resumo do resto da descrição. As linhas seguintes devem conter um ou mais parágrafos que descrevem convenções de chamada do objeto, seus efeitos colaterais, etc.

O analisador Python não tira identação das literais strings multi-linha em Python, então ferramentas que processam a documentação têm que tirar a identaçãp se desejado. Isso é feito usando a seguinte convenção. A primeira linha não-vazia depois da primeira linha da string determina a quantidade de recuo para toda a docstring. (Nós não podemos usar a primeira linha, uma vez que é geralmente ao lado das aspas de abertura logo sua identação não é aparente na literal string.) O espaço em branco “equivalente” a essa identação é então separado do início de todas as linhas da string. Não devem ocorrer linhas identadas a menos, mas se ocorrerem todos os seus espaços em branco devem ser removidos. Equivalência de espaços em branco devem ser testados após a expansão de tabs (a 8 espaços, normalmente).

Aqui está um exemplo de uma docstring multi-linha:
>>> def my_function():
...     """Do nothing, but document it.
...
...     No, really, it doesn't do anything.
...     """
...     pass
...
>>> print my_function.__doc__
Do nothing, but document it.

    No, really, it doesn't do anything.

4.8. Intermezzo: Estilo de Codificação

Agora que você está prestes a escrever maiores, mais complexas partes de Python, é um bom momento para falar sobre o estilo de codificação. A maioria das línguas podem ser escritas (ou mais conciso, formatadas) em diferentes estilos; alguns são mais legíveis do que os outros. Tornar mais fácil para os outros a ler o seu código é sempre uma boa idéia, e adotando um estilo de codificação agradável ajuda tremendamente para isso.

Para Python, PEP 8 emergiu como o guia de estilo que a maioria dos projetos aderem; promove um estilo de codificação muito legível e agradável ao olho. Cada desenvolvedor Python deve lê-lo em algum momento; aqui estão os pontos mais importantes extraídos para você:

  • Use identação de 4 espaço, e sem tabs.
  • 4 espaços são um bom compromisso entre pequena identação (permite uma maior profundidade de aninhamentos) e grande identação (mais fácil de ler). Tabs introduzem confusão, e é melhor ficar de fora.
  • Envolver as linhas de modo que não excedam 79 caracteres.
  • Isso ajuda os usuários com telas pequenas e faz com que seja possível ter vários arquivos de código lado-a-lado em telas maiores.
  • Use linhas em branco para separar as funções e classes, e grandes blocos de código dentro de funções.
  • Sempre que possível, colocar comentários em uma linha própria.
  • Use docstrings.
  • Use espaços em torno de operadores e depois vírgulas, mas não directamente para dentro de “brackets” construtores: a = f (1, 2) + g (3, 4).
  • Nomeie suas classes e funções de forma consistente; a convenção é usar CamelCase para classes e lower_case_with_underscores para funções e métodos. Sempre use self como o nome para o primeiro argumento do método (ver Um Primeiro Olhar em Classes para saber mais sobre as classes e métodos).
  • Não use codificações complicadas se o seu código é feito para ser usado em ambientes internacionais. Plain ASCII funciona melhor em qualquer caso.
Notas de Rodapé
[1] Na verdade, chamada por referência de objeto seria uma descrição melhor, uma vez que um objeto mutável é passado, o caller vai ver todas as mudanças que o receptor faz a ele (itens inseridos em uma lista).
Prévio Próximo