Neste capítulo, você aprenderá a escrever funções, que são blocos de código nomeados projetados para fazer um trabalho específico. Quando você quer executar uma tarefa específica que você definiu em uma função, você chama a função responsável por ela. Se você precisa executar essa tarefa várias vezes ao longo do seu programa, você não precisa digitar todo o código para a mesma tarefa repetidamente; você apenas chama a função dedicada a lidar com essa tarefa, e a chamada diz ao Python para executar o código dentro da função. Você descobrirá que usar funções torna seus programas mais fáceis de escrever, ler, testar e consertar.
Neste capítulo, você também aprenderá uma variedade de maneiras de passar informações para funções. Você aprenderá como escrever certas funções cuja tarefa principal é exibir informações e outras funções projetadas para processar dados e retornar um valor ou conjunto de valores. Finalmente, você aprenderá a armazenar funções em arquivos separados chamados módulos para ajudar a organizar seus principais arquivos de programa.
Definindo uma função
Aqui está uma função simples chamada saudacao() que imprime uma saudação:
def saudacao():
"""Mostrar a mensagem de boas vindas."""
print('Olá')
saudacao()
Este exemplo mostra a estrutura mais simples de uma função. A primeira linha usa a palavra-chave def para informar ao Python que você está definindo uma função. Esta é a definição da função, que informa ao Python o nome da função e, se aplicável, que tipo de informação a função precisa para fazer seu trabalho. Os parênteses contêm essa informação. Neste caso, o nome da função é saudacao(), e ela não precisa de nenhuma informação para fazer seu trabalho, então seus parênteses estão vazios. (Mesmo assim, os parênteses são obrigatórios). Finalmente, a definição termina em dois pontos.
Quaisquer linhas recuadas que seguem def saudacao(): compõem o corpo da função. O texto na segunda linha é um comentário chamado docstring, que descreve o que a função faz. Quando o Python gera documentação para as funções em seus programas, ele procura uma string imediatamente após a definição da função. Essas strings geralmente são colocadas entre aspas triplas, o que permite que você escreva várias linhas.
A linha print('Olá') é a única linha de código real no corpo desta função, então saudacao() tem apenas uma tarefa: print('Olá').
Quando você quiser usar esta função, você tem que chamá-la. Uma chamada de função diz ao Python para executar o código na função. Para chamar uma função, você escreve o nome da função, seguido por qualquer informação necessária entre parênteses. Como nenhuma informação é necessária aqui, chamar nossa função é tão simples quanto digitar saudacao(). Como esperado, ele imprime Olá!.
Passando informações para uma função
Se você modificar a função saudacao() levemente, ela pode cumprimentar o usuário pelo nome. Para que a função faça isso, você insere usuario entre parênteses da definição da função em def saudacao(). Ao adicionar usuario aqui, você permite que a função aceite qualquer valor de usuario que você especificar. A função agora espera que você forneça um valor para usuario cada vez que você a chamar. Quando você chama saudacao(), você pode passar um nome, como 'maria', dentro dos parênteses:
def saudacao(usuario):
"""Mostrar a mensagem de boas vindas."""
print(f'Olá, {usuario.title()}!')
saudacao('maria')
Digitar saudacao('maria') chama saudacao() e dá à função as informações de que ela precisa para executar a chamada print(). A função aceita o nome que você passou e exibe a saudação para esse nome.
Da mesma forma, digitar saudacao('joão') chama saudacao(), passa 'joão' e imprime Olá, João! Você pode chamar saudacao() quantas vezes quiser e passar qualquer nome que quiser para produzir uma saída previsível todas as vezes.
Argumentos e Parâmetros
Na função saudacao() anterior, definimos saudacao() para exigir um valor para a variável usuario. Depois que chamamos a função e demos a ela a informação (o nome de uma pessoa), ela imprimiu a saudação correta.
A variável usuario na definição de saudacao() é um exemplo de um parâmetro, uma informação que a função precisa para fazer seu trabalho. O valor 'maria' em saudacao('maria') é um exemplo de um argumento. Um argumento é uma informação que é passada de uma chamada de função para uma função.
Quando chamamos a função, colocamos o valor com o qual queremos que a função trabalhe entre parênteses. Neste caso, o argumento 'maria' foi passado para a função saudacao(), e o valor foi atribuído ao parâmetro usuario.
Às vezes, as pessoas falam de argumentos e parâmetros de forma intercambiável. Não se surpreenda se você vir as variáveis em uma definição de função referidas como argumentos ou as variáveis em uma chamada de função referidas como parâmetros.
Tente você mesmo!
Exercício 01: Escreva uma função chamada mostrar_mensagem() que imprima uma frase contando a todos o que você está aprendendo neste capítulo. Chame a função e certifique-se de que a mensagem seja exibida corretamente.
Exercício 02: Escreva uma função chamada livro_favorito() que aceite um parâmetro, titulo. A função deve imprimir uma mensagem, como O meu livro favorito é Harry Potter. Chame a função, certificando-se de incluir um título de livro como argumento na chamada da função.
Passando argumentos
Como uma definição de função pode ter vários parâmetros, uma chamada de função pode precisar de vários argumentos. Você pode passar argumentos para suas funções de várias maneiras. Você pode usar argumentos posicionais, que precisam estar na mesma ordem em que os parâmetros foram escritos; argumentos de palavra-chave, onde cada argumento consiste em um nome de variável e um valor; e listas e dicionários de valores. Vamos dar uma olhada em cada um deles por vez.
Argumentos posicionais
Quando você chama uma função, o Python deve corresponder cada argumento na chamada da função com um parâmetro na definição da função. A maneira mais simples de fazer isso é com base na ordem dos argumentos fornecidos. Valores correspondidos dessa forma são chamados argumentos posicionais.
Para ver como isso funciona, considere uma função que exibe informações sobre animais de estimação. A função nos diz que tipo de animal cada animal de estimação é e o nome do animal, como mostrado aqui:
def descrever_pet(tipo_animal, nome): # 1
"""Mostrar as informações do pet."""
print(f'\nEu tenho um {tipo_animal}.')
print(f'Meu {tipo_animal} se chama {nome.title()}.')
descrever_pet('peixe', 'nemo') # 2
A definição mostra que esta função precisa de um tipo de animal e do nome do animal (1). Quando chamamos descrever_pet(), precisamos fornecer um tipo de animal e um nome, nessa ordem. Por exemplo, na chamada da função, o argumento 'peixe' é atribuído ao parâmetro tipo_animal e o argumento 'nemo' é atribuído ao parâmetro nome (2). No corpo da função, esses dois parâmetros são usados para exibir informações sobre o animal de estimação que está sendo descrito.
Chamadas de funções múltiplas
Você pode chamar uma função quantas vezes forem necessárias. Descrever um segundo animal de estimação diferente requer apenas mais uma chamada para descrever_pet():
descrever_pet('peixe', 'nemo')
descrever_pet('gato', 'frajola')
Nesta segunda chamada de função, passamos a descrever_pet() os argumentos 'gato' e 'frajola'. Assim como no conjunto anterior de argumentos que usamos, Python combina 'gato' com o parâmetro tipo_animal e 'frajola' com o parâmetro nome. Como antes, a função faz seu trabalho, mas desta vez ela imprime valores para um gato chamado Frajola.
Chamar uma função várias vezes é uma maneira muito eficiente de trabalhar. O código que descreve um animal de estimação é escrito uma vez na função. Então, sempre que você quiser descrever um novo animal de estimação, você chama a função com as informações do novo animal de estimação. Mesmo se o código para descrever um animal de estimação fosse expandido para 10 linhas, você ainda poderia descrever um novo animal de estimação em apenas uma linha chamando a função novamente.
A ordem importa em argumentos posicionais
Você pode obter resultados inesperados se misturar a ordem dos argumentos em uma chamada de função ao usar argumentos posicionais:
descrever_pet('nemo', 'peixe')
Nesta chamada de função, listamos o nome primeiro e o tipo de animal em segundo. Como o argumento 'nemo' é listado primeiro desta vez, esse valor é atribuído ao parâmetro tipo_animal. Da mesma forma, 'peixe' é atribuído a nome. Agora temos um “nemo” chamado “Peixe”.
Se você obtiver resultados engraçados como este, verifique se a ordem dos argumentos na chamada da função corresponde à ordem dos parâmetros na definição da função.
Argumentos de palavras-chave
Um argumento de palavra-chave é um par nome-valor que você passa para uma função. Você associa diretamente o nome e o valor dentro do argumento, então quando você passa o argumento para a função, não há confusão (você não vai acabar com um nemo chamado Peixe). Argumentos de palavra-chave livram você de ter que se preocupar em ordenar corretamente seus argumentos na chamada de função, e eles esclarecem o papel de cada valor na chamada de função.
Vamos reescrever a chamada da função usando argumentos de palavras-chave para chamar descrever_pet():
descrever_pet(tipo_animal='peixe', nome='nemo')
A função descrever_pet() não mudou. Mas quando chamamos a função, dizemos explicitamente ao Python com qual parâmetro cada argumento deve ser correspondido. Quando o Python lê a chamada da função, ele sabe atribuir o argumento 'peixe' ao parâmetro tipo_animal e o argumento 'nemo' ao nome. A saída mostra corretamente que temos um peixe chamado Nemo.
A ordem dos argumentos de palavra-chave não importa porque Python sabe onde cada valor deve ir. As duas chamadas de função a seguir são equivalentes:
descrever_pet(tipo_animal='peixe', nome='nemo')
descrever_pet(nome='nemo', tipo_animal='peixe')
Ao usar argumentos de palavras-chave, certifique-se de usar os nomes exatos dos parâmetros na definição da função.
Valores Padrão
Ao escrever uma função, você pode definir um valor padrão para cada parâmetro. Se um argumento para um parâmetro for fornecido na chamada de função, o Python usa o valor do argumento. Se não, ele usa o valor padrão do parâmetro. Então, quando você define um valor padrão para um parâmetro, você pode excluir o argumento correspondente que você normalmente escreveria na chamada de função. Usar valores padrão pode simplificar suas chamadas de função e esclarecer as maneiras como suas funções são normalmente usadas.
Por exemplo, se você notar que a maioria das chamadas para descrever_pet() estão sendo usadas para descrever cachorros, você pode definir o valor padrão de tipo_animal para 'cachorro'. Agora, qualquer um que chame descrever_pet() para um cachorro pode omitir essa informação:
def descrever_pet(nome, tipo_animal='cachorro'):
"""Mostrar as informações do pet."""
print(f'\nEu tenho um {tipo_animal}.')
print(f'Meu {tipo_animal} se chama {nome.title()}.')
descrever_pet(nome='rex')
Alteramos a definição de descrever_pet() para incluir um valor padrão, 'cachorro', para tipo_animal. Agora, quando a função é chamada sem tipo_animal especificado, o Python sabe usar o valor 'cachorro' para este parâmetro.
Note que a ordem dos parâmetros na definição da função teve que ser alterada. Como o valor padrão torna desnecessário especificar um tipo de animal como argumento, o único argumento restante na chamada da função é o nome do animal de estimação. O Python ainda interpreta isso como um argumento posicional, então se a função for chamada apenas com o nome de um animal de estimação, esse argumento corresponderá ao primeiro parâmetro listado na definição da função. Esta é a razão pela qual o primeiro parâmetro precisa ser nome.
A maneira mais simples de usar esta função agora é fornecer apenas o nome de um cachorro na chamada da função:
descrever_pet('rex')
Esta chamada de função teria a mesma saída do exemplo anterior. O único argumento fornecido é 'rex', então ele é correspondido com o primeiro parâmetro na definição, nome. Como nenhum argumento é fornecido para tipo_animal, Python usa o valor padrão 'cachorro'.
Para descrever um animal diferente de um cachorro, você pode usar uma chamada de função como esta:
descrever_pet(nome='nemo', tipo_animal='peixe')
Como um argumento explícito para tipo_animal é fornecido, o Python ignorará o valor padrão do parâmetro.
Quando você usa valores padrão, qualquer parâmetro com um valor padrão precisa ser listado depois de todos os parâmetros que não têm valores padrão. Isso permite que o Python continue interpretando argumentos posicionais corretamente.
Chamadas de função equivalentes
Como argumentos posicionais, argumentos de palavra-chave e valores padrão podem ser usados juntos, você frequentemente terá várias maneiras equivalentes de chamar uma função. Considere a seguinte definição para descrever_pet() com um valor padrão fornecido:
def descrever_pet(nome, tipo_animal='cachorro'):
Com essa definição, um argumento sempre precisa ser fornecido para nome, e esse valor pode ser fornecido usando o formato posicional ou de palavra-chave. Se o animal que está sendo descrito não for um cachorro, um argumento para tipo_animal deve ser incluído na chamada, e esse argumento também pode ser especificado usando o formato posicional ou de palavra-chave.
Todas as chamadas a seguir funcionariam para esta função:
# --- Um cachorro chamado Rex --- #
descrever_pet('rex')
descrever_pet(nome='rex')
# --- Um peixe chamado Nemo --- #
descrever_pet('nemo', 'peixe')
descrever_pet(nome='nemo', tipo_animal='peixe')
descrever_pet(tipo_animal='peixe', nome='nemo')
Cada uma dessas chamadas de função teria a mesma saída dos exemplos anteriores.
Não importa realmente qual estilo de chamada você usa. Contanto que suas chamadas de função produzam a saída que você quer, use apenas o estilo que você achar mais fácil de entender.
Evitando erros de argumento
Quando você começar a usar funções, não se surpreenda se encontrar erros sobre argumentos não correspondidos. Argumentos não correspondidos ocorrem quando você fornece menos ou mais argumentos do que uma função precisa para fazer seu trabalho. Por exemplo, aqui está o que acontece se tentarmos chamar descrever_pet() sem argumentos:
descrever_pet()
O Python reconhece que algumas informações estão faltando na chamada de função, e o traceback nos informa que há um erro.
O traceback primeiro nos diz a localização do problema, permitindo que olhemos para trás e vejamos que algo deu errado em nossa chamada de função. Em seguida, a chamada de função ofensiva é escrita para que vejamos. Por último, o traceback nos diz que a chamada está sem dois argumentos e relata os nomes dos argumentos ausentes. Se essa função estivesse em um arquivo separado, provavelmente poderíamos reescrever a chamada corretamente sem ter que abrir esse arquivo e ler o código da função.
Python é útil porque lê o código da função para nós e nos diz os nomes dos argumentos que precisamos fornecer. Esta é outra motivação para dar nomes descritivos às suas variáveis e funções. Se você fizer isso, as mensagens de erro do Python serão mais úteis para você e qualquer outra pessoa que possa usar seu código.
Se você fornecer muitos argumentos, deverá obter um rastreamento semelhante que pode ajudá-lo a corresponder corretamente sua chamada de função à definição da função.
Tente você mesmo!
Exercício 03: Escreva uma função chamada fazer_camisa() que aceita um tamanho e o texto de uma mensagem que deve ser impressa na camisa. A função deve imprimir uma frase resumindo o tamanho da camisa e a mensagem impressa nela. Chame a função uma vez usando argumentos posicionais para fazer uma camisa. Chame a função uma segunda vez usando argumentos de palavra-chave.
Exercício 04: Modifique a função fazer_camisa() para que as camisas sejam grandes por padrão com uma mensagem que diz Eu amo Python. Faça uma camisa grande e uma camisa média com a mensagem padrão, e uma camisa de qualquer tamanho com uma mensagem diferente.
Exercício 05: Escreva uma função chamada descrever_cidade() que aceite o nome de uma cidade e seu país. A função deve imprimir uma frase simples, como Tóquio está no país Japão. Dê ao parâmetro para o país um valor padrão. Chame sua função para três cidades diferentes, pelo menos uma das quais não esteja no país padrão.
Valores de retorno
Uma função nem sempre precisa exibir sua saída diretamente. Em vez disso, ela pode processar alguns dados e então retornar um valor ou conjunto de valores. O valor que a função retorna é chamado de valor de retorno. A instrução return pega um valor de dentro de uma função e o envia de volta para a linha que chamou a função. Os valores de retorno permitem que você mova muito do trabalho pesado do seu programa para funções, o que pode simplificar o corpo do seu programa.
Retornando um valor simples
Vamos dar uma olhada em uma função que recebe um nome e sobrenome e retorna um nome completo bem formatado:
def obter_nome_completo(nome, sobrenome):
"""Retorna o nome completo formatado."""
nome_completo = f'{nome} {sobrenome}' # 1
return nome_completo.title() # 2
musico = obter_nome_completo('john', 'lennon') # 3
print(musico)
A definição de obter_nome_completo() toma como parâmetros um primeiro e último nome. A função combina esses dois nomes, adiciona um espaço entre eles e atribui o resultado a nome_completo (1). O valor de nome_completo é convertido para para que fique em letras maiúsculas as iniciais e então retornado para a linha de chamada (2).
Quando você chama uma função que retorna um valor, você precisa fornecer uma variável à qual o valor de retorno pode ser atribuído. Neste caso, o valor retornado é atribuído à variável musico (3). A saída mostra um nome bem formatado composto pelas partes do nome de uma pessoa.
Pode parecer muito trabalho obter um nome bem formatado quando poderíamos simplesmente ter escrito:
print('John Lennon')
No entanto, quando você considera trabalhar com um programa grande que precisa armazenar muitos nomes e sobrenomes separadamente, funções como obter_nome_completo() se tornam muito úteis. Você armazena nomes e sobrenomes separadamente e então chama essa função sempre que quiser exibir um nome completo.
Tornando um argumento opcional
Às vezes, faz sentido tornar um argumento opcional, para que as pessoas que usam a função possam escolher fornecer informações extras somente se quiserem. Você pode usar valores padrão para tornar um argumento opcional.
Por exemplo, digamos que queremos expandir obter_nome_completo() para lidar com nomes do meio também. Uma primeira tentativa de incluir nomes do meio pode parecer com isto:
def obter_nome_completo(primeiro_nome, nome_meio, ultimo_nome):
"""Retorna o nome completo formatado."""
nome_completo = f'{primeiro_nome.title()} {nome_meio.title()} {ultimo_nome.title()}'
return nome_completo
musico = obter_nome_completo('john', 'winston', 'lennon')
print(musico)
Esta função funciona quando é dado um primeiro, segundo e último nome. A função pega todas as três partes de um nome e então constrói uma string a partir delas. A função adiciona espaços onde apropriado e converte o nome completo para maiúsculas e minúsculas.
Mas nomes do meio nem sempre são necessários, e essa função, como escrita, não funcionaria se você tentasse chamá-la com apenas um primeiro nome e um sobrenome. Para tornar o nome do meio opcional, podemos dar ao argumento nome_meio um valor padrão vazio e ignorar o argumento, a menos que o usuário forneça um valor. Para fazer obter_nome_completo() funcionar sem um nome do meio, definimos o valor padrão de nome_meio para uma string vazia e o movemos para o final da lista de parâmetros:
def obter_nome_completo(primeiro_nome, ultimo_nome, nome_meio=''):
"""Retorna o nome completo formatado."""
if nome_meio: # 1
nome_completo = f'{primeiro_nome.title()} {nome_meio.title()} {ultimo_nome.title()}'
else: # 2
nome_completo = f'{primeiro_nome.title()} {ultimo_nome.title()}'
return nome_completo
musico = obter_nome_completo('george', 'harrison')
print(musico)
musico = obter_nome_completo('john', 'lennon', 'winston') # 3
print(musico)
Neste exemplo, o nome é construído a partir de três partes possíveis. Como sempre há um primeiro e último nome, esses parâmetros são listados primeiro na definição da função. O nome do meio é opcional, então ele é listado por último na definição, e seu valor padrão é uma string vazia.
No corpo da função, verificamos se um nome do meio foi fornecido. Python interpreta strings não vazias como True, então o teste condicional if middle_name avalia como True se um argumento de nome do meio estiver na chamada de função (1). Se um nome do meio for fornecido, o primeiro, o segundo e o último nomes são combinados para formar um nome completo. Esse nome é então alterado para que os nomes começem com letra maiúscula e retornado para a linha de chamada de função, onde é atribuído à variável musico e impresso. Se nenhum nome do meio for fornecido, a string vazia falha no teste if e o bloco else é executado (2). O nome completo é feito apenas com um primeiro e último nome, e o nome formatado é retornado para a linha de chamada, onde é atribuído a musico e impresso.
Chamar essa função com um primeiro e último nome é direto. Se estivermos usando um nome do meio, no entanto, temos que garantir que o nome do meio seja o último argumento passado para que o Python combine os argumentos posicionais corretamente (3).
Esta versão modificada da nossa função funciona para pessoas que têm apenas um nome e sobrenome, e também funciona para pessoas que têm um nome do meio.
Valores opcionais permitem que funções lidem com uma ampla gama de casos de uso, enquanto permitem que chamadas de função permaneçam o mais simples possível.
Devolvendo um dicionário
Uma função pode retornar qualquer tipo de valor que você precise, incluindo estruturas de dados mais complicadas, como listas e dicionários. Por exemplo, a função a seguir recebe partes de um nome e retorna um dicionário representando uma pessoa:
def pessoa(primeiro_nome, ultimo_nome):
"""Retorna um dicionário com as informações sobre a pessoa."""
pessoa = {'nome': primeiro_nome, 'sobrenome': ultimo_nome} # 1
return pessoa # 2
musico = pessoa('john', 'lennon')
print(musico) # 3
A função pessoa() recebe um primeiro e último nome, e coloca esses valores em um dicionário (1). O valor de primeiro_nome é armazenado com a chave 'nome', e o valor de ultimo_nome é armazenado com a chave 'sobrenome'. Então, o dicionário inteiro representando a pessoa é retornado (2). O valor de retorno é impresso (3) com as duas partes originais de informação textual agora armazenadas em um dicionário.
Esta função pega informações textuais simples e as coloca em uma estrutura de dados mais significativa que permite que você trabalhe com as informações além de apenas imprimi-las. As strings 'john' e 'lennon' agora são rotuladas como um primeiro nome e sobrenome. Você pode facilmente estender esta função para aceitar valores opcionais como um nome do meio, uma idade, uma ocupação ou qualquer outra informação que você queira armazenar sobre uma pessoa. Por exemplo, a seguinte alteração permite que você armazene a idade de uma pessoa também:
def pessoa(primeiro_nome, ultimo_nome, idade=None):
"""Retorna um dicionário com as informações sobre a pessoa."""
pessoa = {'nome': primeiro_nome, 'sobrenome': ultimo_nome}
if idade:
pessoa['idade'] = idade
return pessoa
musico = pessoa('john', 'lennon', idade=40)
print(musico)
Adicionamos um novo parâmetro opcional idade à definição da função e atribuímos ao parâmetro o valor especial None, que é usado quando uma variável não tem um valor específico atribuído a ela. Você pode pensar em None como um valor de espaço reservado. Em testes condicionais, None é avaliado como False. Se a chamada da função incluir um valor para idade, esse valor será armazenado no dicionário. Esta função sempre armazena o nome de uma pessoa, mas também pode ser modificada para armazenar qualquer outra informação que você queira sobre uma pessoa.
Usando uma função com um loop while
Você pode usar funções com todas as estruturas Python que aprendeu até agora. Por exemplo, vamos usar a função obter_nome_completo() com um loop while para cumprimentar os usuários de modoa mais formal. Aqui está uma primeira tentativa de cumprimentar as pessoas usando seus primeiros e últimos nomes:
def obter_nome_completo(nome, sobrenome):
"""Retorna o nome completo formatado."""
nome_completo = f'{nome} {sobrenome}'
return nome_completo.title()
# --- Esse é um loop infinito!! --- #
while True:
print('\nDigite o seu nome:') # 1
primeiro_nome = input('Nome: ')
ultimo_nome = input('sobrenome: ')
nome_completo = obter_nome_completo(primeiro_nome, ultimo_nome)
print(f'\nOlá, {nome_completo}!')
Para este exemplo, usamos uma versão simples de obter_nome_completo() que não envolve nomes do meio. O loop while pede ao usuário para digitar seu nome, e nós pedimos seu primeiro e último nome separadamente (1).
Mas há um problema com esse loop while: não definimos uma condição de saída. Onde você coloca uma condição de saída quando solicita uma série de entradas? Queremos que o usuário consiga sair o mais facilmente possível, então cada prompt deve oferecer uma maneira de sair. A instrução break oferece uma maneira direta de sair do loop em qualquer prompt:
def obter_nome_completo(nome, sobrenome):
"""Retorna o nome completo formatado."""
nome_completo = f'{nome} {sobrenome}'
return nome_completo.title()
while True:
print('\nDigite o seu nome:')
print('(Aperte "q" para sair)')
primeiro_nome = input('Nome: ')
if primeiro_nome == 'q':
break
ultimo_nome = input('sobrenome: ')
if ultimo_nome == 'q':
break
nome_completo = obter_nome_completo(primeiro_nome, ultimo_nome)
print(f'\nOlá, {nome_completo}!')
Adicionamos uma mensagem que informa ao usuário como sair e, então, saímos do loop se o usuário digitar o valor de saída em qualquer prompt. Agora, o programa continuará cumprimentando as pessoas até que alguém digite q para qualquer nome.
Tente você mesmo!
Exercício 6: Escreva uma função chamada cidade_pais() que recebe o nome de uma cidade e seu país. A função deve retornar uma string formatada assim:
"Londres, Inglaterra"
Chame sua função com pelo menos três pares cidade-país e imprima os valores retornados.
Exercício 7: Escreva uma função chamada criar_album() que constrói um dicionário descrevendo um álbum de música. A função deve receber um nome de artista e um título de álbum, e deve retornar um dicionário contendo essas duas informações. Use a função para criar três dicionários representando álbuns diferentes. Imprima cada valor de retorno para mostrar que os dicionários estão armazenando as informações do álbum corretamente. Use None para adicionar um parâmetro opcional a criar_album() que permite armazenar o número de músicas em um álbum. Se a linha de chamada incluir um valor para o número de músicas, adicione esse valor ao dicionário do álbum. Faça pelo menos uma nova chamada de função que inclua o número de músicas em um álbum.
Exercício 8: Comece com seu programa do exercício 7. Escreva um loop while que permita que os usuários insiram o artista e o título de um álbum. Depois de ter essas informações, chame criar_album() com a entrada do usuário e imprima o dicionário criado. Certifique-se de incluir um valor de saída no loop while.
Passando uma lista
Você frequentemente achará útil passar uma lista para uma função, seja uma lista de nomes, números ou objetos mais complexos, como dicionários. Quando você passa uma lista para uma função, a função obtém acesso direto ao conteúdo da lista. Vamos usar funções para tornar o trabalho com listas mais eficiente.
Digamos que temos uma lista de usuários e queremos imprimir uma saudação para cada um. O exemplo a seguir envia uma lista de nomes para uma função chamada saudar_usuarios(), que cumprimenta cada pessoa na lista individualmente:
def saudar_usuarios(nomes):
"""Mostra uma mensagem de boas vindas para cada usuário na lista."""
for nome in nomes:
mensagem = f'Olá, {nome.title()}!'
print(mensagem)
usuarios = ['maria', 'joão', 'lucas']
saudar_usuarios(usuarios)
Definimos saudar_usuarios() para que ele espere uma lista de nomes, que ele atribui ao parâmetro nomes. A função faz um loop pela lista que recebe e imprime uma saudação para cada usuário. Fora da função, definimos uma lista de usuários e então passamos os nomes de usuarios da lista para saudar_usuarios() na chamada da função.
Esta é a saída que queríamos. Cada usuário vê uma saudação personalizada, e você pode chamar a função sempre que quiser cumprimentar um conjunto específico de usuários.
Modificando uma lista em uma função
Quando você passa uma lista para uma função, a função pode modificar a lista. Quaisquer alterações feitas na lista dentro do corpo da função são permanentes, permitindo que você trabalhe de forma eficiente mesmo quando estiver lidando com grandes quantidades de dados.
Considere uma empresa que cria modelos impressos em 3D de designs que os usuários enviam. Os designs que precisam ser impressos são armazenados em uma lista e, após serem impressos, são movidos para uma lista separada. O código a seguir faz isso sem usar funções:
# --- Modelos para impressão --- #
modelos_impressao = ['dinossauro', 'cubo', 'pirâmide']
modelos_prontos = []
# --- Mover cada modelo a ser impresso para a lista de modelos prontos --- #
while modelos_impressao:
impressao_atual = modelos_impressao.pop()
print(f'Imprimindo modelo: {impressao_atual}')
modelos_prontos.append(impressao_atual)
# --- Mostrar os modelos prontos --- #
print('\nOs seguintes modelos foram impressos:')
for modelo in modelos_prontos:
print(modelo)
Este programa começa com uma lista de designs que precisam ser impressos e uma lista vazia chamada modelos_prontos para a qual cada design será movido após ser impresso. Enquanto os designs permanecerem em modelos_impressao, o loop while simula a impressão de cada design removendo um design do final da lista, armazenando-o em impressao_atual e exibindo uma mensagem de que o design atual está sendo impresso. Em seguida, ele adiciona o design à lista de modelos concluídos. Quando o loop termina de ser executado, uma lista dos designs que foram impressos é exibida.
Podemos reorganizar esse código escrevendo duas funções, cada uma das quais faz um trabalho específico. A maior parte do código não mudará; estamos apenas estruturando-o com mais cuidado. A primeira função lidará com a impressão dos designs, e a segunda resumirá as impressões que foram feitas:
def imprimir_modelos(modelos_impressao, modelos_prontos): # 1
"""Move cada modelo a ser impresso para a lista de modelos prontos."""
while modelos_impressao:
impressao_atual = modelos_impressao.pop()
print(f'Imprimindo modelo: {impressao_atual}')
modelos_prontos.append(impressao_atual)
def mostrar_modelos_prontos(modelos_prontos): # 2
"""Mostra os modelos prontos."""
print('\nOs seguintes modelos foram impressos:')
for modelo in modelos_prontos:
print(modelo)
# --- Modelos para impressão --- #
modelos_impressao = ['dinossauro', 'cubo', 'pirâmide']
modelos_prontos = []
# --- Chamar as funções --- #
imprimir_modelos(modelos_impressao, modelos_prontos)
mostrar_modelos_prontos(modelos_prontos)
Definimos a função imprimir_modelos() com dois parâmetros: uma lista de designs que precisam ser impressos e uma lista de modelos concluídos (1). Dadas essas duas listas, a função simula a impressão de cada design esvaziando a lista de designs não impressos e preenchendo a lista de modelos concluídos. Em seguida, definimos a função mostrar_modelos_prontos() com um parâmetro: a lista de modelos concluídos (2). Dada essa lista, mostrar_modelos_prontos() exibe o nome de cada modelo que foi impresso.
Este programa tem a mesma saída que a versão sem funções, mas o código é muito mais organizado. O código que faz a maior parte do trabalho foi movido para duas funções separadas, o que torna a parte principal do programa mais fácil de entender. Observe o corpo do programa e observe como você pode acompanhar facilmente o que está acontecendo:
# --- Modelos para impressão --- #
modelos_impressao = ['dinossauro', 'cubo', 'pirâmide']
modelos_prontos = []
# --- Chamar as funções --- #
imprimir_modelos(modelos_impressao, modelos_prontos)
mostrar_modelos_prontos(modelos_prontos)
Configuramos uma lista de designs não impressos e uma lista vazia que conterá os modelos concluídos. Então, como já definimos nossas duas funções, tudo o que precisamos fazer é chamá-las e passar os argumentos corretos. Chamamos imprimir_modelos() e passamos as duas listas necessárias; como esperado, imprimir_modelos() simula a impressão dos designs. Então chamamos mostrar_modelos_prontos() e passamos a lista de modelos concluídos para que ele possa relatar os modelos que foram impressos. Os nomes descritivos das funções permitem que outros leiam esse código e o entendam, mesmo sem comentários explícitos.
Este programa é mais fácil de estender e manter do que a versão sem funções. Se precisarmos imprimir mais designs mais tarde, podemos simplesmente chamar imprimir_modelos() novamente. Se percebermos que o código de impressão precisa ser modificado, podemos alterar o código uma vez, e nossas alterações ocorrerão em todos os lugares em que a função for chamada. Esta técnica é mais eficiente do que ter que atualizar o código separadamente em vários lugares do programa.
Este exemplo também demonstra a ideia de que cada função deve ter um trabalho específico. A primeira função imprime cada design, e a segunda exibe os modelos concluídos. Isso é mais benéfico do que usar uma função para fazer os dois trabalhos. Se você estiver escrevendo uma função e perceber que ela está fazendo muitas tarefas diferentes, tente dividir o código em duas funções. Lembre-se de que você sempre pode chamar uma função de outra função, o que pode ser útil ao dividir uma tarefa complexa em uma série de etapas.
Impedindo que uma função modifique uma lista
Às vezes, você vai querer impedir que uma função modifique uma lista. Por exemplo, digamos que você comece com uma lista de designs não impressos e escreva uma função para movê-los para uma lista de modelos concluídos, como no exemplo anterior. Você pode decidir que, mesmo que tenha impresso todos os designs, você quer manter a lista original de designs não impressos para seus registros. Mas como você moveu todos os nomes de design de modelos_impressao, a lista agora está vazia, e a lista vazia é a única versão que você tem; o original se foi. Neste caso, você pode resolver este problema passando à função uma cópia da lista, não o original. Quaisquer alterações que a função fizer na lista afetarão apenas a cópia, deixando a lista original intacta.
Você pode enviar uma cópia de uma lista para uma função como esta:
funcao(lista[:])
A notação de fatia ([:]) faz uma cópia da lista para enviar à função. Se não quiséssemos esvaziar a lista de designs não impressos, poderíamos chamar imprimir_modelos() assim:
imprimir_modelos(modelos_impressao[:], modelos_prontos)
A função imprimir_modelos() pode fazer seu trabalho porque ainda recebe os nomes de todos os designs não impressos. Mas dessa vez ela usa uma cópia da lista original de designs não impressos, não a lista real modelos_impressao. A lista modelos_prontos será preenchida com os nomes dos modelos impressos como fazia antes, mas a lista original de designs não impressos não será afetada pela função.
Mesmo que você possa preservar o conteúdo de uma lista passando uma cópia dela para suas funções, você deve passar a lista original para as funções, a menos que tenha um motivo específico para passar uma cópia. É mais eficiente para uma função trabalhar com uma lista existente, porque isso evita usar o tempo e a memória necessários para fazer uma cópia separada. Isso é especialmente verdadeiro ao trabalhar com listas grandes.
Tente você mesmo!
Exercício 09: Crie uma lista contendo uma série de mensagens de texto curtas. Passe a lista para uma função chamada mostrar_mensagens(), que imprime cada mensagem de texto.
Exercício 10: Comece com uma cópia do seu programa do exercício 9. Escreva uma função chamada enviar_mensagens() que imprima cada mensagem de texto e mova cada mensagem para uma nova lista chamada mensagens_enviadas conforme ela é impressa. Após chamar a função, imprima ambas as listas para certificar-se de que as mensagens foram movidas corretamente.
Exercício 11: Comece com seu trabalho do exercício 10. Chame a função enviar_mensagens() com uma cópia da lista de mensagens. Após chamar a função, imprima ambas as suas listas para mostrar que a lista original reteve suas mensagens.
Passando um número arbitrário de argumentos
Às vezes, você não saberá com antecedência quantos argumentos uma função precisa aceitar. Felizmente, Python permite que uma função colete um número arbitrário de argumentos da declaração de chamada.
Por exemplo, considere uma função que prepara uma pizza. Ela precisa aceitar uma série de coberturas, mas você não pode saber com antecedência quantas coberturas uma pessoa vai querer. A função no exemplo a seguir tem um parâmetro, *coberturas, mas esse parâmetro coleta tantos argumentos quanto a linha de chamada fornece:
def criar_pizza(*coberturas):
"""Mostra todas as coberturas que o cliente pediu."""
print(coberturas)
criar_pizza('bacon')
criar_pizza('da casa', '4 queijos', 'frango')
O asterisco no nome do parâmetro *coberturas diz ao Python para fazer uma tupla chamada coberturas, contendo todos os valores que esta função recebe. A chamada print() no corpo da função produz uma saída mostrando que o Python pode manipular uma chamada de função com um valor e uma chamada com três valores. Ele trata as diferentes chamadas de forma semelhante. Observe que o Python empacota os argumentos em uma tupla, mesmo que a função receba apenas um valor.
Agora podemos substituir a chamada print() por um loop que percorre a lista de coberturas e descreve a pizza que está sendo pedida:
def criar_pizza(*coberturas):
"""Mostra em tópicos as coberturas solicitadas pelo cliente."""
print('\nCriando a pizza com as seguintes coberturas:')
for cobertura in coberturas:
print(f'- {cobertura}')
criar_pizza('bacon')
criar_pizza('da casa', '4 queijos', 'frango')
A função responde apropriadamente, independentemente de receber um valor ou três valores. Essa sintaxe funciona, não importa quantos argumentos a função receba.
Misturando argumentos posicionais e arbitrários
Se você quiser que uma função aceite vários tipos diferentes de argumentos, o parâmetro que aceita um número arbitrário de argumentos deve ser colocado por último na definição da função. O Python combina argumentos posicionais e de palavra-chave primeiro e então coleta quaisquer argumentos restantes no parâmetro final.
Por exemplo, se a função precisa receber um tamanho para a pizza, esse parâmetro deve vir antes do parâmetro *coberturas:
def criar_pizza(tamanho, *coberturas):
"""Mostra em tópicos as coberturas solicitadas pelo cliente."""
print(f'\nCriando a pizza de tamanho {tamanho} com as seguintes coberturas:')
for cobertura in coberturas:
print(f'- {cobertura}')
criar_pizza('broto', 'bacon')
criar_pizza('grande', 'da casa', '4 queijos', 'frango')
Você verá frequentemente o nome de parâmetro genérico *args, que coleta argumentos posicionais arbitrários como este.
Usando argumentos de palavras-chave arbitrárias
Às vezes, você vai querer aceitar um número arbitrário de argumentos, mas não vai saber com antecedência que tipo de informação será passada para a função. Nesse caso, você pode escrever funções que aceitem tantos pares de chave-valor quanto a declaração de chamada fornecer. Um exemplo envolve a construção de perfis de usuário: você sabe que obterá informações sobre um usuário, mas não tem certeza de que tipo de informação receberá. A função criar_profile() no exemplo a seguir sempre aceita um primeiro e último nome, mas também aceita um número arbitrário de argumentos de palavra-chave:
def criar_perfil(nome, sobrenome, **info_usuario):
"""Criar um dicionário com as informações do usuário."""
info_usuario['nome'] = nome # 1
info_usuario['sobrenome'] = sobrenome
return info_usuario
perfil_usuario = criar_perfil(
'wolfgang',
'mozart',
localizacao='austria',
atuacao='música'
)
print(perfil_usuario)
A definição de criar_perfil() espera um primeiro e último nome, e então permite que o usuário passe quantos pares nome-valor quiser. Os asteriscos duplos antes do parâmetro **info_usuario fazem com que o Python crie um dicionário chamado info_usuario contendo todos os pares nome-valor extras que a função recebe. Dentro da função, você pode acessar os pares chave-valor em info_usuario assim como faria para qualquer dicionário.
No corpo de criar_perfil(), adicionamos o primeiro e o último nome ao dicionário info_usuario porque sempre receberemos essas duas informações do usuário (1), e elas ainda não foram colocadas no dicionário. Então, retornamos o dicionário info_usuario para a linha de chamada da função.
Chamamos criar_perfil(), passando a ele o primeiro nome 'wolfgang', o sobrenome 'mozart' e os dois pares de chave-valor localizacao='austria' e atuacao='música'. Atribuímos o perfil retornado a perfil_usuario e o imprimimos na tela.
O dicionário retornado contém o primeiro e último nome do usuário e, neste caso, o local e o campo de atuação também. A função funcionará não importa quantos pares de chave-valor adicionais sejam fornecidos na chamada de função.
Você pode misturar valores posicionais, de palavra-chave e arbitrários de muitas maneiras diferentes ao escrever suas próprias funções. É útil saber que todos esses tipos de argumento existem porque você os verá frequentemente quando começar a ler o código de outras pessoas. É preciso prática para usar os diferentes tipos corretamente e saber quando usar cada tipo. Por enquanto, lembre-se de usar a abordagem mais simples que faça o trabalho. Conforme você progride, aprenderá a usar a abordagem mais eficiente a cada vez.
Você frequentemente verá o nome do parâmetro **kwargs usado para coletar argumentos de palavras-chave não específicos.
Tente você mesmo!
Exercício 12: Escreva uma função que aceite uma lista de itens que uma pessoa quer em um sanduíche. A função deve ter um parâmetro que colete tantos itens quanto a chamada de função fornecer, e deve imprimir um resumo do sanduíche que está sendo pedido. Chame a função três vezes, usando um número diferente de argumentos a cada vez.
Exercício 13: Comece com uma cópia da função criar_perfil(). Crie um perfil seu chamando criar_perfil(), usando seu nome e sobrenome e três outros pares de chave-valor que o descrevam.
Exercício 14: Escreva uma função que armazene informações sobre um carro em um dicionário. A função deve sempre receber um nome de fabricante e modelo. Ela deve então aceitar um número arbitrário de argumentos de palavra-chave. Chame a função com as informações necessárias e dois outros pares nome-valor, como uma cor ou um recurso opcional. Sua função deve funcionar para uma chamada como esta:
carro = criar_carro('volvo', 'S60', cor='cinza', alarme=True)
Imprima o dicionário retornado para garantir que todas as informações foram armazenadas corretamente.
Armazenando suas funções em módulos
Uma vantagem das funções é a maneira como elas separam blocos de código do seu programa principal. Quando você usa nomes descritivos para suas funções, seus programas se tornam muito mais fáceis de seguir. Você pode ir um passo além armazenando suas funções em um arquivo separado chamado módulo e então importando esse módulo para seu programa principal. Uma declaração import diz ao Python para tornar o código em um módulo disponível no arquivo de programa em execução no momento.
Armazenar suas funções em um arquivo separado permite que você esconda os detalhes do código do seu programa e se concentre na lógica de nível mais alto. Também permite que você reutilize funções em muitos programas diferentes. Quando você armazena suas funções em arquivos separados, você pode compartilhar esses arquivos com outros programadores sem ter que compartilhar seu programa inteiro. Saber como importar funções também permite que você use bibliotecas de funções que outros programadores escreveram.
Existem várias maneiras de importar um módulo, e veremos cada uma delas brevemente.
Importando um módulo inteiro
Para começar a importar funções, primeiro precisamos criar um módulo. Um módulo é um arquivo terminado em .py que contém o código que você deseja importar para seu programa. Vamos fazer um módulo que contém a função criar_pizza(). Para fazer este módulo, criaremos o arquivo pizza.py com a função criar_pizza():
def criar_pizza(tamanho, *coberturas):
"""Mostra em tópicos as coberturas solicitadas pelo cliente."""
print(f'\nCriando a pizza de tamanho {tamanho} com as seguintes coberturas:')
for cobertura in coberturas:
print(f'- {cobertura}')
Agora, faremos um arquivo separado chamado criar_pizzas.py no mesmo diretório que o arquivo pizza.py. O priemiro arquivo importa o módulo que acabamos de criar e, em seguida, faz duas chamadas para criar_pizza():
import pizza
pizza.criar_pizza('broto', 'bacon') # 1
pizza.criar_pizza('grande', 'da casa', '4 queijos', 'frango')
Quando o Python lê esse arquivo, a linha import pizza diz ao Python para abrir o arquivo pizza.py e copiar todas as funções dele para esse programa. Na verdade, você não vê o código sendo copiado entre os arquivos porque o Python copia o código nos bastidores, logo antes do programa ser executado. Tudo o que você precisa saber é que qualquer função definida em pizza.py agora estará disponível em criar_pizzas.py.
Para chamar uma função de um módulo importado, insira o nome do módulo que você importou, pizza, seguido pelo nome da função, criar_pizza(), separado por um ponto (1). Este código produz a mesma saída que o programa original que não importou um módulo.
Essa primeira abordagem para importação, na qual você simplesmente escreve import seguido pelo nome do módulo, torna cada função do módulo disponível no seu programa. Se você usar esse tipo de declaração import para importar um módulo inteiro chamado modulo.py, cada função no módulo estará disponível por meio da seguinte sintaxe:
modulo.funcao()
Importando funções específicas
Você também pode importar uma função específica de um módulo. Aqui está a sintaxe geral para essa abordagem:
from modulo import funcao
Você pode importar quantas funções quiser de um módulo separando o nome de cada função com uma vírgula:
from modulo import funcao_1, funcao_2, funcao_3
O exemplo criar_pizzas.py ficaria assim se quiséssemos importar apenas a função que vamos usar:
from pizza import criar_pizza
criar_pizza('broto', 'bacon')
criar_pizza('grande', 'da casa', '4 queijos', 'frango')
Com essa sintaxe, você não precisa usar a notação de ponto quando chamar uma função. Como importamos explicitamente a função criar_pizza() na declaração import, podemos chamá-la pelo nome quando usarmos a função.
Usando as para dar um apelido a uma função
Se o nome de uma função que você está importando pode entrar em conflito com um nome existente no seu programa, ou se o nome da função for longo, você pode usar um apelido curto e exclusivo — um nome alternativo semelhante a um apelido para a função. Você dará à função esse apelido especial quando importar a função.
Aqui damos à função criar_pizza() um apelido, cp(), importando criar_pizza como cp. A palavra-chave as renomeia uma função usando o apelido que você fornece:
from pizza import criar_pizza as cp
cp('broto', 'bacon')
cp('grande', 'da casa', '4 queijos', 'frango')
A declaração import mostrada aqui renomeia a função criar_pizza() para cp() neste programa. Sempre que quisermos chamar criar_pizza(), podemos simplesmente escrever cp() em vez disso, e o Python executará o código em criar_pizza(), evitando qualquer confusão com outra função criar_pizza() que você possa ter escrito neste arquivo de programa. A sintaxe geral para fornecer um apelido é:
from modulo import funcao as f
Usando as para dar um apelido a um módulo
Você também pode fornecer um alias para um nome de módulo. Dar a um módulo um alias curto, como p para pizza, permite que você chame as funções do módulo mais rapidamente. Chamar p.criar_pizzas() é mais conciso do que chamar pizza.criar_pizzas():
import pizza as p
p.criar_pizzas('broto', 'bacon')
p.criar_pizzas('grande', 'da casa', '4 queijos', 'frango')
O módulo pizza recebe o apelido p na declaração de importação, mas todas as funções do módulo mantêm seus nomes originais. Chamar as funções escrevendo p.criar_pizzas() não é apenas mais conciso do que pizza.criar_pizzas(), mas também redireciona sua atenção do nome do módulo e permite que você se concentre nos nomes descritivos de suas funções. Esses nomes de função, que dizem claramente o que cada função faz, são mais importantes para a legibilidade do seu código do que usar o nome completo do módulo. A sintaxe geral para essa abordagem é:
import modulo as m
Importando todas as funções em um módulo
Você pode dizer ao Python para importar todas as funções em um módulo usando o operador asterisco (*):
from pizza import *
criar_pizzas('broto', 'bacon')
criar_pizzas('grande', 'da casa', '4 queijos', 'frango')
O asterisco na declaração import diz ao Python para copiar todas as funções do módulo pizza para este arquivo de programa. Como todas as funções são importadas, você pode chamar cada função pelo nome sem usar a notação de ponto. No entanto, é melhor não usar essa abordagem quando estiver trabalhando com módulos maiores que você não escreveu: se o módulo tiver um nome de função que corresponda a um nome existente no seu projeto, você poderá obter resultados inesperados. O Python pode ver várias funções ou variáveis com o mesmo nome e, em vez de importar todas as funções separadamente, ele substituirá as funções.
A melhor abordagem é importar a função ou funções que você quer, ou importar o módulo inteiro e usar a notação de ponto. Isso leva a um código claro que é fácil de ler e entender. É importante que você reconheça instruções import que vimos, pois quando as vir no código de outras pessoas você não acahrá estranho. A sintaxe geral para essa abordagem é:
from modulo import *
Estilizando a função
Você precisa manter alguns detalhes em mente ao estilizar funções. Funções devem ter nomes descritivos, e esses nomes devem usar letras minúsculas e sublinhados. Nomes descritivos ajudam você e outros a entender o que seu código está tentando fazer. Nomes de módulos também devem usar essas convenções.
Cada função deve ter um comentário que explique concisamente o que a função faz. Este comentário deve aparecer imediatamente após a definição da função e usar o formato docstring. Em uma função bem documentada, outros programadores podem usar a função lendo apenas a descrição na docstring. Eles devem ser capazes de confiar que o código funciona conforme descrito e, desde que saibam o nome da função, os argumentos de que ela precisa e o tipo de valor que ela retorna, eles devem ser capazes de usá-la em seus programas.
Se você especificar um valor padrão para um parâmetro, nenhum espaço deverá ser usado em nenhum lado do sinal de igual:
def funcao(parametro_0, parametro_1='valor padrão')
A mesma convenção deve ser usada para argumentos de palavras-chave em chamadas de função:
funcao(valor_0, parametro_1='valor')
O PEP 8 recomenda que você limite as linhas de código a 79 caracteres para que cada linha fique visível em uma janela de editor de tamanho razoável. Se um conjunto de parâmetros fizer com que a definição de uma função tenha mais de 79 caracteres, pressione ENTER após o parêntese de abertura na linha de definição. Na próxima linha, pressione a tecla TAB duas vezes para separar a lista de argumentos do corpo da função, que será recuada apenas um nível.
A maioria dos editores alinha automaticamente quaisquer linhas adicionais de argumentos para corresponder ao recuo que você estabeleceu na primeira linha:
def funcao(
parametro_0, parametro_1, parametro_2,
parametro_3, parametro_5, parametro_5
)
Se o seu programa ou módulo tiver mais de uma função, você pode separar cada uma delas por duas linhas em branco para facilitar a visualização de onde uma função termina e a próxima começa.
Todas as instruções import devem ser escritas no início de um arquivo. A única exceção é se você usar comentários no início do seu arquivo para descrever o programa geral.
Tente você mesmo!
Exercício 15: Coloque as funções para o exemplo da empresa de impressão 3D em um arquivo separado chamado funcoes_impressao.py. Escreva uma declaração import no topo de modelos_impressos.py e modifique o arquivo para usar as funções importadas.
Exercício 16: Usando um programa que você escreveu que tem uma função nele, armazene essa função em um arquivo separado. Importe a função para seu arquivo de programa principal e chame a função usando cada uma dessas abordagens:
import modulo
from modulo import funcao
from modulo import funcao as f
import modulo as m
from modulo import *
Exercício 17: Escolha três programas que você escreveu para este capítulo e certifique-se de que eles sigam as diretrizes de estilo descritas nesta seção.
Resumo
Neste capítulo, você aprendeu como escrever funções e passar argumentos para que suas funções tenham acesso às informações de que precisam para fazer seu trabalho. Você aprendeu como usar argumentos posicionais e de palavra-chave, e também como aceitar um número arbitrário de argumentos. Você viu funções que exibem saída e funções que retornam valores. Você aprendeu como usar funções com listas, dicionários, instruções if e loops while. Você também viu como armazenar suas funções em arquivos separados chamados módulos, para que seus arquivos de programa sejam mais simples e fáceis de entender. Finalmente, você aprendeu a estilizar suas funções para que seus programas continuem bem estruturados e mais fáceis possível para você e outros lerem.
Um dos seus objetivos como programador deve ser escrever código simples que faça o que você quer que ele faça, e as funções ajudam você a fazer isso. Elas permitem que você escreva blocos de código e os deixe sozinhos quando souber que eles funcionam. Quando você sabe que uma função faz seu trabalho corretamente, você pode confiar que ela continuará a funcionar e passar para sua próxima tarefa de codificação.
Funções permitem que você escreva código uma vez e então reutilize esse código quantas vezes quiser. Quando você precisa executar o código em uma função, tudo o que você precisa fazer é escrever uma chamada de uma linha e a função faz seu trabalho. Quando você precisa modificar o comportamento de uma função, você só precisa modificar um bloco de código, e sua mudança entra em vigor em todos os lugares que você fez uma chamada para essa função.
Usar funções torna seus programas mais fáceis de ler, e bons nomes de funções resumem o que cada parte de um programa faz. Ler uma série de chamadas de função dá a você uma noção muito mais rápida do que um programa faz do que ler uma longa série de blocos de código.
As funções também tornam seu código mais fácil de testar e depurar. Quando a maior parte do trabalho do seu programa é feito por um conjunto de funções, cada uma das quais tem uma tarefa específica, é muito mais fácil testar e manter o código que você escreveu. Você pode escrever um programa separado que chama cada função e testa se cada função funciona em todas as situações que pode encontrar. Quando você faz isso, pode ter certeza de que suas funções funcionarão corretamente sempre que você as chamar.
No Capítulo 9, você aprenderá a escrever classes. As classes combinam funções e dados em um pacote organizado que pode ser usado de maneiras flexíveis e eficientes.