Funções#

As funções são usadas para organizar o fluxo do programa, especialmente para nos permitir realizar facilmente tarefas comumente necessárias repetidamente.

Já usamos muitas funções, como aquelas que trabalham com listas (append() e pop()) ou strings (como replace()). Aqui, veremos como escrever nossas próprias funções.

Funções definem blocos de código que podem ser reutilizados sempre que necessário. Assim como as funções matemáticas, as funções de Python recebem valores de entrada (chamados de parâmetros ou argumentos) e retornam valores de saída, no entanto não é obrigatório que elas recebam entradas nem que retornem saídas.

A criação de uma função começa com a palavra def, seguida pelo nome da função (neste caso, f) e os parâmetros entre parênteses, concluindo com os dois pontos (:).

Vale ressaltar que, como a função define um bloco, as linhas subsequentes precisam ser corretamente indentadas.

Aqui está um exemplo simples de uma função que recebe um único argumento, i:

def minha_func(i):
    print(f"na função temos que i = {i}")
    
minha_func(10)
minha_func(5)
na função temos que i = 10
na função temos que i = 5

As funções sempre retornam um valor — se um valor não for explicitamente fornecido, então elas retornam None

a = minha_func(10)
na função temos que i = 10
print(a)
None

Para retornar um valor ao chamarmos um função podemos usar return. Como exemplo, vamos definir a função \(f(x) = x^2\) em Python.

def f(x):
    return x**2

f(3)
9

Portanto, quando uma função não possui um comando return, ou possui o comando sem associá-lo a um valor, a função retorna None (nulo).

A definição de uma função gera uma variável com o nome da função, e o tipo dessa variável é função.

type(f)
function

Isso significa que é possível atribuir a função a outra variável, o que pode ser utilizado como uma forma de renomeação.

xaoquadrado = f

xaoquadrado(4)
16

A função não está restrita a apenas a um argumento, podemos utilizar mais do que um. Aqui está uma função simples que recebe dois números e retorna seu produto.

def multiplicação(a, b):
    return a*b

c = multiplicação(3, 4)
c
12

O «uso» de uma função após a sua definição é conhecido como «chamada» e requer que valores sejam fornecidos para todos os parâmetros. Esses valores podem ser passados de maneira posicional (segundo a ordem de declaração dos parâmetros) ou pelo nome dos parâmetros, dispensando a necessidade de seguir a ordem estabelecida.

def power(x, y):
    return x ** y

power(3, 2), power(y=2, x=3)
(9, 9)

É possível construir funções que retornam múltiplos valores em Python.

def medêambos(a, b):
    return a+10, b+10

medêambos(1,2)
(11, 12)

Como podemos observar, os resultados da função que retorna dois valores foram impressos entre parênteses, ou seja, na forma de uma tupla.

Isso significa que Python simula o retorno de múltiplos valores ao retornar um único valor do tipo tupla.

Essa técnica permite que o retorno seja desconstruído em várias atribuições durante a chamada.

Vamos considerar então uma função que recebe dois inteiros e retorna o quociente e o resto da sua divisão

def div(dividend, divisor):
    return dividend // divisor, dividend % divisor

div(7, 2)
(3, 1)

Podemos então pegar cada termo da seguinte forma

dividend, divisor = 7, 2
(quocient, remainder) = div(dividend, divisor)

quocient * divisor + remainder
7

Exercício Rapído

Escreva uma função simples que recebe uma frase (como uma string) e retorna um inteiro igual ao comprimento da palavra mais longa na frase. A função len() e o método .split() serão úteis aqui.

Definindo valores padrão para parâmetros#

É possível definir funções com valores padrão para seus parâmetros. Isso facilita a criação de funções que podem ser chamadas sem a necessidade de passar todos os argumentos. Veja um exemplo:

def power(x=3, y=2):
    return x ** y

print(power())
print(power(x=4)) 
print(power(y=3)) 
print(power(x=10,y=0))
print(power(x=0,y=10)) 
9
16
27
1
0

É comum que funções sejam definidas com parâmetros que possuam valores padrão (default) e parâmetros obrigatórios. Quando isso ocorre, é necessário declarar os parâmetros sem valor padrão primeiro.

Como vimos anteriormente, é possível passar valores para parâmetros com valores padrão pelo nome, o que permite que a ordem de declaração dos parâmetros seja ignorada.

No entanto, caso um parâmetro seja passado sem especificar seu nome, a ordem de declaração deve ser respeitada.

def power(x, y=2):
    return x ** y

power(3), power(y=2, x=3), power(3, y=2), power(3, 2)
(9, 9, 9, 9)

Exercício

Execute esses dois blocos de código e entenda/explique a diferença… Isso leva a um dos erros mais comuns para iniciantes em Python

Isto:

    def f(a, L=[]):
        L.append(a)
        return L

    print(f(1))
    print(f(2))
    print(f(3))
    
e isto:
 ````python    
    def fnew(a, L=None):
        if L is None:
            L = []
        L.append(a)
        return L

    print(fnew(1))
    print(fnew(2))
    print(fnew(3))   

Passando funções como argumentos#

Como mencionado anteriormente, quando uma função é definida, ela cria uma variável com o mesmo nome, cujo valor é do tipo função. Isso significa que funções também podem ser passadas como parâmetros para outras funções. O exemplo abaixo mostra como isso pode ser feito. A função aplicar_operacao recebe um valor x e uma função oper, que define qual operação será realizada com o valor de x:

# Definindo algumas funções
def quadrado(x):
    return x ** 2

def aplicar_operacao(x, oper):
    return 3 * oper(x)

def cubo(x):
    return x ** 3

# Usando a função 'aplicar_operacao' com diferentes operações
resultado1 = aplicar_operacao(4, quadrado)
resultado2 = aplicar_operacao(3, cubo)

print(resultado1)
print(resultado2)
48
81

Operador Lambda#

Lambdas são funções pequenas «descartáveis» (que ocupam apenas uma linha). Essas são pequenas funções sem nome que são frequentemente usadas como argumentos em outras funções. Os seguintes exemplos são equivalentes:

def aoquadrado(x):
    return x**2

aoquadrado = lambda x : x**2

Por exemplo: temos uma lista de tuplas e queremos ordenar a lista com base no segundo item da tupla. O método sort pode receber um argumento opcional key que nos diz como interpretar o item da lista para a ordenação.

pairs = [(1, 'um'), (2, 'dois'), (3, 'três'), (4, 'quatro')]
pairs.sort()
pairs
[(1, 'um'), (2, 'dois'), (3, 'três'), (4, 'quatro')]
pairs.sort(key = lambda p: p[1])
pairs
[(2, 'dois'), (4, 'quatro'), (3, 'três'), (1, 'um')]

Aqui usamos uma lambda em um extrato de uma lista (com a função filter).

list(filter(lambda x:x == 1, [1,2,3]))
[1]
squares = [x**2 for x in range(100)]
sq = list(filter(lambda x : x%2 == 0 and x%3 == 0, squares))
sq
[0,
 36,
 144,
 324,
 576,
 900,
 1296,
 1764,
 2304,
 2916,
 3600,
 4356,
 5184,
 6084,
 7056,
 8100,
 9216]

Um dos casos de uso comuns do operador lambda é quando precisamos passar uma função simples como parâmetro para outra função de forma concisa. Exemplo:

def aplicar_operacao(x, oper):
    return oper(x)

aplicar_operacao(3, lambda x: x ** 4)
81

Funções essenciais#

Python oferece uma série defunções integradas, veja documentação (https://docs.python.org/3.7/library/functions.html), que são muito práticas no dia a dia de programação. Algumas operações úteis são:

Função

Descrição

list()

Converte um iterável para uma lista.

set()

Converte um iterável para um conjunto (removendo duplicatas).

tuple()

Converte um iterável para uma tupla.

dict()

Converte um iterável (geralmente de pares chave-valor) para um dicionário.

type()

Retorna o tipo de um objeto.

sum()

Calcula a soma dos itens de uma coleção.

min()

Retorna o menor valor de uma coleção.

max()

Retorna o maior valor de uma coleção.

pow(x, y)

Eleva o valor de x à potência y.

round()

Aproxima o valor passado para o número inteiro mais próximo.

all()

Retorna True se todos os itens da coleção forem avaliados como True.

any()

Retorna True se ao menos um item da coleção for avaliado como True, caso contrário, retorna False.

Existem ainda outras funções frequentemente usadas, como as de entrada e saída de dados, além das funções zip e enumerate.

Função zip#

A função zip combina os elementos de duas ou mais coleções em tuplas, de modo que a i-ésima tupla contém os elementos de índice i de cada coleção fornecida.

valor = [1, 2, 3]
letras = ['a', 'b', 'c']
resultado = zip(valor, letras)

# Convertendo o objeto zip para uma lista para visualização
print(list(resultado))
[(1, 'a'), (2, 'b'), (3, 'c')]
for x, y in zip(valor, letras):
    print('Valor: {0}, Letra: {1}'.format(x, y))
Valor: 1, Letra: a
Valor: 2, Letra: b
Valor: 3, Letra: c

Função enumerate#

Permite iterar pelos elementos de uma coleção e pelos seus índices simultaneamente. Exemplo:

for i, le in enumerate(letras):
    print('Índice: {0}, Letra: {1}'.format(i, le))
Índice: 0, Letra: a
Índice: 1, Letra: b
Índice: 2, Letra: c

Como a função zip gera uma sequência de tuplas, é possível usar a função enumerate para iterar sobre o resultado do zip. Vale ressaltar que é importante usar parênteses corretamente para evitar confusão entre as tuplas geradas por zip e os valores retornados por enumerate.

for i, (le, val) in enumerate(zip(letras, valores)):
    print('Índice: {0}, Letras: {1}, Valores: {2}'.format(i, le, val))
---------------------------------------------------------------------------
NameError                                 Traceback (most recent call last)
Cell In[24], line 1
----> 1 for i, (le, val) in enumerate(zip(letras, valores)):
      2     print('Índice: {0}, Letras: {1}, Valores: {2}'.format(i, le, val))

NameError: name 'valores' is not defined

Docstrings em Funções#

Em Python, uma boa prática é documentar suas funções usando docstrings, que são strings utilizadas para descrever o comportamento e a finalidade da função. As docstrings são colocadas logo após a definição da função, entre aspas triplas (""" ou '''), e podem ser acessadas a qualquer momento usando o comando help(). Elas ajudam tanto os desenvolvedores que escreveram o código quanto aqueles que forem utilizá-lo, proporcionando uma explicação clara e acessível sobre como a função deve ser usada.

Uma boa docstring normalmente inclui:

  • Uma descrição sucinta do que a função faz.

  • A descrição dos parâmetros da função, incluindo o tipo esperado e o significado.

  • O valor que a função retorna, caso haja.

  • Possíveis exceções ou erros que a função pode levantar.

Exemplo de uma função com docstring:

def soma(a, b):
    """
    Soma dois números e retorna o resultado.

    Parâmetros:
    a (int ou float): O primeiro número a ser somado.
    b (int ou float): O segundo número a ser somado.

    Retorna:
    int ou float: O resultado da soma de a e b.
    """
    return a + b

Vamos elaborar então um programa um pouco mais elaborado que retorna a sequência de Fibonacci

def fib2(n):
    """
    Retorna uma lista contendo a série de Fibonacci até o valor n e o número de elementos gerados.

    Parâmetros:
    n (int): O valor limite até o qual a sequência de Fibonacci será gerada. 
             A sequência irá até o maior número menor que n.

    Retorna:
    tuple: Um tuplo contendo:
        - Uma lista com os números da sequência de Fibonacci até n.
        - Um inteiro que representa o número de elementos na lista gerada.
    """
    result = []
    a, b = 0, 1
    while a < n:
        result.append(a)
        a, b = b, a + b
    return result, len(result)

fibb, n = fib2(250)
print(fibb, "\n", n)
[0, 1, 1, 2, 3, 5, 8, 13, 21, 34, 55, 89, 144, 233] 
 14

Observe que como função inclui uma docstring (logo após a definição da função), quando usamos função help podemos visualiza-lá.

help(fib2)
Help on function fib2 in module __main__:

fib2(n)
    Retorna uma lista contendo a série de Fibonacci até o valor n e o número de elementos gerados.
    
    Parâmetros:
    n (int): O valor limite até o qual a sequência de Fibonacci será gerada. 
             A sequência irá até o maior número menor que n.
    
    Retorna:
    tuple: Um tuplo contendo:
        - Uma lista com os números da sequência de Fibonacci até n.
        - Um inteiro que representa o número de elementos na lista gerada.

Módulos#

A linguagem Python básica é bastante pequena. A maior parte acontece em módulos. Alguns módulos fazem parte de uma biblioteca padrão que fornece funcionalidade adicional. Essas partes adicionadas estão na forma de módulos que podemos importar para nossa sessão (ou programa) Python.

O módulo math fornece funções que realizam as operações matemáticas básicas, bem como constantes (note que há um módulo separado cmath para números complexos).

Em Python, você importa um módulo. As funções são então definidas em um namespace separado (ou espaço de nomes) — esta é uma região separada que define nomes e variáveis, etc. Uma variável em um namespace pode ter o mesmo nome que uma variável em um namespace diferente, e elas não entram em conflito. Você usa o operador . para acessar um membro de um namespace.

Em geral, sempre será da forma:

módulo.namespace

Por padrão, quando você digita algo no interpretador Python ou aqui no notebook Jupyter, ou em um script, ele está em seu próprio namespace padrão, e você não precisa prefixar nenhuma das variáveis com um indicador de namespace.

import math

O módulo math fornece o valor de π (pi).

math.pi
3.141592653589793

Isso é distinto de qualquer variável pi que possamos definir aqui.

pi = 3
print(pi, math.pi)
3 3.141592653589793

Observe que pi e math.pi são distintos um do outro—eles estão em namespaces diferentes.

O módulo math fornece muitas das funções matemáticas padrão. A maioria delas é na verdade repetida no numpy, então, na prática, eu pessoalmente quase nunca uso math. Veremos o numpy logo logo, mas vamos brincar um pouco com o math.

Para as funções trigonométricas, a expectativa é que o argumento da função esteja em radianos—você pode usar math.radians() para converter de graus para radianos, por exemplo:

math.cos(math.radians(45))
0.7071067811865476

Observe que nessa declaração estamos alimentando a saída de uma função (math.radians()) em uma segunda função, math.cos()

Quando em dúvida, peça ajuda para descobrir todas as coisas que um módulo fornece:

help(math.sin)
Help on built-in function sin in module math:

sin(x, /)
    Return the sine of x (measured in radians).

Módulos da biblioteca padrão de Python#

Python já vem de fábrica com uma enorme quantidade de módulos que podem ser importados para facilitar o trabalho de desenvolvimento. Recomendamos uma visita à lista de módulos da biblioteca padrão. Alguns módulos importantes:

  • math: Funções matemáticas

  • statistics: Funções estatísticas

  • random: Geração de números aleatórios

  • datetime: Tipos e funções básicos de datas

  • os: Funções de interação com o sistema operacional

  • sqlite3: Interface para bancos de dados SQLite

  • itertools: Funções para programação funcional

  • multiprocessing: Processamento paralelo

  • urllib: Funções para manipulação de URLs e requisições