[Continuação] Introdução ao Python: lógica da programação em Python
- #Python
Essa é uma continuação do meu primeiro artigo, na qual me aprofundo sobre Python, explicando conceitos básicos da linguagem e sobre lógica da programação. São abordados nesse artigo alguns assuntos como comentários em código, valores e variáveis, tipos de dados básicos em Python, objetos e operadores.
Para poupar seus esforços, o artigo foi escrito no formato de resumo – fique à vontade para salvá-lo e utilizá-lo sempre como referência em seus estudos.
Boa leitura!
Comentários no código
É possível incluir textos que serão ignorados durante a compilação do código. Eles são chamados de comentários, e são muito importantes para realizar a documentação de um arquivo de código, explicando para que serve determinada função ou para dividir o código em seções.
Deve-se tomar cuidado para não exagerar na quantidade de comentários.
Em Python, utilizamos # <texto>
para comentar. Não existem comentários em bloco.
Valores e variáveis
O valor é um dado, é a unidade mais básica e fundamental que temos na programação. É um pedaço de memória que recebe um nome, o qual você pode alocar específicos tipos de dados. Ele pode ser um número, uma string
(sequência de caracteres), uma booleana, etc. Os valores podem ser armazenados em variáveis para serem reutilizados no código.
Em Python utilizamos o operador de atribuição de valores (=). Por exemplo: radius = 1
.
Convenções para nomeação de variáveis
Em Python, os nomes das variáveis podem conter letra, número, underscore. A seguir, algumas convenções utilizadas pelos desenvolvedores de Python para padronizar a nomeação de variáveis:
- Utilizar a notação snake_case
- Não iniciar nem terminar as variáveis com dois underscores, pois o Python utiliza esse formato para os chamados dunder variables, atributos especiais
- Não inserir números no início do nome
- Não nomear variáveis com palavras-chaves (palavras reservadas) da própria linguagem, por exemplo
def
,if
,and
, etc. - Utilizar a notação camelCase para classes
- Escrever todas as letras em maiúsculo quando a variável for um valor constante
- Nomear as variáveis de forma mais descritível o possível, ou seja, deve ser fácil entender sobre o que se trata a variável
Tipos de dados básicos
Os computadores armazenam números utilizando o sistema binário, que se utiliza somente de 0s e 1s para representar números integrais ou decimais. O número decimal 0.625 por exemplo, pode ser representado de forma binária como:
1/2¹ + 0/2² + 1/2³
É um número finito. No entanto, assim como algumas frações (por exemplo: 1/3) não possuem representação finita no sistema decimal, alguns números decimais não possuem representação finita no sistema binário. Portanto, o tipo float
não é sempre exato, e isso pode trazer problemas quando utilizamos esse tipo de dado para aplicações financeiras.
Um ponto importante a se atentar é evitar a comparação de float
uns com os outros.
Observação: isso não é uma limitação apenas do Python. Qualquer linguagem que utilize frações binárias possuem esse problema.
Há um tipo de dado capaz de fazer representações exatas de frações decimais, chamado decimal
. No entanto, ele não é tão eficiente, nem do ponto de vista de memória quanto computacional, ou fácil de trabalhar quanto o tipo float
.
int
O tipo int
é utilizado para representar números integrais, como 0, 1, 100, -100, etc.
Em Python, as integrais possuem representação exata e podem ser de qualquer magnitude (positiva, ou negativa), desde que haja espaço para isso na memória da máquina.
Os números integrais podem ser criados por literais – ou seja, basta digitar literalmente o número no código – ou como resultado de alguma expressão. Eles também podem ser divididos por underscore para facilitar a leitura, por exemplo 10_500_000
. Isso não afeta como o código é compilado ou interpretado.
float
O tipo float
é utilizado para representar números racionais (pontos flutuantes), como 3.14, -1.3, etc. Assim como nos números do tipo int
, pode-se utilizar underscore para facilitar a leitura, por exemplo 1_234.567_876
. Os números do tipo float
também podem ser definidos a partir de literais.
Objetos
Objetos são entidades criadas pelo Python que contém estados e métodos, sendo:
- estados: dados
- métodos: funcionalidades
Ambos estão encapsulados dentro do objeto. Geralmente utilizamos objetos para representar coisas do mundo real, como um carro.
Por exemplo: Carro
Estado (dados)
- marca → Toyota
- modelo → Prius LE
- portas → 4
- ano → 2020
- quilometragem → 5_402
Funcionalidades
- acelerar( )
- frear( )
- calibrar( )
- ligar_seta_esquerda( ) </aside>
Observação: todos os tipos de dados em Python são objetos.
int como objeto
Uma integral é considerada um objeto em Python porque ela tem um estado (que é o valor da integral) e possui funcionalidade. Ela sabe, por exemplo, como adicionar si mesma a outra integral por meio do operador (+) e pelo método __add__
. Uma integral também sabe como representar si mesma como uma string
(para o output visual).
float como objeto
Assim como as integrais, os números do tipo float
também são considerados objetos, pois seus valores também são estados e também possuem funcionalidades. Além do __add__
, possuem outros métodos, como por exemplo o .as_integer_ratio( )
, que retorna o float
como uma fração.
Atributos de um objeto
Denominamos o encapsulamento (estado e métodos) como atributos de um objeto, uma definição genérica. Alguns atributos são para o estado, outros para a funcionalidade. Podemos acessar esses atributos através da notação de ponto.
Exemplificação:
car.brand
: acessa o atributo brand do objeto carcar.model
: acessa o atributo model do objeto car
Para atributos que representam a funcionalidade (métodos), devemos chamar o atributo para realizar a ação, assim como chamamos as funções. Para isso, devemos passar parâmetros adicionais para que funcione.
Por exemplo:
car.accelerate(10, 'km')
(10).__add__(100)
Mutabilidade e imutabilidade
Um objeto é considerável mutável se seu estado interno pode ser alterado, ou seja, se pelo menos um dos seus atributos podem ser mudados.
Um objeto é considerável imutável se seu estado interno não pode ser alterado.
Em Python, muitos tipos de dados são imutáveis, tais como int
, float
, bool
, str
, tuple
, etc. Outros são mutáveis, como list
, dict
, set
, etc.
Operadores
Precedência de operadores
Operadores têm precedência, ou seja, eles são classificados de modo que uma operação é avaliada antes de outra.
No que se refere aos operadores aritméticos, temos a seguinte classificação, da menor para maior precedência:
- Operadores binários + e -
- Operadores binários * e /
- Operadores unários + e -
- Operador de exponenciação **, exceto quando há um operador unário à direita do **
Por exemplo:
2 * 10 + 5 # (2 * 10) + 5 = 25
2 * 2 ** 3 # 2 * (2 ** 3) = 2 * 8 = 16
-2 ** 4 # -(2 ** 4) = -16
2 ** -3 # 2 ** (-3) = 0.125
Recurso/Documentação: Expressions
Depender da precedência de operadores automática pode resultar em bugs, por isso, é melhor sempre utilizar parênteses para especificar as expressões, mesmo que sejam apenas adição e subtração.
Operadores aritméticos
Terminologia
Um operador, em uma linguagem da programação, é um símbolo que realiza uma operação em um ou mais valores. Eles podem ser do tipo:
- Aritmético
- Comparativo (ou de relação)
- Lógico
Os valores sob os quais os operadores agem são chamados de operandos. Os operadores podem ser classificados de acordo com a quantidade de operandos sob os quais eles operam:
- Unário: apenas um operando
- Binário: dois operandos
- Ternário: três operandos
Operadores aritméticos
Unários
- Unário menos: -10
- Unário mais: +10
Binários
- Adição: 10 + 20
- Subtração: 20 - 10
- Multiplicação: 10 * 2
- Divisão: 10 / 2
- Exponenciação: 2 ** 4
- Módulo: 131 % 3
O operador de exponenciação
O operador de exponenciação pode retornar um int
ou um float
dependendo de seus operandos.
Por exemplo:
- 2 ** 4 = 16, retorna um
int
- 2 ** (-4) = 0.0625, retorna um
float
Assim como uma exponenciação matemática, o Python aceita float
para ambos os operandos do operador **. E também aceita bases negativas com exponentes reais, ou seja, números complexos. Inclusive, existe um tipo numérico conhecido como complex
em Python.
O operador de divisão
Em Python, existe um símbolo para realizar divisões de integrais que, sendo exatas ou não, retornam uma integral //
. No caso de divisões não exatas, ela retorna o valor antes do decimal – o restante é descartado, por exemplo 131 // 3 = 43.
O Python utiliza a função floor( )
para retornar uma integral como resultado de uma operação com o operador //
. A função floor( )
retorna o maior número integral <= x. Na prática, funciona bem para números positivos, mas para números negativos não é ideal.
Por exemplo:
floor(3.14)
= 3floor(-3.14)
= -4, ao invés de -312 // 5
= 2-12 // 5
= -3, ao invés de -2
O operador mod (%)
Da maneira como o módulo é definido em Python, ele acaba por resultar em comportamentos “estranhos” (não-intuitivos) para números negativos e do tipo float
.
Por exemplo:
- 12 % 5 = 2
- 12 % -5 = -3
- -12 % 5 = 3
- -12 % -5 = -2
Isso ocorre porque a operação mod equivale a % b = a - b (a // b).
Para visualizar isso na prática, vamos tomar como exemplo a seguinte equivalência 131/3 = 43 + 2/3. Ela pode ser escrita também da seguinte forma:
131/3 = floor(131/3) + (131%3)/3
cuja fórmula pode ser descrita como:
a/b = a//b + (a%b)/b
a%b = b * (a/b - a// b)
a%b = a - b * (a//b)
Tipos dos operandos
Operadores aritméticos podem operar em qualquer tipo numérico (int
, float
, etc).
No entanto, a operação realizada pelo operador é na verdade determinada pelo tipo dos operandos.
Um operador pode operar em operandos de tipos mistos.
Exemplificação:
- 2 + 2 retorna um
int
- 2 + 2.0 retorna um
float
- 5.5 * 2 retorna um
float
- 4 / 2 retorna um
float
(esse símbolo é chamado de divisão defloat
– o símbolo para divisão deint
é //)
Como Python implementa os operadores matemáticos
Quando somamos dois números, o Python executa o método dunder __add__
para avaliar a expressão. Por exemplo: a.__add__(b)
, sendo a = 10 e b = 20, resulta em 30.
Qualquer tipo pode implementar o método __add__
como queira, de tal modo que o Python poderá então utilizar esse método para avaliar a expressão type_1
+ type_2
.
Operadores comparativos
Também conhecidos como operadores relacionais, eles são utilizados para comparar dois dados, retornando um valor booleano (verdadeiro ou falso).
Alguns operadores são:
==
comparador de igualdade!=
comparador de diferença<
,<=
,>
,>=
menor, menor ou igual, maior, maior ou igual – estes comparadores assumem que os operandos sejam comparáveis, como 10.5 < 100, ao invés de olá > 100 (o que não faz sentido)
Quando o operador de igualdade é utilizado entre operandos que não são comparáveis, o resultado retornado é False
. No entanto, quando o mesmo ocorre com os operadores menor, maior, menor ou igual ou maior ou igual, é retornada uma exceção, neste caso, um TypeError
. Caso a exceção não seja resolvida, o programa não roda.
Objetos do mesmo tipo normalmente são comparáveis, e objetos numéricos como int
e float
também são comparáveis entre si. No entanto, é preciso se atentar aos problemas de precisão de float
, como explicado anteriormente, onde, por exemplo, 0.1 + 0.1 + 0.1 == 0.3 retorna False
.
Logo, quando utilizando float
, não devemos utilizar o comparador de igualdade.
O que é necessário para que dois objetos sejam considerados iguais?
Tudo em Python é um objeto, tal como 1 é um objeto int
e 1.0 é um objeto float
. Temos que 1 e 1.0 não são o mesmo objeto, mas possuem o mesmo valor.
Para checar se dois objetos são o mesmo, utilizamos a palavra-chave is
, enquanto para verificar se dois objetos (compatíveis) possuem o mesmo valor utilizamos o comparador de igualdade ==
.
Sempre utilizamos o comparador de igualdade para números, pois em Python há um bug no qual o número 1 é armazenado no mesmo objeto, mesmo que sejam criadas variáveis diferentes.
Por exemplo:
- Se a = 1 e b = 1, temos que
a == b
retornaTrue
ea is b
também retornaTrue
- Se d = 500 e e = 500, temos que
d == e
retornaTrue
ed is e
retornaFalse
De fato, o operador is
está apenas interessado no endereço da memória dos objetos, e por isso é conhecido como comparador de identidade.
O operador de igualdade, assim como o operador de soma aritmética, é implementado pelo tipo, ou seja, pode ser definido de acordo com tipos customizáveis utilizando o método dunder __eq__
.
Outros operadores de comparação
Temos os operadores de afiliação in
e not in
, que funcionam junto com as coleções para determinar se um objeto está incluso em determinada coleção. Para isso, ele realiza uma comparação de igualdade, e não de identidade.
Por exemplo:
s = {1, 2, 3.14, True, 5.1}
# 1 in s retorna True
# 10 in s retorna False
# 10 not in s retorna True
Operadores lógicos
Em álgebra booleana, temos apenas dois valores possíveis: True
e False
(verdadeiro ou falso). Temos três operadores básicos: e, ou, não (and
, or
, not
).
Pela sintaxe de Python, not
é um operador unário, por exemplo not True
e not (a < b)
. Já and
e or
são operadores binários, por exemplo True or False
, True and False
e (enabled == True) and (withdraw <= balance)
.
Tabelas-verdade
O operador not
simplesmente inverte os valores.
O operador and
só retorna verdadeiro se tanto a e b são verdadeiros, do contrário é falso.
O operador or
retorna falso se tanto a e b são falsos, do contrário é verdadeiro.
Se a é falso, a e b será sempre falso; se a é verdadeiro, a ou b será sempre verdadeiro. Saber disso pode otimizar processos ao realizar uma avaliação de curto-circuito no código, ou seja, interromper ele prematuramente para poupar recursos.