Aprofundamento em programação com Python

Conteúdos

  • Controle de Fluxo
  • Estruturas de repetição
  • Funções
  • Orientação a Objetos
  • Instalação de bibliotecas
  • Importação de bibliotecas

Controle de fluxo

Nem sempre nosso código deve se comportar de maneira igual para todas as situações, às vezes precisamos tomar uma decisão em função de nossas operações em situações específicas e para isso podemos utilizar estruturas de condição.

if

Quando falamos em tomar decisões, a estrutura que trabalhamos é sempre o if, que é uma estrutura que testa condições impostas para decidir como prosseguir em certas situações, como por exemplo checar se um número é maior, menor ou igual a outro. Testando uma condição única:

if 10 < 30:
    print('10 é menor que 30')

10 é menor que 30

Podemos também testar múltiplas condições exigindo que todas sejam verdadeiras ou apenas uma seja, dependendo do operador usado: or ou and. Precisamos separar essas diferentes condições usando parentêsis.

O operador or, “ou” traduzindo do inglês, faz com que a condição seja satisfeita se a primeira ou a segunda expressão (ou as duas) forem verdadeiras. Pode ser também representado pelo caractere |

if (7 < 3) or (5 > 1):
    print('Pelo menos um é verdade')
Pelo menos um é verdade

Já o operador and, “e” traduzindo do inglês, faz com que a condição seja satisfeita se a primeira e a segunda expressão sejam verdadeiras Pode ser também representado pelo caractere &

if (7 == 7) and (5 > 1):
    print('Ambas expressões são verdadeiras')
Ambas expressões são verdadeiras

else

Para toda situação sempre podemos incluir uma saída padrão como resposta se a condição não for satisfeita, para isso temos o else.

n = 40
if n < 30:
    print('%i é menor que 30'%(n))
else:
    print('%i é maior ou igual a 30'%(n))
40 é menor maior ou igual a 30

elif

Podemos também incluir mais de uma condição a ser testada, adicionando mais instâncias do if ao mesmo if com a estrutura elif.

n = 40
if n < 30:
    print('%i é menor que 30'%(n))
elif n > 30:
    print('%i é maior que 30'%(n))
else:
    print('%i é igual a 30'%(n))
40 é maior que 30

Estruturas de repetição

No python, assim como qualquer linguagem de programação, existe o que chamamos de estruturas de repetição. Elas servem para executar um mesmo comando mais de uma vez, muito útil quando trabalhando com listas.

for

O for é uma estrutura de repetição que percorre um escopo de valores específicos, por exemplo se queremos printar os números de 0 até 10 podemos escrever desta forma:

for numero in range(0,10):
    print(numero)
0
1
2
3
4
5
6
7
8
9

Note que o primeiro número impresso é o 0 como especificado e o último número o 9. A variável numero armazena o valor de cada iteração do loop, mas o for sempre para antes do último número, que seria o 10, porque o Python é uma linguagem que começa com 0 invés de 1, e descartar esse último número auxilia na hora de iterar listas, a principal razão do uso do laço for. Observe como percorrer os elementos de uma lista:

meses = ['jan', 'fev', 'mar', 'abr', 'mai', 'jun']
for i in range(length(meses)):
    print(meses[i])
jan
fev
mar
abr
mai
jun

Por convenção, no python usamos como variável do loop o i se quisermos percorrer uma lista. Usar um for dentro de outro serve para percorrer uma matriz. Nesse caso usaremos j no segundo. Se mais uma dimensão for adicionada será k. Para ordens ainda maiores não existe uma convenção, porém, eu utilizo o alfabeto invés de i, j, k.

matriz = [[1, 2, 3], [4, 5, 6], [7, 8, 9]]
for i in range(length(matriz)):
    for j in range(length(matriz[i])):
        print(matriz[i][j])
1
2
3
4
5
6
7
8
9

Existe uma alternativa ao range de como percorrer uma lista com o for. O range cria esses índices para serem referenciados na lista. Podemos salvar os elementos direto na variável do loop como feito a seguir:

meses = ['jan', 'fev', 'mar', 'abr', 'mai', 'jun']
for mes in meses:
    print(mes)
jan
fev
mar
abr
mai
jun

O problema de usar o loop dessa forma é que não podemos alterar a lista original já que perdemos a referência numérica dela. Existem situações que o primeiro método é preferível e situações que o segundo método é preferível.

while

O while é um loop que enquanto uma condição não for satisfeita ou enquanto ela for satisfeita esse loop permanecerá ocorrendo, usado principalmente quando o número de iterações do loop não é definido por um número e sim por uma condição.

animais = ['cachorro', 'gato', 'rato', 'macaco', 'cachorro', 'cachorro', 'elefante', 'leao']
while 'cachorro' in animais:
    animais.remove('cachorro')
print(animais)
['gato', 'rato', 'macaco', 'elefante', 'leao']

Repare que 'cachorro' foi removido mais de uma vez da lista de animais. Uma vez que não haviam mais cachorros na lista de animais, o loop do while foi finalizado.

break e flags

Podemos interromper o while utilizando o break ou flags, como nos exemplos abaixo:

# Saudando pessoas até que elas queiram sair do programa com o break
while True:
    resposta = input('Digite seu nome para que eu possa te dar um oi!:\n'+'(Entre com "quit" para sair da aplicacao)\n\n')
    
    if resposta.lower() == 'quit':
        break
    # SE CAIR NA CONDIÇÃO DO `BREAK` AS LINHAS ABAIXO DESTE COMENTÁRIO NÃO SERÃO EXECUTADAS!!!
print('Olá, ' + resposta)
Olá quit

Um exemplo com flags que são variáveis booleanas que controlam o fluxo.

continuar_execucao = True
while continuar_execucao:
    resposta = input('Digite seu nome para que eu possa te dar um oi!:\n'+'(Entre com "quit" para sair da aplicacao)\n\n')
    if resposta.lower() != 'quit':
        print('Olá, ' + resposta)
    else:
        continuar_execucao = False
Olá quit

continue

Também podemos dar continuidade a um ciclo com a utilização do continue.

while True:
    resposta = input('Digite seu nome para que eu possa te dar um oi!:\n' + '(Entre com "quit" para sair da aplicacao)\n\n')
    if resposta == '':
        print('Digite um nome válido!')
        continue
    # SE O USUÁRIO APENAS PRECIONAR ENTER SEM ADICIONAR SEU NOME O CICLO REINICIARÁ E PEDIRA NOVAMENTE PARA QUE
    # DIGITE SEU NOME.
    if resposta.lower() == 'quit':
        break
    # SE CAIR NA CONDIÇÃO DO `BREAK` AS LINHAS ABAIXO DESTE COMENTÁRIO NÃO SERÃO EXECUTADAS!!!
    print('Olá, ' + resposta)
Digite um nome válido!

Repare que o print('Olá, ' + resposta) não foi feito porque na primeira iteração o loop caiu no continue e no segundo no break porque o quit foi digitado.

Funções

Várias vezes na programação repetimos a mesma tarefa várias vezes ou precisamos criar uma tarefa que possa ser usada mais tarde em outro programa, para evitar repetição de código e seguir um dos princípios da programação que é a modulação utilizamos da criação de funções.

Em resumo, funções são um escopo de código que pode ser chamado para realizar uma tarefa específica, podem ter ou não um tipo de retorno, possuem um título e podem ter ou não parâmetros passados para a função.

Em python utilizamos a sintaxe: def nome_da_funcao(parametros): #escopo da função return objeto_a_ser_retornado

Os exemplos mais simples de funções podem ser vistos abaixo:

def soma(a, b): #definição/criação da função
    return a + b #escopo da função
soma(3, 4) #chamada da função
def ola_mundo():
    print('Olá, Mundo!')
ola_mundo() 
Olá, Mundo!

Exemplo de uma função com retorno de um objeto do tipo lista:

def formata_nomes(nomes):
    lista_formatada = []
    for nome in nomes:
        lista_formatada.append(nome.title())     
    return lista_formatada
lista_nao_formatada = ['dAnIeL', 'iGoR', 'caRoLINA']
lista_formatada = formata_nomes(lista_formatada)
print(lista_formatada)
['Daniel', 'Igor', 'Carolina']

Orientação a objetos

No mundo da programação existem diversas linguagens. As linguagens são criadas seguindo paradigmas por vezes distintos, isso é, diferentes lingugagens seguem diferentes formas de se encarar o próprio ato de programar, podendo ainda seguir mais de um paradigma diferente. No python, um dos paradigmas explorados é o de orientação a objetos.

Uma linguagem orientada a objetos escreve seus programas baseados em classes e objetos. Para entender o que são classes e objetos precisamos fazer um paralelo com o mundo real, por exemplo um carro.

Um carro possui características específicas: modelo, cor, velocidade. O carro possui ainda comportamentos: acelerar e freiar, acessíveis pelo motorista para usar quando quiser. Na programação, o carro é o objeto, a ideia do que é um carro é a classe e o motorista é o programador que usa o carro em seu programa.

Traduzindo para python:

class Carro():
    
    def __init__(self, modelo, cor, velocidade):
        self.modelo = modelo
        self.cor = cor
        self.velocidade = velocidade
    
    def getModelo(self):
        return self.modelo
    
    def getCor(self):
        return self.cor
    
    def getVelocidade(self):
        return self.velocidade 

    def acelerar(self, incrementoVelocidade):
        self.velocidade = self.velocidade + incrementoVelocidade 

    def freiar(self, decrementoVelocidade):
        if self.velocidade < decrementoVelocidade:
            self.velocidade = 0
        else:
            self.velocidade = self.velocidade - decrementoVelocidade 


meuCarro = Carro("Gol", "vermelho", 0)

O comando class do python funciona semelhante ao def, porém define uma classe. Dentro da definição da classe, diversas “funções” foram definidas: __init__, getModelo, getCor, getVelocidade, acelerar, freiar. O nome de funções no contexto de um objeto é método. Métodos são semelhantes a funções, porém utilizam também como variáveis os atributos da classe, isso é, são os comportamentos do carro e usam as características do carro. Na definição, os métodos da classe têm os argumentos começando com o self se forem usar algum atributo do objeto, na hora de utilizar esses metodos, não se escreve o self, uma vez que o metodo que parte do objeto em questão já entende que o mesmo é o “self”.

O método __init__ é um método especial, ele pode ser chamado de construtor. Ele receberá como argumentos as características que julgamos necessárias para definir nosso objeto. Na linha meuCarro = Carro(“Gol”, “vermelho”, 0), o método __init__ é chamado e retorna um objeto da classe Carro de modelo “Gol”, de cor “vermelha” e velocidade 0. Sempre com o nome da classe e os argumentos do método __init__ da classe em questão (exceto o self), essa função constroi o objeto.

Para usar os métodos precisa digitar a variável em que o objeto está armazenado depois .nomeDoMetodo(argumentos) como segue no exemplo abaixo:

print("Velocidade antes de acelerar:", meuCarro.getVelocidade()) 
meuCarro.acelerar(10)
print("Velocidade depois de acelerar:", meuCarro.getVelocidade())

Velocidade antes de acelerar: 0

Velocidade depois de acelerar: 10

O código do exemplo mudou a velocidade do objeto meuCarro, que originalmente era 0 e passou para 10.

Para acessar os atributos (características) do objeto, normalmente se usa esses métodos do tipo getAtributo(). Pode-se, também, trazendo pro contexto do exemplo, acessar usando apenas meuCarro.velocidade invés de meuCarro.getVelocidade(). Semelhante a como é feito a chamada dos métodos, porém sem o () no final. Embora possa parecer mais simples acessar a velocidade diretamente, sem criar e chamar o método getVelocidade, em orientação a objetos existem algumas práticas que todo o programador deve seguir, essa é uma delas, o encapsulamento, que consiste em esconder os detalhes da implementação da classe do objeto criado.

Essa regra do encapsulamento e outras regrinhas não são importantes de se saber para o contexto de ciência de dados. Orientação a objetos é toda uma forma de se programar, um assunto extenso, imprescindível no processo de criação de um software, não necessariamente para se usar um. É importante saber o básico para entender o que está acontecendo por baixo de bibliotecas, que comumente utilizam classes. O matplotlib, uma biblioteca de visualização de dados, por exemplo, tem um objeto de uma classe de visualizações como meio de se criar as imagens de visualização de dados, todos os comandos de como criar os gráficos são feitos por métodos desse objeto.

Instalação de bibliotecas

O píp é um repositório que guarda uma quantidade enorme de pacotes e módulos para utilizarmos em nossas aplicações. Se o Python estiver adicionado ao PATH digite no terminal do windows:
~~~ $ pip –version ~~~

Caso seu pip esteja desatualizado utilize:

$ python -m pip install --upgrade pip

Para instalar algum pacote execute:

$ pip install "nome do pacote"

ou ainda

$ python -m pip install "nome do pacote"

Importação

Agora veremos as diferentes formas de importar módulos (os pacotes do python) para seu ambiente.

import numpy                        # Importa o módulo numpy
import pandas as pd                 # Importa o módulo pandas com um alias pd
import matplotlib.pyplot as plt     # Importa a classe pyplot dentro do pacote matplotlib com o alias plt
from os import getcwd               # Importa apenas a função getcwd do módulo os
from time import asctime as data    # Importa a penas a função asctime com o alias data 
from statistics import *            # Importa tudo que pertence ao módulo statistics

Alias é o “apelido” que o módulo terá no código, ou seja, apenas precisamos chamar o alias para referenciar o módulo

Vejamos agora como cada forma de importação é utilizada!

Veja que o numpy foi importado utilizando import numpy dessa forma devemos sempre colocar o nome do módulo antes de utilizar os métodos e atributos que ele possui

numpy.random.seed(60)
numpy.random.normal(0, 1, 10)       # Cria um array aleatório de tamanho 10 com distriuição normal mu = 0, sigma = 1.

array([-9.21770993e-01, -5.86317634e-01, 1.16399914e+00, -1.24172396e+00, -1.98523022e+00, 1.30670891e+00, 7.37807059e-01, 3.79111282e-01, 9.89200864e-04, -1.10503482e+00])

Já em from os import getcwd apenas importamos a função getcwd do módulo os

getcwd()                            # Retorna o diretório atual do workspace.

‘/home/daniel.dsantos/Documents/personal/git/python-basico/notebooks/01_python_basico’

Utilizando o comando from time import asctime as data importamos a função asctime do módulo time e adicionamos um alias data utilizando a keyword as.

data()                              # Retorna a data de hoje

‘Wed Nov 13 10:39:42 2019’

Ao utilizar from statistics import * estamos importando todas as funções e métodos pertencentes ao módulo statistics. Não é aconselhável utilizar já que o código perde semântica.

numpy.random.seed(35)
numbers = numpy.random.randint(1, 20, 30)

print(mean(numbers))
print(harmonic_mean(numbers))
print(median(numbers))
9
4.522366591633217
10.5

Referências

MATTHES, Eric. Python Crash Course: A Hands-On, Project-Based Introduction to Programming. San Francisco: No Starch Press, 2016.
LUTZ, Mark. Learning Python. Estados Unidos: O’Reilly Media, 2013.