A interactive help (ajuda interativa) é algo fácil de ser usado em Python, já que tem a função nativa help()
. Podemos ir direto no console Python e digitar isso para o visualizarmos, aí ele entrará no modo help e podemos digitar o nome da função ou atributo interno do Python (por exemplo, o print, o input, o len e o datetime). Ao digitar quit
ele sairá desse modo.
Podemos também colocar a ajuda diretamente no script, com a função ou atributo especificado dentro dos parênteses (por exemplo, help(print)
.
Podemos imprimir o documento da função ou atributo digitando ele, seguido de __doc__, como por exemplo, print(input.__doc__)
.
As docstrings são basicamente, as strings de documentação, como a que acabamos de ver com a função __doc__.
Vamos supor que exista uma função chamada contador com três parâmetros, para início, fim e passo, por exemplo:
def contador(i, f, p):
c = i
while c <= f:
print(f"{c} ", end="")
c += p
print("FIM!")
contador(2, 10, 2)
Como nós que criamos a função, sabemos como funciona, mas se outro programador ver essa mesma função, poderá não saber como ela funciona exatamente.
Para criar uma docstring, basta colocar dentro da função, antes de qualquer código, um comentário de várias linhas, assim:
def contador(i, f, p):
"""
-> Faz uma contagem e mostra na tela.
Parâmetro i: Início da Contagem.
Parâmetro f: Fim da Contagem.
Parâmetro p: Passo da Contagem.
"""
c = i
while c <= f:
print(f"{c} ", end="")
c += p
print("FIM!")
contador(2, 10, 2)
help(contador)
PS: Podemos colocar outros comentários dentro dessas aspas, que aparecerá no help também.
Podemos também usar parâmetros opcionais, para entendermos como funciona, vamos supor uma função soma, assim:
def somar(a, b, c):
s = a + b + c
print(f"A soma vale {s}.")
somar(3, 2, 5)
Se colocarmos menos de três parâmetros, ele dará erro, para isso, podemos colocar atribuições padrão nos parâmetros, por exemplo:
def somar(a = 0, b = 0, c = 0):
s = a + b + c
print(f"A soma vale {s}.")
somar(3, 2, 5)
somar(8, 4)
somar(7)
somar()
Nesse caso, se ele não receber nenhum valor, ele será atribuído à zero, se receber ele será substituído pelo valor passado.
PS: Nem todos os parâmetros precisam ser inicializados, nesse caso, coloque os parâmetros nos à direita.
Podemos também, na mesma função acima, definir qual valor será passado em cada parâmetro, independente da ordem, por exemplo:
somar(b = 4, c = 2)
somar(c = 3, a = 2)
Escopo é o local onde ela existe, se ela existirá no programa todo ou dentro de alguma função.
Veja um exemplo de uso de variável global:
def teste():
print(f"Na função teste, n vale {n}.")
n = 2 # Variável Global
print(f"No programa principal, n vale {n}.")
teste()
Pode ver que n existe no programa todo, por isso é uma variável global, acessada dentro e fora da função.
Vamos criar uma variável local da função teste, assim:
def teste():
x = 8 # Variável Local
print(f"Na função teste, n vale {n}.")
print(f"Na função teste, x vale {x}.")
n = 2 # Variável Global
print(f"No programa principal, n vale {n}.")
print(f"No programa principal, x vale {x}.") # Dará erro porque a variável x é local da função.
teste()
Como visto acima, o programa não conseguiu acessar x fora da função teste, ela só existe dentro dela. Remova o print do x fora da função e funcionará normalmente.
PS: Variáveis de parâmetros também são locais da função.
Veja outro exemplo:
def teste2(b):
b += 4 # Variável Local
c = 2 # Variável Local
print(f"A dentro vale {a}.")
print(f"B dentro vale {b}.")
print(f"C dentro vale {c}.")
a = 5 # Variável Global
teste2(a)
print(f"A fora vale {a}.")
Como visto acima, a não se alterou e é acessível tanto pra função quanto pra fora dela, e tem escopo global. As b e c são de escopo local.
Caso criemos uma variável a dentro da função, ela será outra variável, de escopo local, por exemplo:
def teste2(b):
a = 8 # Variável Local, Não é o Mesmo a do Programa Principal
b += 4 # Variável Local
c = 2 # Variável Local
print(f"A dentro vale {a}.")
print(f"B dentro vale {b}.")
print(f"C dentro vale {c}.")
a = 5 # Variável Global
teste2(a)
print(f"A fora vale {a}.")
Como visto acima, o a da função local tem valor diferente do a global.
Podemos também indicar que a variável é global dentro da função, de forma que ele não crie outra local com o mesmo nome, assim:
def teste2(b):
global a
a = 8 # Variável Global Alterada.
b += 4 # Variável Local
c = 2 # Variável Local
print(f"A dentro vale {a}.")
print(f"B dentro vale {b}.")
print(f"C dentro vale {c}.")
a = 5 # Variável Global
teste2(a)
print(f"A fora vale {a}.")
No caso acima, o a global é alterado para 8 ao chamar a função. O b recebeu o parâmetro antes do a ser alterado.
Outro exemplo:
def funcao():
n1 = 4
print(f"N1 dentro vale {n1}.")
n1 = 2
funcao()
print(f"N1 fora vale {n1}.")
No caso acima, o n1 de dentro da função também não é o mesmo n1 do programa principal.
As funções também podem retornar valores, usando a palavra return
, vamos voltar à função somar, como estava:
def somar(a = 0, b = 0, c = 0):
s = a + b + c
print(f"A soma vale {s}.")
somar(3, 2, 5)
somar(1, 7)
somar(4)
Alterada com retorno, ficaria assim:
def somar(a = 0, b = 0, c = 0):
s = a + b + c
return s
print(somar(3, 2, 5))
Aí, a função receberá um valor de retorno, e pode ser usada como uma variável, sendo atribuída a uma variável comum ou colocada no print.
PS: Qualquer tipo de valor pode ser retornado, como string, inteiro, booleano, listas, dicionários, objetos, etc.
Outro exemplo:
def somar(a = 0, b = 0, c = 0):
s = a + b + c
return s
r1 = somar(3, 2, 5)
r2 = somar(1, 7)
r3 = somar(4)
print(f"Meus cálculos deram {r1}, {r2} e {r3}.")
Vamos fazer um programa pra calcular fatoriais:
def fatorial(num = 1):
f = 1 # Local
for c in range(num, 0, -1):
f *= c
return f
n = int(input("Digite um número: "))
print(f"O fatorial de {n} é igual à {fatorial(n)}.")
Podemos fazer assim com a função acima:
f1 = fatorial(5)
f2 = fatorial(4)
f3 = fatorial()
print(f"Os resultados são {f1}, {f2} e {f3}.")
Outro exemplo, com valores booleanos:
def par(n = 0):
if n % 2 == 0:
return True
else:
return False
num = int(input("Digite um número: "))
print(par(num))
Usando a mesma função acima, podemos exibir assim:
if par(num): # O mesmo que par(num) == True
print("É Par!")
else:
print("É Ímpar!")
Temos também as expressões Lambda em Python, que são pequenas funções sem nome, usadas com uma variável, dessa forma:
mensagem = lambda: print("Expressão Lambda Exibida com Sucesso!")
mensagem() # Exibição de lambda
Quando precisar usar uma expressão Lambda com retorno, nem precisa indicar return, e quando tiver parâmetros basta escrever eles após a palavra lambda, separados por vírgulas, assim:
produto = lambda n1, n2: n1 * n2
print(f"O resultado da multiplicação é {produto(5, 2)}.") # Exibição de lambda com retorno
Imagina que temos um programa grande, como o de um cadastro, se colocássemos todas as funções num arquivo só seria complicado pra entender e mantê-lo, pra isso surgiu os módulos, que nos permite dividir um programa grande.
Vamos supor a seguinte função:
num = int(input("Digite um número: "))
fat = fatorial(num)
print(f"O fatorial de {num} é {fat}.")
A função fatorial não existe no Python, teremos que criá-la, e faríamos assim:
def fatorial(n):
f = 1
for c in range(1, n + 1):
f *= c
return f
Até agora, colocamos tudo isso num arquivo só.
Mas vamos supor que no mesmo programa temos mais funções, por exemplo:
def fatorial(n):
f = 1
for c in range(1, n + 1):
f *= c
return f
def dobro(n):
return n * 2
def triplo(n):
return n * 3
num = int(input("Digite um número: "))
fat = fatorial(num)
print(f"O fatorial de {num} é {fat}.")
print(f"O dobro de {num} é {dobro(num)}.")
Dessa forma, podemos criar os módulos, fazemos isso criando um arquivo no projeto (por exemplo, uteis.py), e colocando as funções dentro dele:
def fatorial(n):
f = 1
for c in range(1, n + 1):
f *= c
return f
def dobro(n):
return n * 2
def triplo(n):
return n * 3
E para importar, faça da mesma forma que outros módulos, colocando o nome do arquivo seguido da função, assim:
import uteis
num = int(input("Digite um número: "))
fat = uteis.fatorial(num)
print(f"O fatorial de {num} é {fat}.")
print(f"O dobro de {num} é {uteis.dobro(num)}.")
Podemos também importar funções específicas assim:
from uteis import fatorial, dobro
num = int(input("Digite um número: "))
fat = fatorial(num)
print(f"O fatorial de {num} é {fat}.")
print(f"O dobro de {num} é {dobro(num)}.")
PS: Apesar de possível, não é recomendado importar duas ou mais funções de um módulo separando por vírgulas.
Podemos observar que usamos isso de forma parecida com o uso de módulos nativos do Python, como o math e o datetime.
As vantagens da modularização são várias, como organização do código, facilidade na manutenção, ocultação de código detalhado, reutilização em outros projetos, etc.
Temos também os pacotes no Python, que são como as bibliotecas de outras linguagens.
Caso os módulos também fiquem muito grandes, podemos separar em vários arquivos, que podem ser juntados em pacotes (por exemplo, o uteis pode ser um pacote com vários módulos separados, como o de números, strings, datas e cores, etc.)
Dentro do Python, assim como todo arquivo Python é um módulo, toda pasta é um pacote, e dentro do pacote podemos ter outros pacotes (por exemplo, o uteis pode ter pastas para arquivos de números, strings, etc). Cada pacote tem dentro dele um arquivo chamado __init__.py, que é onde temos nossos módulos.
Dentro do Pycharm, exclua o arquivo uteis.py e crie um pacote Python com o nome de uteis. Dentro de uteis, crie outros pacotes com o nomes especificados acima.
Vamos no pacote numeros e coloque as funções dentro dele, e no arquivo principal altere dessa forma:
from uteis import numeros
num = int(input("Digite um número: "))
fat = numeros.fatorial(num)
print(f"O fatorial de {num} é {fat}.")
print(f"O dobro de {num} é {numeros.dobro(num)}.")
Vamos supor um exemplo de erro (por exemplo, usar primt(x)
), isso é um erro de sintaxe, mas também tem casos que mesmo sem esse tipo de problema, o programa dá algum erro, vamos supor por exemplo:
print(x)
Como a variável x não foi inicializada, ele vai dar um erro semântico, e no PyCharm aparece os erros vermelhos, que é o significado do erro. Nesse caso é chamado de exceção, e no caso foi a "NameError".
Veja outro exemplo que soltará uma exceção, caso não seja inserido um número:
n = int(input("Número: "))
print(f"Você digitou o número {n}.")
Ele soltará a exceção "ValueError", nesse caso.
Outro exemplo que pode dar exceção:
a = int(input("Numerador: "))
b = int(input("Denominador: "))
r = a / b
print(f"O resultado é {r}.")
Ao colocar denominador "0", ele dará a exceção "ZeroDivisionError".
Veja esse outro exemplo, que gerará o erro "TypeError":
r = 2 / "2"
Esse exemplo dará um "IndexError":
lst = [3, 6, 4]
print(lst[3])
Ao tentar importar um módulo que não existe, gerará a exceção "ModuleNotFoundError".
Resumindo, as exceções são mensagens disparadas ao ter um erro semântico no programa, que interromperá a execução dele e mostrará um aviso dela.
Existem muitas exceções em Python, mas a maioria das exceções conseguimos identificar ao fazermos nossos programas com esses erros, e posteriormente tratá-las. Elas são filhas da classe Exception no Python.
Pra tratar exceções em Python, usamos a estrutura try except, dessa forma:
try:
# Código a tentar ser executado
except:
# Código a ser executado caso o try tenha uma falha
else:
# Código a ser executado caso o try não dê erro
finally:
# Código a ser executado sempre
Eles sempre tem que estar nessa ordem, try, except, else e finally, mas else e finally são opcionais.
Veja como ficará o programa de divisão assim:
try:
a = int(input("Numerador: "))
b = int(input("Denominador: "))
r = a / b
except:
print("Infelizmente deu erro! :(")
else:
print(f"O resultado é {r:.1f}.")
finally:
print("Volte Sempre!")
No caso acima, o erro foi tratato e se tentarmos dividir por zero ou inserir uma string, ele exibirá a nossa mensagem.
Podemos jogar a exceção numa variável, assim:
try:
a = int(input("Numerador: "))
b = int(input("Denominador: "))
r = a / b
except Exception as erro:
print(f"Problema encontrado foi {erro.__class__}.")
else:
print(f"O resultado é {r:.1f}.")
finally:
print("Volte Sempre!")
Toda estrutura try pode ter mais de um except, cada um com seu erro especificado, ao invés de colocar um except genérico, por exemplo:
try:
a = int(input("Numerador: "))
b = int(input("Denominador: "))
r = a / b
except (ValueError, TypeError):
print("Tivemos um problema com os tipos!")
except ZeroDivisionError:
print("Não é possível dividir por zero!")
except KeyboardInterrupt:
print("Não foram informados os dados.")
except Exception as erro:
print(f"O erro encontrado foi {erro.__class__}.")
else:
print(f"O resultado é {r:.1f}.")
finally:
print("Volte Sempre!")
PS: Ao testar um programa com exceções, teste fazendo o certo primeiro, pra depois provocar as exceções.
Veja esse exemplo com arquivos:
try:
arquivo = open("arquvo.txt", "rt")
for l in arquivo:
print(l)
arquivo.close()
except:
print("Arquivo não Encontrado!")
Podemos também criar nossas próprias condições para exceções, usando um if e o comando raise
, assim:
try:
sexo = str(input("Digite o sexo [M/F]: ")).strip().upper()[0]
if sexo not in "MmFf":
raise Exception("Não foi inserido um sexo válido!")
except Exception as erro:
print(erro)
else:
print(f"O sexo digitado foi {sexo}.")
No caso acima, ele tentará executar o programa, e caso o sexo inserido não seja M ou F, ele lançará a exceção Exception e interromperá a execução no try, passando a executar o except especificado.
O ideal é colocar o raise numa função, dessa forma:
def verifSexo(sexo):
if sexo not in "MmFf":
raise Exception("Não foi inserido um sexo válido!")
print(f"O sexo digitado foi {sexo}.")
try:
s = str(input("Digite o sexo [M/F]: ")).strip().upper()[0]
verifSexo(s)
except Exception as erro:
print(erro)
PS: É interessante colocar a cor vermelha nas exceções criadas.