Como Declarar uma Função em Python? (Com Exemplos)

Para declarar uma função em Python use a seguinte sintaxe:

def funcao(parametros):
    # Bloco de código da função
    return valorCode language: Python (python)
Como Declarar uma Função em Python
Como Declarar uma Função em Python

Antes de explicar essa sintaxe em detalhes, é importante que você entenda o conceito de função.

Eu sugiro que você execute o código Python mostrado nesse post no Jupyter Lab para treinar.

O Jupyter Lab é uma ferramenta ótima para começar a programar e deve fazer parte da sua lista de ferramentas como programador.

Se você ainda não tem o Jupyter Lab instalado, dá uma olhada nesse post onde eu explico o passo-a-passo para a instalação.

Como esse assunto é extenso, eu separei esse post em seções, para facilitar o entendimento.

O que é uma função em programação?

Uma função é um bloco de código que tem um nome, recebe parâmetros para executar um conjunto determinado de instruções e retorna um valor como resultado do processamento. A função serve para dividir o programa em partes menores reutilizáveis, com finalidades específicas.

Essa definição é semelhante à definição matemática de função, que determina a relação entre dois conjuntos.

Veja o exemplo abaixo. A função divide o número 1 pelo número fornecido e retorna o resultado, ou retorna None se o denominador for igual a 0.

def dividirPositivo(x):
    if x == 0:
        return None
    else:
        return 1 / x

print(dividirPositivo(1))  #  Retorna 1.0Code language: Python (python)

De fato, a função 1/x só não é definida quando o valor de x é igual a 0, mas tem valores negativos em y quando x < 0.

Gráfico da função matemática f(x)=1/x
Figura 1 – Gráfico da função matemática f(x)=1/x

Na definição matemática de função, para cada valor de x (entrada) só pode haver um valor de y (conjunto de valores do resultado).

No entanto, a função em programação é mais flexível, já que pode retornar mais de um valor diferentes para uma entrada, dependendo do contexto, e pode retornar qualquer valor como resultado, inclusive o tipo nulo.

Certo, chega de matemática…

Agora que você já entendeu o que é uma função e qual é a sua finalidade (sua função?) em um programa, veja como usar as funções embutidas do Python e aprenda a definir sua própria função.

A linguagem Python possui algumas funções embutidas. Para ser mais exato, na versão 3.8, elas são 69.

Para usar uma função embutida, você não precisa fazer nada. Só precisa chamar a função.

Se você já leu outros posts aqui no site, com certeza já viu a função embutida print() sendo chamada. Ela não retorna um valor, mas serve para imprimir na tela o parâmetro passado.

print(10)  #  Imprime 10 na telaCode language: Python (python)

As funções embutidas no Python e nas bibliotecas que você pode importar te permitem executar todas as operações que o seu programa precisar.

Então, se você estiver criando um script simples, pode abrir o editor de texto e digitar todas as instruções necessárias para fazer o seu script funcionar, uma em cada linha.

Mas se você estiver escrevendo um programa complexo, como um site Web ou uma API Rest, vai querer definir suas próprias funções, para separar seu código de forma mais legível e para poder reutilizar blocos de funcionalidades.

Esse é o conceito de modularidade e significa dividir um problema complexo em partes menores, cada uma com foco em uma tarefa específica.

Veja a seguir os detalhes da definição de uma função em Python.

Qual é a sintaxe para declarar uma função em Python?

A definição de uma função em Python começa com palavra reservada def, seguida do nome da função e dos parâmetros entre parênteses, terminando com dois pontos :.

Por exemplo:

def somarDoisNumeros(a, b):
    return a + b

print(somarDoisNumeros(1, 3))  #  Chama a função e imprime o resultadoCode language: Python (python)

No exemplo acima, dois parâmetros (ou argumentos), a e b, são passados para a função somarDoisNumeros().

A palavra reservada return identifica o que é retornado pela função. Nesse exemplo, a função retorna a soma dos valores dos parâmetros.

!

Sempre que o seu código chamar uma função que retorna um valor, esse valor deve ser atribuído a uma variável ou usado como parâmetro para outra função, como print(). Caso contrário, a função é executada, mas o valor retornado é perdido.

No entanto, o contrário não é permitido. Você não pode atribuir uma variável a uma chamada de função.

Por exemplo, a atribuição int('1') = x resulta em erro.

Uma função também pode não retornar um valor de forma explícita.

def somarDoisNumeros(a, b):
    print(a + b)

somarDoisNumeros(1, 3)  #  Só chama a funçãoCode language: Python (python)

A função acima faz a mesma coisa que a função do exemplo anterior, mas em vez de retornar o valor da soma para o código que chama a função, ela só imprime o valor na tela. Eu explico melhor os tipos de retorno de uma função mais adiante.

Observe que o bloco de código dentro da função deve ser indentado, como nas declarações condicionais if e nos laços for e while.

Como a função nem sempre tem a palavra reservada return, o interpretador Python identifica o fim do bloco da função quando a indentação do código retorna ao mesmo nível da palavra def.

Quanto ao nome da função, é considerada uma boa prática usar um verbo para expressar a finalidade da função. Pense nisso da seguinte forma: uma função é responsável por fazer alguma coisa. Por exemplo, calcularMedia(), gerarRelatorio(), inserirRegistro(), apagarFicha().

O nome da função também é um identificador. Por isso, segue as mesmas regras que existem para os nomes de variáveis. Se você quer conhecer todas essas regras, dá uma olhada nesse post completo sobre nomes de variáveis.

Quando uma função é chamada, as variáveis criadas dentro dela são isoladas das demais variáveis do programa.

Por enquanto você não precisa se preocupar em entender como essa separação acontece, mas isso significa que duas funções podem usar variáveis com o mesmo nome sem risco de interferência.

Por exemplo:

def somar(a, b):
    x = a + b
    print(f'x em somar é {x}')
    return x

def dividir(a, b, c):
    x = somar(a, b) / c
    print(f'x em dividir é {x}')
    return x

x = 5
y = 4
c = 3
print(f'Resultado da função: {dividir(x, y, c)}')
print(f'x aqui fora da função ainda é {x}')Code language: Python (python)

No exemplo acima, as variáveis x, y e c são declaradas no corpo do programa.

A função dividir() também define uma variável x, para receber o resultado de uma divisão. Mas antes, a função somar() é chamada dentro de dividir(), para calcular o valor da soma dos dois primeiros parâmetros.

Já a função somar(), também tem uma variável x, para receber o resultado da soma dos parâmetros.

Durante a execução do programa, é possível acompanhar os valores atribuídos a cada variável x e perceber que elas não interferem umas nas outras. O resultado é mostrado abaixo.

x em somar é 9
x em dividir é 3.0
Resultado da função: 3.0
x aqui fora da função ainda é 5Code language: plaintext (plaintext)

Como passar parâmetros para uma função em Python?

Uma função pode receber qualquer número de parâmetros e cada linguagem de programação tem seus mecanismos próprios para isso.

Veja a seguir um resumo de como fazer isso em Python.

O termo “parâmetro” é usado para indicar os nomes entre parênteses existentes na declaração da função. Já o termo “argumento” se refere às variáveis ou aos valores literais passados quando uma linha de código chama a função.

É muito comum ver programadores usando somente o termo “parâmetro”, sem todo esse rigor científico.

Por isso, nesse post, vou usar o termo “parâmetro” para indicar as duas coisas. Se você achar que ficou difícil de entender, dê a sua opinião nos comentários.

Parâmetros posicionais

Também conhecidos como parâmetros obrigatórios, são escritos entre parênteses e separados por vírgula na definição da função.

O código que chama a função deve usar a mesma quantidade de parâmetros da declaração e na mesma ordem.

def dim(x, y):
    return x - y
dim(5, 4)Code language: Python (python)

Esse tipo de parâmetro é o mais simples de usar, mas também é o menos flexível.

No exemplo acima, se a função for chamada com os valores dim(4, 5), o resultado será -1.

Parâmetros nomeados

Os parâmetros nomeados têm a forma de pares chave = valor.

Todas as chaves usadas na chamada da função devem ter uma correspondência exata com o nome de um parâmetro declarado na função, ou o interpretador Python irá gerar um erro.

def dim(x, y):
    return x - y
dim(x = 5, J = 4)  # Erro! ‘J’ não existe.Code language: Python (python)

Usando parâmetros nomeados, a ordem dos parâmetros na chamada da função pode ser qualquer uma, mas a quantidade ainda precisa ser a mesma.

dim(y = 4, x = 5)Code language: Python (python)

É possível usar uma mistura de parâmetros posicionais e nomeados para chamar uma função, mas os parâmetros posicionais sempre devem vir primeiro.

dim(5, y = 4)  #  OK

dim(x = 5, 4)  #  Erro!Code language: Python (python)

Parâmetros com valores default

É possível declarar uma função com valores default para os parâmetros. Dessa forma, você pode chamar a função sem passar esses parâmetros.

Caso você forneça parte dos parâmetros de forma posicional, eles serão atribuídos a partir do primeiro em diante. Se você quiser usar o valor default dos parâmetros iniciais, vai ter que chamar a função usando parâmetros nomeados.

def dim(x = 2, y = 1):  #  Valores default
    return x - y
dim()  #  Chamada sem parâmetros. O resultado é 1.
dim(9)  #  1º parâmetro. O resultado é 8.
dim(y = 2)  #  O resultado é 0.
dim(10, 5)  #  Ignora os valores default.Code language: Python (python)

A partir da versão 3.8 da linguagem Python, há uma nova maneira de separar os parâmetros posicionais e nomeados usando os caracteres / e *. Eu não vou cobrir essa forma aqui para não aumentar demais o tamanho desse post.

Caso queira saber mais sobre o assunto, consulte a documentação oficial da linguagem ou o tutorial Python.

Lista de parâmetros de tamanho variável

Caso você esteja definindo uma função e não saiba a quantidade exata de parâmetros que ela pode receber, você pode usar um asterisco * na frente do nome de um parâmetro.

Isso indica que todos os parâmetros passados à função serão empacotados em uma tupla e disponibilizados para a função.

A função do exemplo abaixo calcula a média de notas, com qualquer quantidade de entradas.

def calcularMedia(*args):
    print(args)
    return sum(args) / len(args)

media = calcularMedia(7.0, 7.5, 9.5, 5)
print(media)
#  Resultado: 7.25

media = calcularMedia(6.5, 6, 5.5)
print(media)
#  Resultado: 6.0Code language: Python (python)

Perceba que o * só é usado na definição do parâmetro. Ao usar o valor de args dentro da função, esse parâmetro é tratado como uma tupla comum, como mostra o resultado da função print().

A função do exemplo acima retorna o valor da soma dos elementos da tupla dividido pela quantidade de elementos da tupla, não importando quantos são.

O inverso disso também funciona em Python. Você pode passar uma tupla para uma função e usar o asterisco para indicar que ela deve ser desempacotada, ou seja, seus valores devem ser passados um a um como parâmetros para a função.

O exemplo a seguir usa a função dim(), que já foi mostrada antes. Perceba que o desempacotamento pode ser usado com qualquer função e funciona não só para tuplas, mas também para listas e conjuntos.

def dim(x, y):
    return x - y

tupla = (5, 4)
print(dim(*tupla))

lista = [5, 4]
print(dim(*lista))

conjunto = (5, 4)
print(dim(*conjunto))Code language: Python (python)

É possível até usar o empacotamento e desempacotamento ao mesmo tempo:

def calcularMedia(*args):
    print(args)
    return sum(args) / len(args)

t = (7.0, 7.5, 9.5, 5)
media = calcularMedia(*t)
print(media)
#  Resultado: 7.25Code language: Python (python)

Essa forma diferenciada de passar um número variável de parâmetros para uma função também pode ser usada para passar parâmetros nomeados, usando **.

Nesse caso, eles são recebidos pela função como um dicionário.

def calcularMedia(**kwargs):
    print(kwargs)
    return sum(kwargs.values()) / len(kwargs)

media = calcularMedia(nota1 = 6.5, nota2 = 6, nota3 = 5.5)
print(media)
#  Resultado:
#  {'nota1': 6.5, 'nota2': 6, 'nota3': 5.5}
#  6.0Code language: Python (python)

Da mesma maneira, também é possível desempacotar um dicionário como parâmetros nomeados na chamada de uma função:

def dim(x, y):
    return x - y

dic = { 'x' : 5, 'y' : 4}
print(dim(**dic))
#  Resultado: 1Code language: Python (python)

Você pode usar qualquer nome para o parâmetro de empacotamento de tupla e de dicionário, mas os valores `*args e `**kwargs são padrões usados pelos programadores Python. Usar esses padrões torna o seu código mais legível, já que muita gente sabe de imediato do que se trata.

Passagem de parâmetros por valor ou por referência

Esse é um assunto tão extenso que daria para escrever um post inteiro só sobre ele.

Por isso, eu vou apresentar aqui apenas um resumo, para que você entenda como a passagem de parâmetros funciona em Python.

Nas linguagens de programação, a passagem de parâmetros ocorre por valor ou por referência. Algumas linguagens apresentam outras formas, mas todas acabam sendo uma combinação desses dois tipos.

Se a passagem é por valor, uma cópia da variável original é passada para a função. Mesmo que o valor do parâmetro seja alterado dentro da função, o valor original (fora da função) não foi alterado quando a função termina de executar.

Se a passagem é por referência, uma referência ao endereço de memória da variável original é passada para a função. Assim, qualquer alteração no valor do parâmetro dentro da função se torna permanente na variável original, mesmo depois que a função termina.

Em Python, a passagem de parâmetros é feita por valor, mas… em Python, tudo é um objeto.

Por isso, o valor passado para a função não é o valor do objeto, mas uma cópia da referência ao objeto, que fica no call stack (pilha de execução).

Você não precisa entender como esse gerenciamento de memória funciona nesse momento. Só precisa saber que, caso você tente alterar o valor do objeto dentro da função, uma nova referência é criada dentro da função e a ligação com o objeto original é perdida.

Veja esse exemplo:

def mudar(numero, lista):
    numero = 5
    lista = ['nova']
    print('1 - ', numero)
    print('2 - ', lista)

n = 1
l = ['original']
mudar(n, l)
print('3 - ', n)
print('4 - ', l)Code language: Python (python)

A função mudar() recebe uma referência para os objetos n e l nos parâmetros numero e lista. Em seguida, a função muda os valores desses parâmetros.

No entanto, a ligação com os objetos originais é perdida nesse momento e os valores originais são preservados. Isso é percebido quando a função termina de executar.

O resultado é o seguinte:

1 -  5
2 -  ['nova']
3 -  1
4 -  ['original']Code language: plaintext (plaintext)

Parece complicado, mas é fácil de entender se você considerar o seguinte:

Dentro de uma função, o objeto original nunca pode receber uma nova atribuição, ou seja, ele não pode ser substituído.

Qualquer tentativa de modificar o valor de um objeto passado como parâmetro dentro de uma função resulta na criação de um novo objeto.

Então nunca é possível modificar o conteúdo de um objeto passado para uma função?

É sim, mas sem substituir o objeto original.

Isso significa que, se o objeto for imutável, como uma string, um inteiro ou uma tupla, o valor original do objeto antes da chamada não pode ser alterado dentro da função.

Mas se o objeto for mutável, como uma lista ou um dicionário, é possível modificar os itens dentro do objeto na função. O objeto continuará sendo o mesmo, mas com o estado alterado após a execução da função.

Veja como, nesse exemplo:

def acrescentar(numero, lista):
    numero += 5
    lista.append('novo item')

n = 1
l = ['original']
acrescentar(n, l)
print(n)
print(l)Code language: Python (python)

A função acrescentar() soma 5 ao valor do número passado. Nesse momento, ao tentar alterar o valor de um objeto imutável (int), um novo objeto é criado, com o valor 6. Esse objeto é perdido quando a função termina.

Já no caso da lista, não há uma nova atribuição para a variável, mas sim uma alteração da lista original, usando a função append() para acrescentar um elemento.

Essa modificação é possível e se torna permanente, mesmo depois que a função termina.

O resultado desse programa é o seguinte:

1
['original', 'novo item']Code language: plaintext (plaintext)

A alteração da lista dentro da função, vista nesse último exemplo, é conhecida na programação como Efeito Colateral.

Isso quer dizer que a função não realizou só uma tarefa computacional e retornou um valor. Ela deixou o sistema em um estado diferente do que ele estava antes da função ser executada.

Na próxima seção você vai ver os detalhes em torno desse conceito.

Qual é a diferença entre função e procedimento?

Um procedimento (ou sub-rotina, como também é chamado) tem um nome, recebe parâmetros e executa um conjunto de instruções, de maneira semelhante a uma função. No entanto, a função retorna um resultado e o procedimento não.

Para entender como a linguagem Python trata os procedimentos, veja a seguir os diferentes tipos de retorno que uma função pode ter.

Em geral, uma função pode ser classificada em três tipos, quanto ao seu retorno.

O primeiro tipo é conhecido como função “pura” e forma a base do paradigma de programação funcional.

Esse tipo de função só computa um valor e retorna um resultado, ou seja, mapeia elementos de um conjunto de entradas para elementos de um conjunto de saídas, da mesma maneira que as funções matemáticas.

A principal característica de uma função pura é que ela não produz efeitos colaterais, ou seja, o sistema não fica em um estado diferente quando ela termina de executar.

Exemplos desse tipo de função em Python:

  • math.sqrt(x): Retorna a raiz quadrada de um número x quando x >= 0, ou um ValueError quando x < 0. Exemplo: math.sqrt(16) = 4.
  • pow(x, y, z): Retorna o valor do número x elevado ao expoente y. Se o parâmetro opcional z estiver presente, retorna o valor de x elevado a y, módulo z. Por exemplo: pow(4, 2, 3) = (4 * 4) % 3 = 16 % 3 = 1

Uma maneira fácil de identificar uma função pura é considerar se faz sentido chamar essa função sem usar o seu resultado. Você calcularia a raiz quadrada de um número e jogaria o resultado fora? Com certeza, não.

O segundo tipo é a função que processa os parâmetros de entrada, mas só retorna um valor para indicar se o processamento funcionou ou não. Em geral, o retorno é True ou False.

Por exemplo, você pode definir uma função que apaga um arquivo e só retorna uma indicação se o arquivo foi apagado.

Você vai ver mais adiante que não é comum usar esse tipo de função em Python. Essa abordagem se popularizou entre as linguagens de mais baixo nível, como C.

O terceiro tipo é o da função que executa um processamento e não retorna um valor explícito. Quando você usa uma função dessas, só está interessado no efeito que resulta do processamento, não no retorno da função.

Um exemplo simples é a função os.chdir(). Ela muda o diretório corrente no sistema operacional e sempre retorna None. O que importa é que o diretório mude, não que a função informe o que fez, entendeu?

Na linguagem Python você vai encontrar funções do primeiro e do terceiro tipo.

Como Python é uma linguagem de muito alto nível, não faz sentido ficar testando se cada operação deu certo ou não.

Se o seu programa não usa o resultado da função, como no caso do os.chdir(), é mais simples chamar a função e assumir que ela funcionou.

Mas e se não funcionar?

Bem, aí ela vai lançar uma exceção. Um erro, que o seu programa vai tratar.

É simples assim: ou funciona como deve, ou dá erro.

Os defensores dessa forma de programar usam como argumento que “é mais fácil pedir perdão do que permissão”, citando Grace Hopper, programadora que desenvolveu o primeiro compilador para uma linguagem de programação, no início dos anos 1950.

Então, de acordo com a definição lá no início dessa seção, todas essas funções do terceiro tipo em Python, que não retornam alguma coisa, são procedimentos?

Não! A linguagem Python só tem funções.

Então, como é possível?

Como você viu, o resultado do processamento de uma função pode modificar os valores dos objetos passados como parâmetros, sem precisar retornar um novo objeto. Esse é o chamado efeito colateral.

Ainda assim, toda função em Python retorna alguma coisa. Caso não tenha a palavra reservada return, a função retorna None de forma implícita.

Você pode confirmar isso imprimindo o resultado de uma função.

def fazNada():
    pass

print(fazNada())
#  Imprime NoneCode language: Python (python)

A palavra reservada pass é usada apenas para criar um bloco de código que não faz nada, ou seja, só “passa” o controle do programa adiante.

Portanto, as funções em Python que não têm um valor de retorno explícito não podem ser consideradas procedimentos.

O que é uma função Lambda em Python?

Uma função lambda é uma função anônima que recebe parâmetros como uma função comum, mas só permite uma expressão e retorna o resultado da avaliação dessa expressão.

A forma da função lambda é:

lambda <parâmetros>: <expressão>

Por exemplo:

lambda x: x ** 2Code language: Python (python)

A função do exemplo acima recebe um parâmetro x e retorna o valor de x elevado ao quadrado. Ela é equivalente à definição normal de função:

def f_lambda(x):  #  lambda é palavra reservada
    return x ** 2Code language: Python (python)

Funções lambda são usadas quando é necessário ter uma função para executar uma tarefa simples em algum ponto do código.

Em geral, essas situações acontecem quando você usa funções de ordem maior, como map() e filter(), que recebem um nome de função como parâmetro e aplicam essa função a cada elemento de uma lista.

Veja isso no exemplo a seguir. A função map() passa cada item da lista para a função lambda do exemplo acima.

lista = [1, 3, 4, 6, 11]
resultado = list(map(lambda x: x ** 2 , lista))
print(resultado)
#  Resultado: [1, 9, 16, 36, 121]Code language: Python (python)

A função map() retorna um objeto do tipo iterator. Por isso, o resultado é convertido em uma lista usando a função embutida list(). Se você não sabe o que é um iterator, dá uma olhada no meu post sobre os laços de repetição for e while e você vai entender.

O resultado do exemplo acima é uma lista com todos os itens da lista original elevados ao quadrado.

Em situações tão simples quanto essa, não justifica criar uma função só para isso e a função anônima é uma boa escolha.

Uma função em Python é um objeto de primeira classe?

Sim. Funções em Python são consideradas objetos de primeira classe. Isso significa que podem ser atribuídas a variáveis, passadas como parâmetros ou retornadas por outras funções.

Veja um exemplo com a função dividirPositivo(), mostrada no início desse post.

dvp = dividirPositivo
print(dvp(1))Code language: Python (python)

Nesse caso, a função não é executada na linha 1. O que acontece é que o nome da função é passado para a variável dvp, com o tipo de dado “função definida pelo usuário”.

Dessa forma, a função dividirPositivo() recebe o apelido dvp e pode ser chamada com dvp().

Como documentar uma função em Python?

Caso a primeira linha do bloco de código da função em Python seja uma string literal, essa linha será considerada como documentação. Esse formato é conhecido como docstring.

def funcao(parametros):
    """Resumo da string de documentação (opcional)
    
    Descrição da finalidade da função.
    """
    # Bloco de código da função
    returnCode language: Python (python)

Apesar de opcional, documentar a finalidade da função também é considerada uma boa prática.

Conclusão

As funções são estruturas de separação de código básicas existentes em todas as linguagens de programação.

Nesse post você viu o que é uma função, como declarar e documentar uma função em Python e como passar parâmetros a ela de diferentes formas.

Além disso, você entendeu a diferença entre função e procedimento e como usar os efeitos colaterais causados por uma função não-pura em seu benefício.

Apesar de ser extenso e complexo, esse tema será fundamental durante toda a sua carreira, já que a definição de funções e a manipulação de parâmetros é uma das tarefas mais executadas pelo programador no dia-a-dia.

Portanto, dominar esses conceitos vai fazer de você um programador mais eficiente.

O que você achou? Comente a sua experiência aqui embaixo!

E não esqueça de dizer como eu posso melhorar esse post!

Guilherme Brügger D Amato - Audiência Pública na Comissão Senado do Futuro

Guilherme Brügger D’Amato é servidor concursado de TI na Câmara dos Deputados, onde ocupou o cargo de Diretor de Informática entre 2015 e 2016. Com mais de 26 anos de experiência como programador e executivo de TI, já desenvolveu sites e sistemas usados por dezenas de milhões de pessoas. Conecte-se com ele no LinkedIn ou no Instagram.

Deixe um comentário