Skip to content

    Chapter 17, Basic

    Functions

    A function names a piece of logic. Writing good functions is what separates a working script from a maintainable program.

    Code for this chapter: basico/cap17_funcoes.py

    Define, call, and return

    A function is defined with def, receives parameters and returns a value with return. If there is no return, it returns None:

    basico/cap17_funcoes.pylinhas 10 a 20
    def saudacao(nome):
        """Devolve uma saudação."""
        return f"Olá, {nome}!"
    
    
    def sem_retorno():
        pass
    
    
    print(saudacao("Ana"))
    print(sem_retorno())
    
    Output
    Olá, Ana!
    None
    

    . A common confusion: print prints to the screen, return returns the value to the caller. A function that only prints cannot have its result used in another calculation.

    Parameters and arguments

    Parameter is the name in the definition. Argument is the value passed in the call. There are four ways to pass:

    • Positional: the order matters.
    • Named: nome=valor, in any order.
    • Default: the parameter has a value if not passed.
    • Only positional (/) and only named (*): you control how the function can be called.
    basico/cap17_funcoes.pylinhas 25 a 34
    def criar_usuario(nome, /, email, *, ativo=True):
        return {"nome": nome, "email": email, "ativo": ativo}
    
    
    print(criar_usuario("Ana", "ana@exemplo.com"))
    print(criar_usuario("Bia", email="bia@exemplo.com", ativo=False))
    try:
        criar_usuario(nome="Caio", email="c@exemplo.com")
    except TypeError as erro:
        print(erro)
    
    Output
    {'nome': 'Ana', 'email': 'ana@exemplo.com', 'ativo': True}
    {'nome': 'Bia', 'email': 'bia@exemplo.com', 'ativo': False}
    criar_usuario() got some positional-only arguments passed as keyword arguments: 'nome'
    

    I use only named parameters (after the *) for boolean options, because formatar(texto, True) says nothing and formatar(texto, maiusculo=True) says everything.

    *args and **kwargs

    To accept a variable number of arguments, *args collects the positional ones into a tuple and **kwargs collects the named ones into a dictionary. In the call, the same symbol does the reverse: unpacks:

    basico/cap17_funcoes.pylinhas 39 a 54
    def somar(*numeros):
        return sum(numeros)
    
    
    def descrever(**atributos):
        for chave, valor in atributos.items():
            print(f"{chave}: {valor}")
    
    
    print(somar(1, 2, 3, 4))
    descrever(nome="Ana", cidade="Recife")
    
    valores = [10, 20, 30]
    print(somar(*valores))
    dados = {"nome": "Bia", "cidade": "Natal"}
    descrever(**dados)
    
    Output
    10
    nome: Ana
    cidade: Recife
    60
    nome: Bia
    cidade: Natal
    

    Multiple return values

    A function can return a tuple, and the caller unpacks it right away:

    basico/cap17_funcoes.pylinhas 59 a 64
    def minimo_maximo(numeros):
        return min(numeros), max(numeros)
    
    
    menor, maior = minimo_maximo([4, 8, 2, 9])
    print(menor, maior)
    
    Output
    2 9
    

    Functions are objects

    In Python, a function is a value like any other: it can be stored in a variable and passed as an argument. This idea is the basis of decorators and functional programming that come in the intermediate level:

    basico/cap17_funcoes.pylinhas 69 a 74
    def aplicar(funcao, valor):
        return funcao(valor)
    
    
    print(aplicar(len, "python"))
    print(aplicar(str.upper, "python"))
    
    Output
    6
    PYTHON
    

    Documentation and type hints

    The docstring explains the contract. Type annotations (type hints) makes it readable by tools. Python does not check the annotations at runtime:

    basico/cap17_funcoes.pylinhas 79 a 85
    def soma(a: int, b: int) -> int:
        """Soma dois inteiros e devolve o resultado."""
        return a + b
    
    
    print(soma(2, 3))
    print(soma("a", "b"))
    
    Output
    5
    ab
    

    The second call works and concatenates two strings, even though the annotation says int. What checks is an external tool, like mypy, which I introduce at the intermediate level.

    Exercise 1

    Average of as many numbers as you want

    Write media(*numeros) that returns the arithmetic mean.

    Exercise 2

    Full name with option

    Write formatar_nome(nome, sobrenome, *, maiusculo=False).