Funções e Módulos

Neste, você aprendeu a fazer alguns programas simples: imprimir certos termos, separar valores em uma string, extrair trechos de uma cadeia maior, gerar listas. Ainda não são programas suficientemente complexos para resolver problemas reais, mas são um passo importante nesse sentido, pois você está aprendendo sobre os elementos que efetivamente compõem qualquer programa que você venha a desenvolver, tais como:

  1. variáveis
  2. expressões condicionais
  3. iteradores
  4. listas

Neste ponto, você aprenderá a combinar esses elementos em funções, ou seja, em conjuntos de instruções que, uma vez definidas, podem ser posteriormente invocadas. No Módulo 2, você aprendeu a usar algumas funções nativas do Python. No Módulo 3, você aprendeu a utilizar algumas funções do módulo dsd.py. Neste momento do curso, você aprenderá a desenvolver novas funções, a partir dos elementos que você já conhece e aprenderá a agrupá-las dentro de um módulo.

1. Definindo funções

Funções são conjuntos de instruções que são acionadas quando você insere, no seu programa, o comando de invocar (call) essa função. Você já tem usado funções nativas do Python, como append() ou print(). Agora você aprenderá a criar novas funções, combinando os elementos que você está aprendendo: funções, iteradores e variáveis, etc.

Você já aprendeu que não é possível explicar para o Python o significado abstrato de uma variável: o que você pode fazer é atribuir um valor a um nome, o que faz com que esse nome sirva como uma variável.

Com as funções ocorre a mesma coisa: você cria funções por meio da definição de que um certo nome corresponderá a um conjunto de instruções, por meio da utilização da função def.

def curso():
	print ('Este é o módulo final do curso Python Para Juristas')

Esse código define como a função curso() o comando de imprimir o texto acima. Se você copiar esse código para o seu Editor e executá-lo, a frase não será impressa porque o código faz uma definição de função (ou seja, liga a função curso() a uma sequência de instruções), mas não realiza um function call, pois não existe neste código nenhuma instrução no sentido de que o interpretador do Python deve executar a função definida.

Já o código abaixo tem uma definição de função e um comando para que ela seja executada, motivo pelo qual a execução deste programa fará com que a frase definida apareça no seu console.

# definição da função
def curso():
	print ('Este é o módulo final do curso Python Para Juristas')

# function call
curso()

Repare que o nome da função definida não é curso, mas curso(), porque toda função que você cria precisa ter esses parênteses. Se você tentar criar uma função sem os parênteses, a execução do programa será interrompida por um SyntaxError, já que a sintaxe do Python exige esses parênteses. Essa exigência decorre do fato de que, dentro deles, você precisa indicar as variáveis necessárias para que a função possa gerar um comando executável.

2. Funções, variáveis e parâmetros

No caso acima, os parênteses ficam vazios porque não há variáveis, uma vez que você instruiu a impressão de uma string cujo conteúdo é totalmente definido pelos termos da função.

O caso mais típico, porém, é que as funções que você define contenham comandos que utilizam certas variáveis.

# definição da função
def curso(modulo):
	print ('Este é o módulo ' + modulo + ' do curso Python Para Juristas')

# function call indicando que a variável módulo terá o valor '4'
curso('4')

Neste caso, a string a ser impressa utiliza o valor da variável modulo, que funciona como parâmetro da função. Parâmetros são os elementos que vêm entre parênteses e que você precisa fornecer toda vez que invoca a função. A combinação adequada de funções e argumentos (ou sejam, valores definidos para os parâmetros) constitui um comando executável, tal como o código acima.

Observação: ambora essa distinção não seja sempre respeitada nos textos sobre Python, há uma diferença entre parâmetro (que são as variáveis que compõem as funções definidas, e devem vir entre parênteses no momento do uso do def) e argumento (que são os valores que você define concretamente no momento em que faz o function call).

Observe que o parâmetro da função curso() é necessariamente uma string, pois usamos como parâmetro do print() uma concatenação de strings. Se você quiser deixar essa função mais robusta (ou seja, menos sujeita a gerar erros), é possível inserir dentro do print o comando str(modulo), pois isso permitirá que você insira também outros tipos de variáveis (como as numéricas) como argumento da função módulo.

# definição da função, com o parâmetro modulo
def curso(modulo):
	print ('Este é o módulo ' + str(modulo) + ' do curso Python Para Juristas')
  
#chamada da função com o argumento '4'
curso(4)

Este é um bom momento para você aprender uma nova função, que permite a introdução de novos valores pelos usuários do programa: input(). Essa função opera conferindo valor a uma variável que você definiu, como na linha 2 do código abaixo:

# define que uma variável textoinformado será preenchida pelo usuário, como input
textoinformado = input('Insira o número do módulo:  ')

# definição da função, com o parâmetro modulo
def curso(modulo):
    print('Este é o módulo ' + str(modulo) + ' do curso Python Para Juristas')
  
#chamada da função com o argumento o valor da variável modulo
curso(textoinformado)

Observe que a sintaxe do input funciona assim: você define o input() como valor de uma variável, e como argumento do próprio input() você coloca a frase que quer que apareça no console. É no console que os programas são efetivamente executados e e lá que você tem de dar o input (no editor, você edita algoritmos, mas não executa nada).

3. Função com múltiplos parâmetros

Você pode ter uma função com vários parâmetros, dependendo da complexidade das instruções a serem executadas. Nos módulos anteriores, trabalhamos a estratégia de extrair um texto de uma string, a partir de um marcador de início e um marcador de fim. Por se tratar de uma instrução que é repetida exaustivamente em nossos programas, é muito útil criar uma função para que você possa repetir essa lógica com um comando apenas.

Que parâmetros precisamos definir para essa função de extração? Se você refletir com cuidado, deve concluir que são 3:

  1. string na qual se deve buscar o texto a a extrair
  2. marcador de início
  3. marcador de fim.

Com esses três parâmetros, você pode definir uma função como:

# define função extrair
def extrair(string, marcador_de_inicio, marcador_de_fim):
    inicio = string.find(marcador_de_inicio)
    fim = string.find(marcador_de_fim, inicio)
    texto = string[inicio + len(marcador_de_inicio):fim]
    print (texto)
    
# atribui valor à variável informacoes
informacoes = 'ADI2343242, Relator: Rosa Weber, Requerente: Indefinido, Requerido: não consta'
        
# function call
extrair(informacoes, 'Relator:',',')

A lógica dessas instruções já foi definida no módulo anterior. Neste momento, trata-se apenas de gerar uma função com uma combinação de instruções que você já conhecia.

Veja que todos os parâmetros que você definir como necessários devem ser utilizados dentro das instruções que compõem a função. Se você não fizer isso (exigindo elementos que não são efetivamente referidos dentro do código), o Python vai te avisar que há um parâmetro definido, mas não usado.

4. Isolamento do processamento das funções

Se você tiver a curiosidade de observar o seu Variable Explorer, notará uma coisa curiosa: não aparecem neles os parâmetros da sua função. Isso ocorre porque todo o processamento das funções ocorre de uma maneira independente, que não interfere com o conteúdo das variáveis do seu programa.

Esse isolamento é útil especialmente porque pode acontecer de a função usar certas variáveis que têm o mesmo nome de variáveis que você usa em seu programa, o que acarretaria uma reação em cadeia indesejada. Imagine, por exemplo, que reescrevêssemos o código do item 1.3, acima, chamando de modulo a variável que nele constava como textoinformado:

# define que uma variável textoinformado será preenchida pelo usuário, como input
modulo = input('Insira o número do módulo:  ')

# definição da função, com o parâmetro modulo
def curso(modulo):
    print('Este é o módulo ' + str(modulo) + ' do curso Python Para Juristas')
    modulo = 25
  
#chamada da função com o argumento o valor da variável modulo
curso(modulo)

Se você executar esse código e inserir como input 4, o resultado da function call utilizará o argumento 4, e não o valor 25, que foi definido para a variável módulo dentro da função.

A vantagem do isolamento é justamente essa: quando você invoca funções feitas por outras pessoas, você não sabe quais sãos as variáveis utilizadas dentro delas, mas isso não é um problema porque, mesmo que usem variáveis com nomes bastante comuns (a, x, nome, number, n, etc.), você não precisa se preocupar com o modo como esses valores vão interagir com o seu código.

Por outro lado, você deve sempre ter em mente que não adianta criar variáveis dentro de um código e  depois tentar utilizar esses valores no seu programa. Não adianta pedir um input dentro da função e depois tentar utilizar esse valor no seu código. Ocorre, porém, que muitas vezes é exatamente isso o que você quer: definir funções que gerem alguns valores específicos. O nosso modo de romper esse isolamento é introduzir dentro da sua função o elemento return().

5. Return()

O return() permite que valores gerados dentro de uma função sejam atribuídos a variáveis do seu programa. Isso é mais fácil de ser compreendido por um exemplo, modificando um pouco a nossa função de extração, trocando o print (texto) por return(texto).

Isso aprimora a nossa função porque, de fato, não queremos imprimir um texto na tela, mas queremos extrair as informações relevantes e atribuí-las a variáveis que possam ser trabalhadas: arquivadas em listas, processadas por outras funções, gravadas em arquivos, etc.

No código abaixo, tomamos a função extrair e acrescentamos um return(), de forma que podemos atribuir o resultado da função a certas variáveis, que depois inserimos em uma lista. Esse código vai ficando maior e mais complexo, mas já temos um programa capaz de resolver um problema prático.

# define função extrair
def extrair(string, marcador_de_inicio, marcador_de_fim):
    inicio = string.find(marcador_de_inicio)
    fim = string.find(marcador_de_fim, inicio)
    texto = string[inicio + len(marcador_de_inicio):fim]
    return (texto)
    
# atribui valor à variável informacoes
informacoes = 'Processo: ADI5000, Relator: Rosa Weber, Requerente: Governador de SP, Resultado: Prejudicado'
        
# cria uma lista para arquivar os dados extraídos
lista_de_dados = []

# atribui valor às variáveis escolhidas
processo = extrair(informacoes, 'Processo:', ',')
relator = extrair(informacoes, 'Relator:', ',')
requerente = extrair(informacoes, 'Requerente: ', ',')

# insere os dados na lista
lista_de_dados.append(processo)
lista_de_dados.append(relator)
lista_de_dados.append(requerente)

print (lista_de_dados)

Extrair esses dados da lista acima é fácil, pois é uma lista de dados previamente organizados, limpos e numa ordem clara. Porém, um programa com essa mesma estrutura (adaptando apenas os marcadores de início e de fim) é capaz de extrair os dados das informações processuais reais, fornecidas pelo STF, na forma longas strings. A habilidade de fazer essa extração real será trabalhada no início dos módulos de Data Mining Judicial, para a qual este curso introdutório ao Python prepara vocês.

6. Criando módulos

Uma vez que você cria uma função interessante, é bem provável que você não queira usá-la somente no programa que você está fazendo, mas também em outros.

Uma boa função de extração será usada, por exemplo, será provavelmente usada em vários programas e, por esse motivo, você pode gravá-la em um módulo de funções, que poderão ser carregadas em outros programas. Como fazer isso?

Primeiro, é preciso ter uma função a ser gravada. Tomemos, por exemplo, a função de extração desenvolvida acima. Crie um novo arquivo no Spyder e cole nele a função abaixo.

# define função extrair
def extrair(string, marcador_de_inicio, marcador_de_fim):
    inicio = string.find(marcador_de_inicio)
    fim = string.find(marcador_de_fim, inicio)
    texto = string[inicio + len(marcador_de_inicio):fim]
    return (texto)

Salve o arquivo, nomenando-o como modulo.py. Está criado o seu primeiro módulo, composto por uma função.

Observe que o Spyder deve ter gravado o modulo.py no seu diretório de trabalho (working directory), que é a pasta que você define previamente como local de gravação padrão dos seus arquivos. Você pode fazer isso no menu Tools: Preferences, que pode ser acessado pelo símbolo da chave de fenda nos botões do seu menu.

No exemplo acima, está definido o C:\Python como diretório de trabalho, mas você pode escolher qualquer pasta no seu computador.

Importante! Vários problemas nesse início do uso do Spyder e do Python decorrem do fato de não existir um diretório de trabalho definido, fazendo com que os arquivos terminem sendo gravados em locais inadequados, como as pastas de Downloads ou de Documentos.

Para evitar isso, defina desde logo um diretório para funcionar como o seu working directory. Minha preferência é por deixá-lo diretamente na raiz do disco C:, o que faz com que ele seja facilmente localizável. Minha sugestão é que você crie, na raiz do drive C, um diretório chamado C:\Spyder e grave nele todos os seus programas e os módulos que você criar ou baixar.

Facilita a sua vida, especialmente nesse início, que todos os programas fiquem nesse diretório porque você poderá importar as funções dos módulos gravados nele com um comando muito simples:

import modulo

Uma vez importada a função, você pode usá-la no seu programa, sem ter de repetir os seus termos. Porém, todas as funções do módulo extrair deverão ser referidas dentro do programa como modulo.nomedafunção().

Usando esse módulo, nosso programa anterior poderia ser reescrito como:

# importa modulo
import modulo
    
# atribui valor à variável informacoes
informacoes = 'Processo: ADI5000, Relator: Rosa Weber, Requerente: Governador de SP, Resultado: Prejudicado'
        
# cria uma lista para arquivar os dados extraídos
lista_de_dados = []

# function call: funcão + argumentos
processo = modulo.extrair(informacoes, 'Processo:', ',')
relator = modulo.extrair(informacoes, 'Relator:', ',')
requerente = modulo.extrair(informacoes, 'Requerente: ', ',')

# insere os dados na lista
lista_de_dados.append(processo)
lista_de_dados.append(relator)
lista_de_dados.append(requerente)

print (lista_de_dados)

Você logo instalará o módulo dsd.py, desenvolvido especialmente para este curso, e que contém várias funções definidas especialmente para nossos programas de data mining (extratores de dados e geradores de bancos de dados):

  • função de extração de dados
  • função de gravação
  • função de abrir arquivos
  • função de gerar tabelas

Para utilizar o dsd.py, basta baixar a sua última versão no GitHub, uma plataforma que hospeda programas que usam a ferramenta Git como sistema de controle de versão. O controle de versão é uma funcionalidade importante no desenvolvimento de software, pois ele permite a introdução controlada de inovações no seu código, mantendo uma memória das mudanças e possibilitando um desenvolvimento colaborativo.

No GitHub, arquivos de código não são baixados por um botão de download, mas podem ser abertos: o dsd.py fica nesta página e pode ser aberto clicando sobre o botão Raw, que abrirá uma tela no seu navegador com o conteúdo do arquivo, que você pode salvar no seu working directory usando um comando save (Ctrl-S).

Importante!  Se você usar Salvar Como (Ctrl-S), provavelmente o seu navegador salvará o arquivo como dsd.py.txt. Caso seja gravado dessa forma, o Python não reconhecerá este arquivo como um módulo, que obrigatoriamente precisa ter a extensão '.py'.

O Chrome, por exemplo, tem como padrão gravar arquivos definindo como tipo Text Document (*.txt), o que faz com que os arquivos sejam gravados com a extensão .txt, mesmo se você o nomear simplesmente como 'dsd.py'. Para evitar esse problema, no momento da gravação, você deve escolher como tipo Todos os Arquivos (*.*), o que permitirá a gravação como 'dsd.py'. Se, por acaso, você gravar o arquivo como 'dsd.py.txt', basta renomear o arquivo, excluindo a extensão .txt.

Se você tiver alguma dificuldade com essa metodologia, outra  é copiar o conteúdo do arquivo dsd.py (veja que o terceiro ícone depois do Raw faz a cópia desse conteúdo na área de transferência) e colar diretamente em um novo aquivo aberto no Spyder. Nesse caso, você poderá salvá-lo como dsd.py.

Um programa que está no Github pode ser baixado e você pode apresentar propostas de aprimoramento ou desenvolvimento. Assim, vale a pena você criar sua conta no GitHub, para poder ter um acesso facilitado a essas funcionalidades e para participar do desenvolvimento colaborativo dos códigos da disciplina.

7. Importando e executando a função dsd.extrair()

Uma vez que você tenha instalado o módulo no seu computador, você pode importar as funções para cada programa, de tal forma que elas possam ser referidas apenas pelo seu nome. Para isso, você pode usar um comando do tipo:

from dsd import extrair

Essa opção faz com que você não importaria o módulo todo (com todas as suas funções), mas uma função específica, que pode ser invocada apenas pelo seu nome: em vez de utilizar a expressão dsd.extrair(), você poderia usar apenas extrair().

Você também pode importar o módulo todo, a partir do comando:

import dsd

Esse será o comando típico que iniciará os nossos programas, possibilitando que você utilize todas as funções definidas no módulo dsd.py.

Por exemplo, esse módulo contém a função dsd.extrair(), sendo que o nome completo da função indica o módulo em que ela está contida (dsd) e o nome específico da função (extrair), separados por um ponto, e seguidos pelos parênteses que indicam tratar-se de uma função.

A dsd.extrair() possibilita realizar de forma simplificada extração de um segmento de string, desde que você indique dentro dos parênteses os 3 parâmetros necessários para essa função: string a ser analisada, marcador de início e marcador de fim. O formato do comando que invoca essa função deve ser esse:

dsd.extrair (string, marcador_de_inicio, marcador_de_fim)

Como a dsd.extrair() não é nativa do Python, ela somente pode ser utilizadas nos programas em que você determine a sua importação (seja utilizando o "import dsd" ou o "from dsd impor extrair"). Feito isso, você pode realizar o mesma extração que realizamos antes, usando a função extrair:

import dsd # necessário para importar a função dsd.extrair()

# definição da string
string = 'ADI 333, Relator: Marco Aurélio, Resultado: Procedente'

# comando usando a função e atribuindo o resultado a uma variável
texto_extraido = dsd.extrair(string, 'Relator: ', ',')

print (texto_extraido)

Aqui você pode perceber facilmente a diferença entre função, que é uma sequência abstrata de instruções  — como a dsd.extrair() — e comando, que é uma definição concreta de instruções, que pode ser executada porque todos os parâmetros necessários estão definidos, como é o caso da linha 7 do programa acima.

8. Módulos e Bibliotecas

Uma das utilidades de produzir um módulo é que você pode usar as funções que você desenvolveu em vários programas diferentes, sem ter o  trabalho de programar várias vezes a mesma coisa. Além disso, uma vez que você introduzir aprimoramentos na função, eles serão incorporados imediatamente por todos os programas em que você a utiliza.

Dessa forma, é útil que você desenvolva programas específicos, mas que armazene as funções em módulos temáticos, que poderão ser utilizados tanto por você como por outras pessoas que produzam códigos em uma determinada área (data mining, estatística, visualização de dados, etc.).

Uma vez que esse desenvolvimento esteja amadurecido, o desenvolvedor normalmente contará com alguns módulos especializados, que podem ser consolidados na forma de um pacote (package), que é uma coleção de módulos organizados de uma forma determinada, para que eles possam ser importados conjuntamente.

Just like the use of modules saves the authors of different modules from having to worry about each other’s global variable names, the use of dotted module names saves the authors of multi-module packages like NumPy or Pillow from having to worry about each other’s module names. (Python Tutorial: Modules)

Uma vez que esses pacotes sejam publicados  (normalmente no Python Package Index (PyPI)), eles passam a ser chamados pelo nome genérico de bibliotecas (libraries). Biblioteca pode ser um módulo, um pacote ou um conjunto de pacotes, a depender de seu tamanho e complexidade. Porém, ela deve estar organizado de tal forma que você pode instalá-lo no seu computador, o que permitirá a sua importação para os seus programas.

Lembre-se: instalar um módulo ou um pacote faz com que eles estejam disponíveis na sua máquina, mas é sempre necessário importar para cada programa o módulo ou a biblioteca que se deseja utilizar, para que as suas funções sejam executáveis dentro de um programa específico.

Em breve, você se acostumará com importar algumas das bibliotecas de Python mais usadas: pandas, os, time, beautifulsoup, etc. Essas bibliotecas são fruto do trabalho de programadores (como você!), que disponibilizam seu trabalho para outras pessoas utilizarem, normalmente utilizando licenças que permitem o uso e modificação dos programas, contribuindo para a construção coletiva de novos desenvolvimentos.

9. PIP

Para instalar pacotes (packages) no Python, você conta com uma ótima ferramenta: pip. O pip já vem instalado no Anaconda e, com isso, você pode instalar novos pacotes a partir do console do Spyder ou prompt de comando.

Por exemplo, se você quiser instalar a biblioteca arrow, que lida com datas, basta inserir o comando:

pip install arrow

Como saber isso? Quando você encontra uma biblioteca em Python, ela provavelmente fará parte da Python Package Index (PyPI), o que significa que ela pode ser instalada via pip. Quando você abre a página de uma dessas bibliotecas, uma das primeiras coisas que aparece é justamente o comando de instalação, inclusive com um botão que já copia o comando para a sua área de transferência.

Assim, quando você encontrar na internet indicações de que uma determinada biblioteca contém funções adequadas para o seu programa, basta fazer a sua instalação.

Lembre-se: instalar a biblioteca é um passo necessário para utilizá-la (embora várias já estejam instaladas via Anaconda), mas para usar a função dentro de um programa específico, é preciso importar o módulo ou pacote para o seu programa.