loading...

Funções em Python

theakira profile image Gabriel Felippe ・8 min read

Uma função é um bloco de código organizado e reutilizável que é capaz de realizar uma determinada ação. Funções nos ajudam a deixar o nosso código mais modular, trazendo a possibilidade de reusabilidade. Como vimos ao longo do guia, Python já nos traz várias funções pré-construídas, como por exemplo print() e range(), mas também é possível criarmos nossas próprias funções!


Anatomia das Funções

Podemos imaginar as funções como um mecanismo capaz de receber valores como input e então realizar operações nesses valores e retornar um output

img


Características das Funções

  • Possuem um nome
  • Possuem parâmetros (0 ou mais)
  • Possuem docstrings (opcional, porém recomendado)
  • Possuem um corpo
  • Retornam algo

Definindo uma Função

Para definir uma função é necessário que sigamos algumas regras

  • Bloco de funções começam com a palavra-chave def seguido do nome da função e parenteses ().
  • Parâmetros de input ou argumentos (dados fornecidos por nós) devem ser colocados entre parenteses ()
  • A palavra-chave return [expressão] sai da função, opcionalmente passando um parâmetro para o chamador. O comando de retorno sem argumentos é o mesmo que return None
  • Opcionalmente podemos usar strings de documentação (docstrings) para descrever o que nossa função faz

Por exemplo:

def oi(nome):
    """
    Essa função cumprimentará a pessoa passada por parâmetro
    """
    print("Olá {0} Seja bem-vindo".format(nome))

A função foi criada e está definida, porém nada aconteceu. Para que ela possa funcionar precisamos invocá-la, para isso chamaremos ela por seu nome e passaremos os parâmetros requisitados por ela

oi("Gabriel") # Olá Gabriel Seja bem-vindo

Parâmetros padrão

Podemos definir parâmetros padrão para caso a função seja invocada sem nenhum parâmetro, estes preencherão as opções

def minha_func(valor=100):
    print("O valor definido foi: " + str(valor))

minha_func()
minha_func(10)
minha_func(33)

Docstring

A primeira string depois do cabeçalho da função é chamada de docstring, é uma string usada para especificar a funcionalidade da nossa função, e embora seja opcional, vos lembro que documentar é uma importante prática de programação, uma vez que possivelmente outras pessoas estarão lendo nosso código, inclusive é importante até mesmo para você lembrar o que você fez! Caso queiramos ver o docstring de uma função

print(oi.__doc__)

O comando return

O comando return nos permite fazer com que a função retorne um valor e possamos armazená-lo em uma variável, por exemplo:

def func(x):
    return x + 1 

y = func(1)
z = func(2)
print(y) # 2
print(z) # 3

Escopo da Variável

  • Parâmetro formal se conecta com o valor do parâmetro real quando a função é chamada
  • Um novo escopo/quadro/ambiente é criado quando entra uma função
  • Escopo é o mapeamento de nomes para objetos
# Definição da Função
def f(x): # parâmetro formal
    x += 1
    print('Em f(x): x = ',x)
    return x 

# Código principal do Programa
# -> Inicializa a variável x
# -> faz uma chamada de função f(x)
# -> atribui o retorno da função para a variável z
x = 10
z = f(x) # parâmetro real
print(z)

Funções como Argumentos

  • Argumentos podem assumir qualquer tipo, até mesmo funções
def func_a():
    print('dentro da func_a')
def func_b(y):
    print('dentro da func_b')
    return y
def func_c(z):
    print('dentro da func_c')
    return z()
print(func_a()) # chama a função func_a, não recebe parâmetros
print(5+func_b(2)) # chama a função func_b, recebe um parâmetro
print(func_c(func_a)) # chama a função func_c, recebe outra função como parâmetro
  • A função func_a retorna None
  • A função func_b retorna 2
  • A função func_c recebe a func_a como parâmetro, que por sua vez retorna None

Exemplo do Escopo

  • Dentro de uma função, podemos acessar uma variável definida fora dela
  • Dentro de uma função, não podemos modificar uma variável definida fora dela, na verdade podemos utilizando a variável global, mas não é o ideal
def f(y):
    # x é redefinido no escopo de f
    x = 1
    x += 1
    print(x)

x = 5
f(x)
print(x)
  • Nesse caso temos diferentes objetos x
def g(y):
    print(x)
    print(x+1)

x = 5
g(x)
print(x)
  • x dentro de x é capturado do mesmo escopo que chamou a função g
def h(y):
    x += 1

x = 5 
h(x)
print(x)

*args & **kwargs

As variáveis mágicas *args e **kwargs são normalmente usadas em definições de funções, elas nos permitem passar um número variável de argumentos para uma função. Variável nesse caso significa que não sabemos de antemão quantos argumentos vamos receber, então nesse caso utilizamos *args e **kwargs.

*args

*args é usado para enviar uma variável que não tenha palavras-chave, veja um exemplo:

def soma(*args):
    total = 0
    for num in args:
        total += num 
    return total

print(soma(2,3,4,6)) # 15
print(soma(2,3,4,6,5,5,5,1,2)) # 32
print(soma(2,3)) # 5

**kwargs

**kwargs nos permite passarmos variáveis com palavras-chave como argumento, veja exemplos:

def pessoa(**kwargs):
    print(kwargs)
    for nome, idade in kwargs.items():
        print(f'{nome} tem atualmente {idade} anos de idade')

pessoa(gabriel='33', rafael='47', daniel='22')

# {'gabriel': '33', 'rafael': '47', 'daniel': '22'}
# gabriel tem atualmente 33 anos de idade
# rafael tem atualmente 47 anos de idade
# daniel tem atualmente 22 anos de idade

Recursão

'Recursividade' é um termo usado de maneira mais geral para descrever o processo de repetição de um objeto de um jeito similar ao que já fora mostrado. Um bom exemplo disso são as imagens repetidas que aparecem quando dois espelhos são apontados um para o outro.

Outro grande exemplo seria O Triângulo de Sierpinski - também chamado de Junta de Sierpinski - é uma figura geométrica obtida através de um processo recursivo. Ele é uma das formas elementares da geometria fractal por apresentar algumas propriedades, tais como: ter tantos pontos como o do conjunto dos números reais; ter área igual a zero; ser auto-semelhante (uma parte sua é idêntica ao todo); não perder a sua definição inicial à medida que é ampliado. Foi primeiramente descrito em 1915 por Waclaw Sierpinski (1882 - 1969), matemático polonês.

Imagem do Triângulo de Sierpinski:
alt text

Pensando de forma computacional

  • Algoritmicamente falando, Recursão Uma maneira de desenvolver soluções para problemas através de divide-and-conquer ou decrease-and-conquer

    • Reduz o problema para versões simplificadas do mesmo problema
  • Semanticamente: Uma técnica de programação onde a função chama a si mesmo

    • Em Programação, o objetivo é não ter recursão infinita
      • Deve ter 1 ou mais casos bases que são fáceis de resolver
      • Deve resolver o mesmo problema em algum outro input com o objetivo de simplificar o input do problema maior

Recursão em Python

Nós sabemos que em Python é possível uma função chamar outras funções, inclusive é possível que uma função chame ela mesma, esses tipos de constructos são chamados de funções recursivas.

A seguir temos o exemplo de uma função que descobre o fatorial de um inteiro. Fatorial de um número é produto de todos inteiros de 1 até esse número. Por exemplo, o fatorial de 5 (denotado por 5!) é 1x2x3x4x5 = 120

Exemplo de uma função recursiva:

def fatorial(x):
    """
    Essa é uma função recursiva
    Ela calcula o fatorial de um número inteiro
    """

    if x == 1:
        return 1
    else:
        return (x * fatorial(x - 1))

y = 4
z = 10
print("O fatorial de {0} é {1}".format(y,fatorial(y))) # O fatorial de 4 é 24
print("O fatorial de {0} é {1}".format(z,fatorial(z))) # O fatorial de 7 é 3628800

No exemplo acima fatorial() é considerada uma função recursiva, pois chama a ela mesma. Quando nós chamamos essa função com um inteiro positivo, ela chamará recursivamente ela mesma diminuindo o número. Para entendermos melhor, veja

fatorial(4)              # Primeira chamada com 4
4 * fatorial(3)          # Segunda chamada com 3
4 * 3 * fatorial(2)      # Terceira chamada com 2
4 * 3 * 2 * fatorial(1)  # Quarta chamada com 1
4 * 3 * 2 * 1            # Retorno da quarta chamada, uma vez que y = 1
4 * 3 * 2                # Retorno da terceira chamada
4 * 6                    # Retorno da segunda chamada
24                       # Retorno da primeira chamada

Como podem ver, nossa recursão acaba quando o valor de y reduz a 1, essa é considerada a condição base. Toda recursão deve ter uma condição base que para a recursão ou a função ficará chamando-a eternamente.


Recursão com Múltiplos Casos Base

  • Números Fibonacci
  • Leonardo de Pisa (também conhecido como Fibonacci) modelou o seguinte desafio:
    • Pares de coelhos recém-nascidos (um macho e uma fêmea) são colocados em um curral
    • Coelhos se acasalam na idade de um mês
    • Coelhos tem um mês de período de gestação
    • Assumindo que o coelho nunca morre, a fêmea sempre produz um novo par (um macho e uma fêmea) a cada mês a partir do segundo mês
    • Quantos coelhos fêmeas estão lá no final do ano?

Fibonacci

  1. Depois de um mês (chama ele de 0) - 1 fêmea
  2. Depois do segundo mês, ainda 1 fêmea (agora grávida)
  3. Depois do terceiro, duas fêmeas, uma grávida, uma não
  4. Em geral, fêmeas(n) = fêmeas(n-1) + fêmeas(n-2)
    • Cada fêmea viva no mês n-2 irá produzir uma fêmea no mês n
    • Estes podem ser adicionados aqueles vivos no mês n-1 para obter total vivos no mês n
  • Casos Base:
    • Femeas(0) = 1
    • Femeas(1) = 1
  • Caso Recursivo
    • Femeas(n) = Femeas(n-1) + Femeas(n-2)
def fib(x):
    """
    Assume x como inteiro >= 0
    Retorna o Fibonacii de x
    """
    if x == 0 or x == 1:
        return 1
    else:
        return fib(x-1) + fib(x-2)

Vantagens e Desvantagens

Vantagens da Recursão:

  1. Funções recursivas tornam o código limpo e elegante
  2. Uma tarefa complexa pode ser quebrada em sub-problemas usando a recursão
  3. Gerar sequências é mais fácil com recursão

Desvantagens da Recursão:

  1. As vezes a lógica por trás dela pode ser complexa de entender
  2. Chamadas recursivas são custosas e ineficientes e podem nos custar muita memória e tempo!
  3. Funções recursivas são mais difíceis de debugar (processo de encontrar e reduzir defeitos de um programa).

Por fim, entendemos que as funções são um tema essencial e fundamental para a programação, elas são capazes de facilitar muito a vida dos programadores através da abstração, tornando os códigos muito mais bonitos, modulares e fáceis de entender. O código ainda pode ser utilizados muitas vezes e precisa ser debuggado/testado apenas uma vez.

Cheque a documentação oficial do Python para conhecer todas as funções construídas no interpretador Python.

Posted on by:

theakira profile

Gabriel Felippe

@theakira

Computer Scientist; Learning and Sharing Knowledge;

Discussion

markdown guide