Fundamentos Python II
Tipos de Controle de Flow
Controle de Fluxo (Control Flow) e Escopo
o Controle de Fluxo (Control Flow) de um programa determina a forma como o código será executado, existem 4 tipos essenciais, isto é, destes quatro surgem variações
- Sequencial: o código é executado de forma sequencial, cima a baixo
-
Decisivo: o código é executado baseado em uma decisão,
ifstatements -
Repetitivo: o código é executado em loop,
whileeforloops - Funcional: o código é dividido e executado em forma de funções (veremos mais a frente)
Sequencial
é essencialmente a forma como viemos escrevendo código até agora, os comandos são providos e executados de forma sequencial
1print("executado primeiro")
2print("executado depois")
3print("executado por ultimo")
4
Decisivo
O decisivo é usado quando queremos executar um codigo quando algo for verdadeiro ou falso, ou seja, quando tomamos uma decisão
em Python nós duas formas de tomar decisões, match-case e cadeias de elifs
começarei pelas cadeias de elifs pois elas constituem a lógica básica de um if statement
a sintaxe de um if statement é a seguinte:
1if (condição):
2 {codigo}
3
irei desestruturar o código e explicar cada parte:
if -> é a keyword, em uma tradução literal significa "se"
(condição) -> é onde você coloca a condição para a execução do codigo e nós usamos os operadores condicionais para isto, os parenteses são opcionais
: (dois pontos) -> eles são ESTRITAMENTE NECESSÁRIOS, pois dão inicio ao escopo do bloco, sem eles o código nunca será executado como esperado
(indentação) -> seu tamanho pode variar dependendo das configurações da IDE, porém ela é o delimitador de escopo, ou seja, uma indentação mal feita torna o codigo impossivel de ser executado
{codigo} -> é o codigo que você deseja executar
vejamos alguns exemplos:
1x, y = 2, 3
2
3if x == 2:
4 print(f"x é igual a {x}")
5
6if x != y:
7 print(f"x não é igual a y")
8
suponhamos que você queria executar um código caso algo seja verdade e outro caso não seja verdade, bom, para isto temos a keyword else, ela não recebe nenhuma condição, pois só serve para executar o código caso o if não se concretize
1x, y = 2, 2
2
3if x == 2:
4 print(f"x é igual a {x}")
5else:
6 print(f"x não é igual a {x}")
7
8if x != y:
9 print(f"x não é igual a {y}")
10else:
11 print(f"x é igual a y")
12
até agora nós conseguimos executar duas funções com base em uma variavel, mas e se quisermos tomar mais decisões?
bom, se raciocinarmos um pouco podemos perceber que basta juntar ifs e elses para termos mais de uma decisão, deste modo:
1x, y = 2, 2
2
3if x == 2:
4 print(f"x é igual a {x}")
5else:
6 if x != y:
7 print(f"x não é igual a {y}")
8 else:
9 print(f"x é igual a y")
10
os desenvolvedores de Python perceberam este padrão e criaram uma keyword especifica para a união de elses e ifs, chamada de elif
ela é usada entre o if e o else em uma especie de cadeia, veja o exemplo:
1x = 1
2
3if x == 1:
4 print(f"x é igual a {x}")
5elif x == 2:
6 print(f"x é igual a {x}")
7elif x == 3:
8 print(f"x é igual a {x}")
9elif x == 4:
10 print(f"x é igual a {x}")
11else:
12 print(f"x é igual a {x}")
13
em cadeias de elifs nós podemos ter quantos elif forem necessários, porém, devemos ter APENAS um if no inicio e um else no fim para termos a cadeia.
Match-Case
o match-case foi introduzido no Python 3.10, essencialmente ele introduz a correspondencia de padrões, essencialmente, você compara um caso a um padrão desejavel de tokens.
a sintaxe de um match-case é a seguinte:
1match (variavel): 2 case (opção): 3 {codigo} 4 case _: 5 {codigo} 6
match -> keyword
(variavel) -> variavel usada na cadeia
case -> keyword
(opção) -> valor esperado, podemos ter quantos case (opção) quisermos
case _ -> valor universal, equivale ao else nas cadeias de elifs, executado caso todos os outros sejam falso
1# pessoas = ['João'] # Retorna: 'Apenas um valor: João'
2pessoas = [
3 "João",
4 "Mateus",
5 "Caio",
6 "Nicolas",
7 "Ivan",
8] # Retorna: Mais que tres itens: João, Mateus, Caio, e também: ['Nicolas', 'Ivan']
9
10match pessoas:
11 case [a]:
12 print(f"Apenas um valor: {a}")
13 case [a, b]:
14 print(f"Dois itens: {a}, {b}")
15 case [a, b, c]:
16 print(f"Três itens: {a}, {b}, e {c}")
17 case [a, b, c, *rest]:
18 print(f"Mais que três itens: {a}, {b}, {c}, e também: {rest}")
19 case _:
20 print("Valor incorreto")
21
Saida
Mais que três itens: João, Mateus, Caio, e também: ['Nicolas', 'Ivan']
NOTA: Você pode tentar descomentar a linha 1 e comentar a linha 2 para testar outras combinações
então qual a diferença entre cadeias de elif e match-case?
veja bem, o match-case serve para padrões estruturados de código, enquanto as cadeias de elif servem para tomar decisões, veja um exemplo:
NOTA: Você pode usar o match-case para tomar decisões baseadas em uma única variavel, mas se é recomendado o uso de cadeias de
elif.
1amigos = ["amigo1", "amigo2", "amigo3"]
2
3match amigos:
4 case [a]:
5 print(f"Você tem apenas um amigo: {a}")
6 case [a, b]:
7 print(f"Você tem dois amigos: {a}, {b}")
8 case [a, b, c]:
9 print(f"Você tem três amigos: {a}, {b}, e {c}")
10 case [a, b, c, *rest]:
11 print(f"Você tem mais que três amigos: {a}, {b}, {c}, e também: {rest}")
12 case _:
13 print("Valor incorreto")
14
15
16if amigos[1] == "amigo2":
17 print("O nome do seu segundo amigo é 'amigo2'")
18elif amigos[1] == "amigo88":
19 print("O nome do seu segundo amigo é 'amigo88'")
20else:
21 print("Seu amigo tem nome?")
22
Saida
Você tem três amigos: amigo1, amigo2, e amigo3 O nome do seu segundo amigo é 'amigo2'
Repetitivo (Recursivo)
Flows repetitivos, como o nome sugere servem para repetir linhas de código, em Python nós temos duas keywords para isto, while e for
o while serve para executar um codigo enquanto uma condição for verdadeira
veja a sintaxe de um while loop:
1while (condição):
2 {codigo}
3
a unica diferença entre a estrutura basica de um if, é a keyword e o fato de termos apenas ela, não existem "cadeias de while"
obs: os parenteses na declaração são opcionais, tanto no while, quanto no for
veja o exemplo:
1x = 0
2
3while x < 10:
4 print(f"valor de x: {x}")
5 x += 1
6
enquanto x for menor que 10, exiba o valor de x, e incremente x por 1
já o for serve para repetir em um "raio", de x a y, veja a sintaxe de um for loop:
1for {index} in (raio):
2 {codigo}
3
for -> keyword
{index} -> variavel usada para o loop
in -> operador membro, "em tal grupo"
(raio) -> item iteravel
veja o exemplo:
1for i in range(10):
2 print(i)
3
para i em um raio de 10 digitos, exiba i
essencialmente o for loop é um while delimitado, e ele é especialmente usado para iterar sob tipos compostos
1lista_legal = ["a", "b", "c", "d"]
2
3for i in lista_legal:
4 print(i)
5
Performance em Loops
suponhamos o seguinte, você quer exibir os numeros entre 1 e 100000000 usando um loop, mas quer fazer isso de forma eficiente, como você fará?
para demonstrar o ponto, irei temporizar o código e usar sintaxes avançadas, como funções, módulos e type-hinting.
While Loops
Usando while loops:
1from timeit import timeit
2
3
4def pure_while(n: int = 100000000) -> int:
5 i, x = 0, 0
6 while i < n:
7 x += i
8 i += 1
9 return x
10
11
12print(f"While Loop:\t{timeit(pure_while, number=1)} segundos")
13
Saida
While Loop: 12.82263323100051 segundos
For Loops
Usando for loops:
1from timeit import timeit
2
3
4def pure_for(n: int = 100000000) -> int:
5 x = 0
6 for i in range(n):
7 x += i
8 return x
9
10
11print(f"For Loop:\t{timeit(pure_for, number=1)} segundos")
12
Saida
For Loop: 8.726445832999161 segundos
como você pode perceber o for loop é consideravelmente mais rápido, isso se dá pelo fato das comparações serem executadas em C, a linguagem cujo a qual Python é baseado, enquanto no while as comparações são executadas diretamente em Python, que é substencialmente mais lento que C.
de todo modo, recomendo que priorize for loops ao invés de while loops sempre que possivel, e só ultilize while quando estritamente necessário.
Escopo
Escopo é uma caracteristica de muitas linguagens de programação, ele exerce a função de separação do código em niveis, relativos entre si.
Por exemplo:
A {
B {
C {
}
}
}
Aqui nós temos 4 escopos:
- Global
- A
- B
- C
O escopo global é o escopo do arquivo, sem delimitações.
Quando eu digo que eles são relativos entre si, eu quero dizer que escopos mais acima influenciam escopos abaixo, porém o oposto não ocorre.
ou seja, seguindo nosso exemplo, qualquer codigo posto no escopo de A pode entrar nos escopos de B e C, mas os codigos postos em B ou C não conseguem sair para o escopo de A.
Em Python nós usamos : (dois-pontos) e a indentação (geralmente de 4 espaços, ou 1 tab) para limitar o escopo.
Desestruturação (Packing & Unpacking)
A desestruturação em Python consiste em assimilar aos valores de um iterável a variaveis.
por exemplo se voce lembra da aula sobre variaveis certamente lembrará que podemos associar multiplas variaveis a multiplos valores em uma unica linha
O que eu não lhe contei naquele momento é que isto é um exemplo de unpacking, nós estamos associando os valores de uma tuple a variaveis correspondentes, pois se você lembra, parenteses são opcionais, então ao fazermos:
1x, y = 1, 2
2
estamos fazendo o mesmo que:
1x, y = (1, 2)
2
ou até mesmo:
1(x, y) = (1, 2)
2
Desestruturação em for loops
a desestruturação em for loops consiste em uma variavel de index e uma variavel de valor
para isso nós usamos a função enumerate()
veja um exemplo:
1lista_amigos = ["amigo1", "amigo2", "amigo3"]
2
3for index, valor in enumerate(lista_amigos):
4 print(f"O {valor} está na posição {index}")
5
a função enumerate() gera uma tuple de 2 valores para cada item do iteravel, um index e o valor propriamente dito
Iteração em dicionários
Nós também podemos Iterar sob dicionarios
Para Iterar sob as chaves basta usar apenas o dicionário
1dicionario_aleatorio = {
2 "Chave A": "Valor 01",
3 "Chave B": "Valor 02",
4 "Chave C": "Valor 03",
5}
6
7for chave in dicionario_aleatorio:
8 print(chave)
9
Para iterar sobre os valores basta usar dict.values()
1dicionario_aleatorio = {
2 "Chave A": "Valor 01",
3 "Chave B": "Valor 02",
4 "Chave C": "Valor 03",
5}
6
7for chave in dicionario_aleatorio.values():
8 print(chave)
9
Para usar ambos, usamos dict.item() e desestruturação
1dicionario_aleatorio = {
2 "Chave A": "Valor 01",
3 "Chave B": "Valor 02",
4 "Chave C": "Valor 03",
5}
6
7for chave, valor in dicionario_aleatorio.items():
8 print(chave, valor)
9
break, continue e else em for loops
break, continue e else, essas keywords tem como objetivo principal alterar o flow de for loops
o break serve para interromper o loop, comumente ultilizamos ele juntamente com um if statement
1for i in range(10):
2 print(i)
3 if i == 6:
4 break
5
o continue já exerce o oposto, ele dá prosseguimento ao loop
1for i in range(10):
2 print(i)
3 if i == 6:
4 print("e vamos continuar")
5 continue
6
e o else é executado ao final do loop apenas quando ele não é parado por um break
1for i in range(10):
2 print(i)
3else:
4 print("não tem breaks então chegamos ao fim")
5
você pode estar se perguntando, qual a ultilidade do else então?
vejamos um exemplo:
1# Vamos checar por numeros pares em uma lista
2
3print("Teste 01")
4for i in [1, 3, 5, 7, 9]:
5 if i % 2 == 0:
6 print("contem um numero par")
7 break
8else:
9 print("não contem um numero par")
10
11print("Teste 02")
12for i in [1, 3, 4, 7, 9]:
13 if i % 2 == 0:
14 print("contem um numero par")
15 break
16else:
17 print("não contem um numero par")
18
Numeros Primos e FizzBuzz
Nós podemos usar for loops para acharmos numeros primos
1x = 12
2y = 11
3for i in range(2, x):
4 if x % i == 0:
5 print(f"{x} não é primo")
6 break
7else:
8 print(f"{x} é primo")
9
10for i in range(2, y):
11 if y % i == 0:
12 print(f"{y} não é primo")
13 break
14else:
15 print(f"{y} é primo")
16
Existe um jogo que se tornou desafio de programação, o nome é FizzBuzz
ele consiste no seguinte:
- Se um numero for divisivel por 3, diga Fizz
- Se um numero for divisivel por 5, diga Buzz
- Se um numero for divisivel por 3 e 5, diga FizzBuzz
podemos usar for loops para isso
1for i in range(1, 21):
2 if i % 3 == 0:
3 print("Fizz")
4 elif i % 5 == 0:
5 print("Buzz")
6 elif (
7 i % 15 == 0
8 ): # 3 x 5 = 15, então qualquer numero divisivel por 15 também é divisivel por 3 e 5
9 print("FizzBuzz")
10 else:
11 print(i)
12
Fatiamento de Listas
"Fatiar" uma lista consiste em exibir apenas uma parte dela, por exemplo:
Ordem Natural
Temos a lista A = [1, 5, 4, 2, 3, 7, 0, 8, 6, 9], e se nós quisermos exibir apenas os numeros 3, 7, 0?
para isso basta colocarmos o A[{index inicial}:{index final+1}]
1# Lembre-se, o index incia em 0, então a ordem de elementos é a seguite:
2# 0, 1, 2, 3, 4, 5, 6, 7, 8, 9
3A = [1, 5, 4, 2, 3, 7, 0, 8, 6, 9]
4
5print(A[4:7])
6
porque {index final+1}? pois o valor será onde a lista acabará, e portanto, não se encontrará incluso, então para incluirmos ele, basta adicionar 1 ao index
Ordem Reversa
Suponhamos que você queira cortar a lista de trás a frente, para isso, basta usar os numeros negativos, exemplo:
1# Ordem Reversa
2# -10, -9, -8, -7, -6, -5, -4, -3, -2, -1
3A = [1, 5, 4, 2, 3, 7, 0, 8, 6, 9]
4
5print(A[-6:-3])
6
Compreensões (Comprehensions)
Compreensões consistem em gerar um novo elemento com base em um tipo de dado composto atráves de ifs, elses e for loops, comumente dizemos ser uma geração dinamica.
Seguimos o seguinte esquema para as compreensões:
- Listas
-
Base:
lista_resultado = ['expr_saida' for 'var' in 'iteravel'] -
Condicional:
lista_resultado = ['expr_saida' for 'var' in 'iteravel' if (condição)]
-
Base:
- Sets
-
Base:
set_resultado = {'expr_saida' for 'var' in 'iteravel'} -
Condicional:
set_resultado = {'expr_saida' for 'var' in 'iteravel' if (condição)}
-
Base:
- Dicionários
-
Base:
dict_resultado = {'chave':'valor' for '(chave, valor)' in 'iteravel'} -
Condicional:
dict_resultado = {'chave':'valor' for '(chave, valor)' in 'iteravel' if ('chave' satisfaz condição)}
-
Base:
Listas
Compreensões de listas consiste em gerar uma nova lista de forma dinamica.
Por exemplo, se você quisesse gerar uma lista com numeros dobrados?
poderiamos usar for loops
1lista_dobrada = []
2for i in range(1, 11):
3 lista_dobrada.append(i * 2)
4
5print(lista_dobrada)
6
Saida
[2, 4, 6, 8, 10, 12, 14, 16, 18, 20]
Porém podemos fazer o mesmo com compreensão de lista, mostrarei primeiro o exemplo e depois explicarei a sintaxe
1lista_dobrada = [i * 2 for i in range(1, 11)]
2print(lista_dobrada)
3
Saida
[2, 4, 6, 8, 10, 12, 14, 16, 18, 20]
Basicamente a lógica esta "ao contrário", no exemplo com for loops estamos dizendo o seguinte:
para cada item 'i' entre 1 e 11, multiplique por 2 e adicione a lista 'lista_dobrada'
e no exemplo de compreensão de lista estamos dizendo:
multiplique 'i' por 2 e adicione a lista 'lista_dobrada' em um raio entre 1 e 11
e tambem tornamos a lógica em uma unica linha, compactando ela, essa prática de tornar o codigo em uma linha se chama 'One Liner', além é claro de termos eliminado a necessidade de inicialização de uma lista vazia.
Listas Condicional
consiste no mesmo acima, porém com uma condição para a geração da lista
Por exemplo, se você quisesse gerar uma lista apenas com numeros pares?
poderiamos usar for loops e ifs
1lista_de_pares = []
2for i in range(1, 11):
3 if i % 2 == 0:
4 lista_de_pares.append(i)
5
6print(lista_de_pares)
7
Saida
[2, 4, 6, 8, 10]
Porém podemos fazer o mesmo com compreensão de lista, mostrarei primeiro o exemplo e depois explicarei a sintaxe
1lista_de_pares = [i for i in range(1, 11) if i % 2 == 0]
2print(lista_de_pares)
3
Saida
[2, 4, 6, 8, 10]
Basicamente a lógica esta "ao contrário", no exemplo com for loops e ifs estamos dizendo o seguinte:
para cada item 'i' entre 1 e 11, se o resto da divisão(modulo) por 2 for igual a 0 adicione a lista 'lista_de_pares'
e no exemplo de compreensão de lista estamos dizendo:
adicione 'i' para a lista 'lista_de_pares' caso o resto da divisão(modulo) por 2 for igual a 0 em um raio entre 1 e 11
Sets
Basicamente a mesma coisa que as compreensões de lista, a unica diferença é que as compreensões estão entre {} e não []
1set_de_pares = {i for i in range(1, 11) if i % 2 == 0}
2print(set_de_pares)
3
Saida
{2, 4, 6, 8, 10}
Dicionários
Extendendo a lógica podemos gerar uma compreensão de dicionários.
Por exemplo, e se quisessemos gerar um dicionário de cubos impares? Com o numero impar e seu respectivo valor ao cubo.
Poderiamos fazer assim:
1res_dict = {}
2
3for i in range(1, 10):
4 if i % 2 != 0:
5 res_dict[i] = i**3
6
7print(res_dict)
8
Saida
{1: 1, 3: 27, 5: 125, 7: 343, 9: 729}
Agora a versão One Liner:
1comp_dict = {i: i**3 for i in range(1, 10) if i % 2 != 0}
2print(comp_dict)
3
Saida
{1: 1, 3: 27, 5: 125, 7: 343, 9: 729}
A função zip()
A função zip() junta elementos de multiplos iteráveis em tuples.
então podemos usar ela para por exemplo, juntar duas listas em um dicionário
1lista_nums = [1, 2, 3, 4, 5]
2lista_chars = ["a", "b", "c", "d", "e"]
3
4zip_dict = {k: v for (k, v) in zip(lista_nums, lista_chars)}
5print(zip_dict)
6
Saida
{1: 'a', 2: 'b', 3: 'c', 4: 'd', 5: 'e'}
A diferença entre a função zip() e a função enumerate() é que a função enumerate() enumera um iterável, enquanto a função zip() junta multiplos iteráveis
Funções
Funções são uma parte essencial da linguagem, nós viemos usando elas varias vezes até aqui, agora está na hora de aprender a definir (criar) suas próprias funções.
O que são funções?
Funções são uma estrutura de código feita pra exercer uma determinada função complexa (vide o nome).
A sintaxe de uma função em Python é a seguinte:
1def {nome}():
2 {corpo da função}
3
-
defé a keyword de definição de uma função em Python -
{nome}é o indentificador da função, usaremos ele para chama-la -
()os parenteses são EXTRITAMENTE necessários, pois eles exercem a função de chamada -
{corpo da função}aqui nós colocamos a lógica da função
eis aqui um exemplo de função:
1def apresentar():
2 print("Olá, eu sou a função apresentar()")
3
4
5apresentar() # Aqui chamamos a função
6
Saida
olá, eu sou a função apresentar()
Argumentos e Parametros
Na função acima, nós apenas a chamamos, mas e se nós quisessemos que ela mostrasse algum nome?
Bom, para isso nós usariamos argumentos e parametros, observe o exemplo:
1def apresentar(nome):
2 print(f"Olá meu nome é {nome}")
3
4
5apresentar("Mirai")
6
Saida
Olá meu nome é Mirai
Nós definimos um argumento e passamos um parametro para esse argumento.
Existe uma pequena confusão entre Argumentos e Parametros de uma função, iniciantes tendem a pensar que são a mesma coisa, mas estão enganados.
Argumento é o espaço reservado na função (no nosso exemplo seria nome) e Parametros são os valores passados a esses argumentos (no nosso exemplo seria "Mirai").
nós tambem podemos colocar multiplos argumentos, basta separa-los por , (virgula) e colocar um numero correspondente de parametros (se nós colocamos 2 argumentos, devemos colocar 2 parametros, etc...).
1def apresentar(nome, idade):
2 print(f"Olá meu nome é {nome} e eu tenho {idade} anos")
3
4
5apresentar("Mirai", 17)
6
Saida
Olá meu nome é Mirai e eu tenho 17 anos
A ordem em que passamos os argumentos deve condizer com os parametros.
Parametros Padrões (Valores Padrões)
Parametros padrões (ou valores padrões) são usados quando nenhum outro argumento é passado ao parametro.
Eles são definidos ao colocar um = (igual) e o valor desejado ao lado do parametro.
Veja o exemplo:
1def apresentar(nome="Python"):
2 print(f"Olá meu nome é {nome}")
3
4
5apresentar() # Nenhum argumento passado
6apresentar("Mirai")
7
Saida
Olá meu nome é Python Olá meu nome é Mirai
Argumentos Chave (Nomeados)
Argumentos Chave (ou nomeados) são usados ao chamar uma função, essencialmente, você especifica qual argumento irá usar qual parametro.
veja o exemplo:
1def apresentar(nome, idade):
2 print(f"Olá meu nome é {nome} e eu tenho {idade} anos")
3
4
5apresentar(idade=17, nome="Mirai")
6
Saida
Olá meu nome é Mirai e eu tenho 17 anos
essa forma de associação permite que não sigamos a ordem de argumentos na definição da função, e tambem nos permite "pular" parametros (esse parametros devem ter valores padrões), veja um outro exemplo:
1def apresentar(nome="cool_name", idade=99, local="place"):
2 print(f"Olá meu nome é {nome} e eu tenho {idade} anos e moro em {local}")
3
4
5apresentar("Mirai", local="casa")
6
Saida
Olá meu nome é Mirai e eu tenho 99 anos e moro em casa
*args, **kwargs, * (Keyword Only Arguments) e / (Positional Only Arguments)
esses são parametros especiais, os *args e **kwargs nos permitem ter um numero indeterminado de argumentos.
o *args nos permite pegar indeterminados parametros passados de forma direta.
1def multi_print(*args):
2 for i in args:
3 print(i)
4
5
6multi_print("ye", 20, "3", 4, [5])
7
Saida
ye 20 3 4 [5]
já o **kwargs nos permite pegar indeterminados parametros-chave.
1def multi_key_print(**kwargs):
2 for k, v in kwargs.items():
3 print("%s, %s" % (k, v))
4
5
6multi_key_print(a="yee", b="bbb", c="cccc")
7
Saida
a, yee b, bbb c, cccc
Nós também podemos usar os dois juntos, pois eles são independentes entre si, a unica convenção é que definimos *args primeiro e depois **kwargs.
1def funcao_legal(*args, **kwargs):
2 print(f"valores: {args}")
3 print(f"valores com chave: {kwargs}")
4
5
6funcao_legal("ye", 20, "3", 4, [5], a="yee", b="bbb", c="cccc")
7
Saida
valores: ('ye', 20, '3', 4, [5]) valores com chave: {'a': 'yee', 'b': 'bbb', 'c': 'cccc'}
O / (Positional Only Arguments) nos limita a ter apenas valores posicionais, isto é, aqueles que não são parametros-chave.
1def funcao_posicional(parametro_1, parametro_2, parametro_3, /):
2 print(f"parametro_1: {parametro_1}")
3 print(f"parametro_2: {parametro_2}")
4 print(f"parametro_3: {parametro_3}")
5
6
7funcao_posicional("Valor 1", "Valor 2", "Valor 3")
8
Saida
parametro_1: Valor 1 parametro_2: Valor 2 parametro_3: Valor 3
se nós tentarmos associar algum parametro antes do separador / usando parametro-chave, teremos um erro, pois este separador especifica que todos os parametros antes dele devem ser posicionais.
1def funcao_posicional(parametro_1, parametro_2, parametro_3, /):
2 print(f"parametro_1: {parametro_1}")
3 print(f"parametro_2: {parametro_2}")
4 print(f"parametro_3: {parametro_3}")
5
6funcao_posicional(parametro_1='Valor 1', 'Valor 2', 'Valor 3') # Retornará um erro
7
Saida
Input In [21] funcao_posicional(parametro_1='Valor 1', 'Valor 2', 'Valor 3') # Retornará um erro ^ SyntaxError: positional argument follows keyword argument
O * (Keyword Only Arguments) especifica que todos os argumentos após ele devem ser parametro-chave.
ele não pode ser o primeiro parametro, então devemos ter um primeiro parametro obrigatório.
1def funcao_parametro_chave(
2 parametro_obrigatorio, *, parametro_1="", parametro_2="", parametro_3=""
3):
4 print(f"parametro_obrigatorio: {parametro_obrigatorio}")
5 print(f"parametro_1: {parametro_1}")
6 print(f"parametro_2: {parametro_2}")
7 print(f"parametro_3: {parametro_3}")
8
9
10funcao_parametro_chave(
11 "valor obrigatorio",
12 parametro_1="Valor 1",
13 parametro_2="Valor 2",
14 parametro_3="Valor 3",
15)
16
Saida
parametro_obrigatorio: valor obrigatorio parametro_1: Valor 1 parametro_2: Valor 2 parametro_3: Valor 3
se tentarmos passar apenas argumentos posicionais, teremos um erro
1def funcao_parametro_chave(
2 parametro_obrigatorio, *, parametro_1="", parametro_2="", parametro_3=""
3):
4 print(f"parametro_obrigatorio: {parametro_obrigatorio}")
5 print(f"parametro_1: {parametro_1}")
6 print(f"parametro_2: {parametro_2}")
7 print(f"parametro_3: {parametro_3}")
8
9
10funcao_parametro_chave(
11 "valor obrigatorio", "Valor 1", "Valor 2", "Valor 3"
12) # Retornará um erro
13
Saida
--------------------------------------------------------------------------- ``````output TypeError Traceback (most recent call last) ``````output /media/Arquivos/Programming/Lessons/PythonLessions/Classes/3-Fundamentos_Python-II.ipynb Cell 94 in <cell line: 7>() <a href='vscode-notebook-cell:/media/Arquivos/Programming/Lessons/PythonLessions/Classes/3-Fundamentos_Python-II.ipynb#Y321sZmlsZQ%3D%3D?line=3'>4</a> print(f"parametro_2: {parametro_2}") <a href='vscode-notebook-cell:/media/Arquivos/Programming/Lessons/PythonLessions/Classes/3-Fundamentos_Python-II.ipynb#Y321sZmlsZQ%3D%3D?line=4'>5</a> print(f"parametro_3: {parametro_3}") ----> <a href='vscode-notebook-cell:/media/Arquivos/Programming/Lessons/PythonLessions/Classes/3-Fundamentos_Python-II.ipynb#Y321sZmlsZQ%3D%3D?line=6'>7</a> funcao_parametro_chave('valor obrigatorio', 'Valor 1', 'Valor 2', 'Valor 3') ``````output TypeError: funcao_parametro_chave() takes 1 positional argument but 4 were given
Se existe uma precedencia, argumentos posicionais devem sempre vir antes de argumentos parametro-chave, caso contrário resultará em um erro, por isso definimos *args, **kwargs e não **kwargs, *args.
Com relação a performance, os argumentos posicionais são relativamente mais rapidos
1from timeit import timeit
2
3
4def func_test(a, b, c):
5 pass
6
7
8print(
9 f'Apenas Posicionais:\t\t{timeit("func_test(1, 2, 3)", number=10, globals={"func_test":func_test}):.8f} segundos'
10)
11print(
12 f'Apenas Parametros-Chave:\t{timeit("func_test(a=1, b=2, c=3)", number=10, globals={"func_test":func_test}):.8f} segundos'
13)
14
Saida
Apenas Posicionais: 0.00000229 segundos Apenas Parametros-Chave: 0.00000446 segundos
DocStrings
Documentation Strings (Strings de Documentação), comumente abreviadas para DocStrings são strings especiais feitas para associar documentação ao codigo.
ela é usada em diversos locais, desde funções, classes e até em projetos.
sua declaração é simples, o local que ela é declarada é que a diferencia, para fazer uma DocString basta por uma multi-line string abaixo da declaração de funções ou classes.
nós podemos acessa-la através do metodo especial __doc__
1def funcao_legal(*args, **kwargs):
2 """Função legal que mostra valores legais"""
3 print(f"valores: {args}")
4 print(f"valores com chave: {kwargs}")
5
6
7funcao_legal("ye", 20, "3", 4, [5], a="yee", b="bbb", c="cccc")
8print(funcao_legal.__doc__)
9
Saida
valores: ('ye', 20, '3', 4, [5]) valores com chave: {'a': 'yee', 'b': 'bbb', 'c': 'cccc'} Função legal que mostra valores legais
Em projetos, se é recomendado uma DocString no topo do projeto (se for o caso, abaixo do SheBang) descrevendo o propósito daquele arquivo/programa.
Eis aqui um exemplo que eu fiz quando estava aprendendo Python.
1"""Apenas um arquivo que faz Nodes aleatorios e organiza eles em uma arvore binária.
2Temporiza, conta quantas vezes fez e salva tudo para um arquivo."""
3
4# {codigo}...
5
return
a keyword return serve para retornar um valor de dentro da função e terminar a função.
ao retornar um valor, nós podemos associa-lo a uma variavel.
1def apresentar(nome="cool_name", idade=99, local="place"):
2 return f"Olá meu nome é {nome} e eu tenho {idade} anos e moro em {local}"
3
4
5texto = apresentar("Mirai", local="casa")
6print(texto)
7
Saida
Olá meu nome é Mirai e eu tenho 99 anos e moro em casa
Funções lambda
as funções lambda são funções em uma linha e também nos possibilitam fazer funções anonimas.
de acordo com documentação do Python (Tradução livre)
Ao contrário das formas de lambdas em outras linguagens, onde elas adicionam funcionalidade, as lambdas de Python são apenas uma notação simplificada se você for preguiçoso demais pra definir uma função.
sua sintaxe é:
1lambda {argumentos} : {expressão}
2
escreverei uma função usando def e a mesma função usando lambda para demonstrar.
1def add_num(x, y):
2 return x + y
3
4
5print(add_num(3, 4))
6
7add_num_ = lambda x, y: x + y
8print(add_num_(5, 6))
9
Saida
7 11
Funções anonimas são funções que não tem um indentificado ou um nome, são comumente executadas em sua declaração.
veja a função anterior, porém anonima
1print((lambda x, y: x + y)(2, 3))
2
Saida
5
Eu sei que os exemplos aqui são bobos, mas em situações mais complexas, elas se fazem necessárias, tendo em vista que elas podem ir além do que simples operadores aritméticos.
Funções de Primeira Classe e Alta Ordem em Python
Esses conceitos são referentes a programação funcional, essencialmente ambos se referem a tratar funções como valores.
Funções de Primeira Classe
Toda função em Python é uma função de primeira classe. Isso significa que nós podemos tratar qualquer função como se fosse uma variavel, ou seja, podemos reassociar e passar como argumento.
1def greeting(nome):
2 return f"Ola {nome}"
3
4
5gratificar = greeting
6print(gratificar("Mirai"))
7
Saida
Ola Mirai
No exemplo acima nós associamos a uma variavel e passamos como argumento para a função print()
Nós também podemos escrever uma função que recebe um argumento e passa-lo através de outra função, veja o exemplo:
1def func_sup(func, name): # Tem o mesmo parametro
2 resp = func(
3 name
4 ) # Passamos o argumento provido para a função passada como argumento
5 up_name = resp.upper()
6 return up_name
7
8
9def hello_name(nome): # Tem o parametro 'nome'
10 return f"Ola {nome}"
11
12
13print(
14 func_sup(hello_name, "Mirai")
15) # Passamos a função hello_name e o argumento "Mirai"
16
Saida
OLA MIRAI
Funções de Alta Ordem
As funções de alta ordem são bem parecidas com as de primeira classe, todavia, elas tem a especificidade de retornar uma função.
veja o exemplo:
1def uppr_func(func):
2 def intrn_func(name):
3 answ = func(name)
4 return answ.upper()
5
6 return intrn_func # retornamos a função interna 'intrn_func'
7
8
9def h_name(name):
10 return f"Ola {name}"
11
12
13print(uppr_func(h_name)("Mirai"))
14
Saida
OLA MIRAI
nós podemos usar funções lambda para termos funções de alta ordem, veja o exemplo:
1func_alta_ord = lambda x, func: x + func(x)
2print(func_alta_ord(2, lambda x: x**2))
3
Saida
6
para facilitar o entendimento eu irie formatar essas duas linhas (substitiuindo valores) de uma forma a representar a logica.
-
1 func_alta_ord = lambda x, func: x + func(x) 2 print(func_alta_ord(2, lambda y: y ** 2)) 3 -
1 func_alta_ord = lambda x, func: x + func(x) 2 func_alta_ord = lambda x, 3 lambda y: y ** 2: 4 x + func(x) 5 -
1 func_alta_ord = lambda x: 2 x + lambda x: x ** 2 3 -
1 print(func_alta_ord(2, lambda y: y ** 2)) 2 3 func_alta_ord = lambda x: 4 2 + lambda x: 2 ** 2 5
global e nonlocal
Essas keywords são referentes ao escopo, a global é usada quando queremos transformar uma variavel local em global, e a nonlocal serve para usar variaveis de escopos superiores quando temos funções aglomeradas (nested functions)
global
Veja um exemplo:
1x = "Escopo Global"
2
3
4def func_legal_var_local():
5 x = "Escopo Local"
6 print(x)
7
8
9def func_legal_var_global():
10 global x
11 print(x)
12
13
14def alterar_global():
15 global x
16 x = "Alteramos a variavel global"
17
18
19print(x) # Escopo Global
20func_legal_var_local() # Escopo Local
21func_legal_var_global() # Escopo Global
22alterar_global()
23print(x) # Alteramos a variavel global
24
Saida
Escopo Global Escopo Local Escopo Global Alteramos a variavel global
nonlocal
Veja um exemplo:
1def func_extern1():
2 x = "Func externa 1"
3
4 def func_intern1():
5 x = "Func interna 1"
6 print(x)
7
8 print(x)
9 func_intern1()
10
11
12def func_extern2():
13 x = "Func externa 2"
14
15 def func_intern2():
16 nonlocal x
17 print(x)
18
19 print(x)
20 func_intern2()
21
22
23def func_extern3():
24 x = "Func externa 2"
25
26 def func_intern3():
27 nonlocal x
28 x = "Alteramos a variavel da Func Externa 3"
29 print(x)
30
31 print(x)
32 func_intern3()
33
34
35func_extern1()
36func_extern2()
37func_extern3()
38
Saida
Func externa 1 Func interna 1 Func externa 2 Func externa 2 Func externa 2 Alteramos a variavel da Func Externa 3
Sombra de variaveis (Variable Shadowing)
Variable Shadowing ocorre quando uma variavel de um escopo interno tem o mesmo nome que uma variavel de um escopo externo, isso pode levar a erros e a comportamentos não esperados no código então deve ser evitado. O recomendado é declarar variaveis internas com outros nomes, ou melhor ainda, não usar variaveis globais.
1x = 0
2
3
4def externa():
5 x = 1
6
7 def interna():
8 x = 2
9 print("interna:", x)
10
11 def interna_nao_associada():
12 print("interna não associada:", x)
13
14 interna()
15 interna_nao_associada()
16 print("externa:", x)
17
18
19externa()
20print("global:", x)
21
Saida
interna: 2 interna não associada: 1 externa: 1 global: 0
perceba que mesmo não especificando a variavel x ainda sim temos um resultado, pois a mesma usou a variavel de escopo externo
Operadores
Em Python, operadores são simbolos (ou "sintaxes") especiais que informam que uma operação deve ser feita.
Nesse tópico eu irei dar uma visão mais detalhada dos operadores, já vimos alguns como os aritméticos e os lógicos (apenas as keywords), eis aqui uma lista com os grupos de operadores:
- Aritméticos (irei mostrar o ultimo)
- Lógicos (irei mostrar o ultimo)
- Comparativos
- Bitwise
- Associativos
- Identidade
- Membros
Aritméticos
Resta apenas vermos o multiplicador de matrizes (@), para esse operador devemos usar um módulo conhecido como NumPy, você verá mais sobre ele no futuro.
Esse operador multiplica os indices de duas matrizes entre si.
1from numpy import array
2
3matriz_1 = [[1, 2], [3, 4]]
4
5matriz_2 = [[5, 6], [7, 8]]
6
7produto_das_matrizes = array(matriz_1) @ array(matriz_2)
8
9print(produto_das_matrizes)
10
Saida
[[19 22] [43 50]]
ele é o equivalente ao seguinte codigo em Python:
1A = [[1, 2], [3, 4]]
2
3
4B = [[5, 6], [7, 8]]
5
6N, M, P = len(A), len(A[0]), len(B[0])
7
8result = []
9for i in range(N):
10 row = [0] * P
11 result.append(row)
12
13for i in range(N):
14 for j in range(P):
15 for k in range(M):
16 result[i][j] += A[i][k] * B[k][j]
17
18print(result)
19
Saida
[[19, 22], [43, 50]]
Lógicos
Nós já vimos as funções de 3 dos 4 operadores lógicos (sim eu omiti um operador no momento pois eu ainda não havia explicado conceitos necessários para seu entendimento) eu estou falando do "Operador Ternário" (tambem chamado de operador condicional).
ele é essencialmente um if porém em uma linha, sua sintaxe é:
{se verdadeiro} if {expressão} else {se falso}
ele é chamado de ternário pois tem 3 partes ao invés de 2 como a maioria dos outros operadores.
veja um exemplo:
1a, b = 1, 2
2
3c = a if a < b else b # c = a
4d = a if a > b else b # d = b
5
6print("C: %d\tD: %d" % (c, d))
7
Saida
C: 1 D: 2
Comparativos
os comparativos são bastante usados nas estruturas de controle de flow, eles são os seguintes:
-
maior que
> -
menor que
< -
é igual
== -
diferente
!= -
maior ou igual a
>= -
menor ou igual a
<=
Seus nomes são auto-explicativos, então irei exemplificar apenas casos complexos.
Por exemplo, podemos usar eles para definir um "raio" de ação junto com os operadores logicos.
1for i in range(10):
2 if i > 3 and i < 7:
3 print(f"o valor de i é {i}")
4
Saida
o valor de i é 4 o valor de i é 5 o valor de i é 6
Indentidade
eles são simples, eles trabalham comparando o id na memória, util quando iniciarmos multi-threading.
são apenas 2:
-
is -
is not
veja a diferença entre eles e os operadores comparativos == e !=:
1a = ["apple", "banana"]
2b = ["apple", "banana"]
3c = a
4
5print(f"A é igual a B ? {a == b}")
6print(f"A é B ? {a is b}")
7print(f"A é C ? {a is c}")
8
9print(f"A é diferente a B ? {a != b}")
10print(f"A não é B ? {a is not b}")
11print(f"A não é C ? {a is not c}")
12
Saida
A é igual a B ? True A é B ? False A é C ? True A é diferente a B ? False A não é B ? True A não é C ? False
A não é B, pois o endereço deles na memoria é diferente, porém seus conteudos são os mesmos então eles são iguais.
para exemplificar, mostrarei o id dos objetos:
1a = ["apple", "banana"]
2b = ["apple", "banana"]
3c = a
4
5print(f"ID de A: {hex(id(a))}")
6print(f"ID de B: {hex(id(b))}")
7print(f"ID de C: {hex(id(c))}")
8
Saida
ID de A: 0x7faa296d72c0 ID de B: 0x7faa295bd000 ID de C: 0x7faa296d72c0
Se você observar, os endereço de A e B são diferentes, já os endereços de A e C são identicos.
Membro
esses operadores são um pouco simples, eles apenas chegam se x está ou não presente em y
são 2:
-
in -
not in
eles funcionam em iteráveis.
vejam alguns exemplos:
1print("h" in "abcdefghijklmnopqrstuvwxyz")
2print(12 not in [1, 2, 3, 4, 5, 6, 7, 8, 9, 0])
3
Saida
True True
Bitwise
Esses operadores não são muito ultilizados mas suas aplicações são diversas.
Você deve ter uma ideia de como computadores funcionam, certo?
De forma muito simplificada, um computador funciona atráves de uma sequencia de 0 e 1 conhecida como código binário (ou codigo de maquina), esses numeros são chamados de 'bits', e são neles que esses operadores funcionam.
eis aqui a lista:
-
AND (&) -
OR (|) -
NOT (~) -
XOR (^)(exclusive or) -
RightShift (>>) -
LeftShift (<<)
antes de irmos aos exemplos eu irei explicar como binário funciona.
Se você lembra das aulas de algebra na escola certamente sabe que nós usamos sistemas numéricos para performar nossos calculos, hoje em dia nós usamos a notação posicional, onde cada algarismo depende da sua posição relativa na composição do numero e o valor do numero é a soma de cada algarismo que o compõe.
Atualmente nós usamos um sistema de base 10, usamos 10 algarismos na composição de nossos numeros e ao avançarmos uma casa significa o mesmo que multiplicar por 10.
1 = 1 x 10⁰, 10 = 1 x 10¹, 100 = 1 x 10²... e por ai vai.
Os computadores contudo, operam em base 2, ou seja, contém apenas dois algarismos, 0 e 1, ou seja, as casas representam potencias de 2.
1 = 1 x 2⁰, 2 = 1 x 2¹, 4 = 1 x 2²... e por ai vai.
em notação binária, 1 significa que estamos usando o valor daquela 'casa' e 0 não (da mesma forma que se escrevermos 404, não estamos usando as dezenas)
irei dispor de uma tabela com algumas casas e seus valores para facilitar o entendimento.
| 2⁷ | 2⁶ | 2⁵ | 2⁴ | 2³ | 2² | 2¹ | 2⁰ | Resultado |
|---|---|---|---|---|---|---|---|---|
| 128 | 64 | 32 | 16 | 8 | 4 | 2 | 1 | Decimal |
| ==== | ==== | ==== | ==== | ==== | ==== | ==== | ==== | =================== |
| 0 | 0 | 0 | 0 | 0 | 1 | 0 | 0 | 4 |
| 0 | 0 | 1 | 0 | 1 | 0 | 0 | 0 | 40 |
| 0 | 1 | 0 | 0 | 0 | 0 | 1 | 1 | 67 |
| 0 | 0 | 0 | 0 | 0 | 0 | 0 | 0 | 0 |
| 1 | 1 | 1 | 1 | 1 | 1 | 1 | 1 | 255 |
Os numeros binários crescem da direita para a esquerda, começando pelo bit menos significante
Espero que tenha ficado minimamente claro, e que agora você tenha uma noção básica de binário vamos aos operadores.
AND
o AND performa a conjunção lógica, basicamente ele só retorna 1 se ambos os bits forem 1.

| Variaveis | 2⁷ | 2⁶ | 2⁵ | 2⁴ | 2³ | 2² | 2¹ | 2⁰ | Resultado |
|---|---|---|---|---|---|---|---|---|---|
| 128 | 64 | 32 | 16 | 8 | 4 | 2 | 1 | Decimal | |
| ======== | ==== | ==== | ==== | ==== | ==== | ==== | ==== | ==== | ======== |
| A | 1 | 0 | 0 | 1 | 1 | 1 | 0 | 0 | 156 |
| B | 0 | 0 | 1 | 1 | 0 | 1 | 0 | 0 | 52 |
| A & B | 0 | 0 | 0 | 1 | 0 | 1 | 0 | 0 | 20 |
para exemplificar, veja este mesmo exemplo em Python:
1a = 156
2b = 52
3print(f"A ({a}) = {bin(a)}")
4print(f"B ({b}) = {bin(b)}")
5print(f"A & B ({a & b}) = {bin(a & b)}")
6
Saida
A (156) = 0b10011100 B (52) = 0b110100 A & B (20) = 0b10100
o prefixo 0b é uma conveção em representação de bases em programação, usamos 0b para binário, 0x para hexadecimal e 0o para octeto
OR
o OR performa a disjunção lógica, basicamente ele retorna 1 caso um ou todos os bits sejam 1 (ou se preferir, só retorna 0 se ambos forem iguais, essencialmente o oposto do AND)

| Variaveis | 2⁷ | 2⁶ | 2⁵ | 2⁴ | 2³ | 2² | 2¹ | 2⁰ | Resultado |
|---|---|---|---|---|---|---|---|---|---|
| 128 | 64 | 32 | 16 | 8 | 4 | 2 | 1 | Decimal | |
| ======== | ==== | ==== | ==== | ==== | ==== | ==== | ==== | ==== | ======== |
| A | 1 | 0 | 0 | 1 | 1 | 1 | 0 | 0 | 156 |
| B | 0 | 0 | 1 | 1 | 0 | 1 | 0 | 0 | 52 |
| A | B | 1 | 0 | 1 | 1 | 1 | 1 | 0 | 0 | 188 |
para exemplificar, veja este mesmo exemplo em Python:
1a = 156
2b = 52
3print(f"A ({a}) = {bin(a)}")
4print(f"B ({b}) = {bin(b)}")
5print(f"A | B ({a | b}) = {bin(a | b)}")
6
Saida
A (156) = 0b10011100 B (52) = 0b110100 A | B (188) = 0b10111100
XOR
O XOR performa a Disjunção Exclusiva, essencialmente, ele só retorna 1 se os bits forem opostos.

| Variaveis | 2⁷ | 2⁶ | 2⁵ | 2⁴ | 2³ | 2² | 2¹ | 2⁰ | Resultado |
|---|---|---|---|---|---|---|---|---|---|
| 128 | 64 | 32 | 16 | 8 | 4 | 2 | 1 | Decimal | |
| ======== | ==== | ==== | ==== | ==== | ==== | ==== | ==== | ==== | ======== |
| A | 1 | 0 | 0 | 1 | 1 | 1 | 0 | 0 | 156 |
| B | 0 | 0 | 1 | 1 | 0 | 1 | 0 | 0 | 52 |
| A ^ B | 1 | 0 | 1 | 0 | 1 | 0 | 0 | 0 | 168 |
para exemplificar, veja este mesmo exemplo em Python:
1a = 156
2b = 52
3print(f"A ({a}) = {bin(a)}")
4print(f"B ({b}) = {bin(b)}")
5print(f"A ^ B ({a ^ b}) = {bin(a ^ b)}")
6
Saida
A (156) = 0b10011100 B (52) = 0b110100 A ^ B (168) = 0b10101000
NOT
O NOT performa a Negação Lógica, basiamente, ele inverte os bits, ele recebe só um argumento, tornando ele o unico operador unário.

| Variaveis | 2⁷ | 2⁶ | 2⁵ | 2⁴ | 2³ | 2² | 2¹ | 2⁰ | Resultado |
|---|---|---|---|---|---|---|---|---|---|
| 128 | 64 | 32 | 16 | 8 | 4 | 2 | 1 | Decimal | |
| ======== | ==== | ==== | ==== | ==== | ==== | ==== | ==== | ==== | ======== |
| A | 1 | 0 | 0 | 1 | 1 | 1 | 0 | 0 | 156 |
| ~ A | 0 | 1 | 1 | 0 | 0 | 0 | 1 | 1 | 99 |
Esses exemplos levam em conta que você está trabalhando com numeros sem bit de sinal, infelizmente o Python não suporta eles de forma nativa, ou seja, ~156 é o equivalente a -157 e não a 99 pois o ultimo (o mais a esquerda) bit é o bit de sinal.
se quisermos ter 99 como resultado, devemos usar o AND junto com o valor maximo das casas para remover esse bit.
para exemplificar, veja este mesmo exemplo em Python:
1a = 156
2print(f"A ({a}) = {bin(a)}")
3print(f"Com Bit de Sinal: A ~ ({~a}) = {bin(~a)}")
4print(f"Sem Bit de Sinal: A ~ ({~a & 255}) = {bin(~a & 255)}")
5
Saida
A (156) = 0b10011100 Com Bit de Sinal: A ~ (-157) = -0b10011101 Sem Bit de Sinal: A ~ (99) = 0b1100011
LeftShift
o operador de LeftShift move/desvia os bits para a esquerda preenchendo o espaço restante com 0.
você especifica o numero de casas para desviar a esquerda do operador, e desviar por 1 casa é o equivalente a multiplicar por uma potencia de 2 equivalente ao numero de casas movidas.

| Variaveis | 2⁷ | 2⁶ | 2⁵ | 2⁴ | 2³ | 2² | 2¹ | 2⁰ | Resultado |
|---|---|---|---|---|---|---|---|---|---|
| 128 | 64 | 32 | 16 | 8 | 4 | 2 | 1 | Decimal | |
| ======== | ==== | ==== | ==== | ==== | ==== | ==== | ==== | ==== | ======== |
| A | 0 | 0 | 1 | 0 | 0 | 1 | 1 | 1 | 39 |
| A << 1 | 0 | 1 | 0 | 0 | 1 | 1 | 1 | 0 | 78 |
| A << 2 | 1 | 0 | 0 | 1 | 1 | 1 | 0 | 0 | 156 |
para exemplificar, veja este mesmo exemplo em Python:
1a = 39
2print(f"A ({a}) = {bin(a)}")
3print(f"A << 1 ({a << 1}) = {bin(a << 1)}")
4print(f"A << 2 ({a << 2}) = {bin(a << 2)}")
5
Saida
A (39) = 0b100111 A << (78) = 0b1001110 A << (156) = 0b10011100
RightShift
o operador de RightShift move/desvia os bits para a direita preenchendo o espaço restante com 0.
você especifica o numero de casas para desviar a esquerda do operador, e desviar por 1 casa é o equivalente a dividir por uma potencia de 2 equivalente ao numero de casas movidas.

| Variaveis | 2⁷ | 2⁶ | 2⁵ | 2⁴ | 2³ | 2² | 2¹ | 2⁰ | Resultado |
|---|---|---|---|---|---|---|---|---|---|
| 128 | 64 | 32 | 16 | 8 | 4 | 2 | 1 | Decimal | |
| ======== | ==== | ==== | ==== | ==== | ==== | ==== | ==== | ==== | ======== |
| A | 1 | 0 | 0 | 1 | 1 | 1 | 0 | 0 | 156 |
| A >> 1 | 0 | 1 | 0 | 0 | 1 | 1 | 1 | 0 | 78 |
| A >> 2 | 0 | 0 | 1 | 0 | 0 | 1 | 1 | 1 | 39 |
| A >> 3 | 0 | 0 | 0 | 1 | 0 | 0 | 1 | 1 | 19 |
para exemplificar, veja este mesmo exemplo em Python:
1a = 156
2print(f"A ({a}) = {bin(a)}")
3print(f"A >> 1 ({a >> 1}) = {bin(a >> 1)}")
4print(f"A >> 2 ({a >> 2}) = {bin(a >> 2)}")
5print(f"A >> 3 ({a >> 3}) = {bin(a >> 3)}")
6
Saida
A (156) = 0b10011100 A >> 1 (78) = 0b1001110 A >> 2 (39) = 0b100111 A >> 3 (19) = 0b10011
Bitwise Masking
Masking consiste em mascarar os bits, para fazer operações especificas nesses bits, um exemplo seriam os Shifts Limitados a uma janela de bits.

você usa o AND com o comprimento em bits da janela
1A = 39
2print(f"A << 3 ({A << 3}): {bin(A << 3)}")
3print(f"(A << 3) & 255 ({(A << 3) & 255}): {bin((A << 3) & 255)}")
4
Saida
A << 3 (312): 0b100111000 (A << 3) & 255 (56): 0b111000
Associativos
Os associativos são bem simples, eles funcionam como abreviação dos outros operadores, eles servem para performar uma ação e reassocia a mesma variavel, pois sua ordem de operação é da direita pra esquerda.
eles são os seguntes:
-
Igual
= -
Mais-Igual
+= -
Menos-Igual
-= -
Multiplicação-Igual
*= -
Divisão-Igual
/= -
Modulo-Igual
%= -
Potencia-Igual
**= -
TrueDiv-Igual
//= -
AND-Igual
&=(Bitwise) -
OR-Igual
|=(Bitwise) -
XOR-Igual
^=(Bitwise) -
RightShift-Igual
>>=(Bitwise) -
LeftShift-Igual
<<=(Bitwise) -
Associação de expressão (Walrus Operator)
:=
eu vou explicar apenas 3 operadores, entendendo eles você entende o resto.
Igual
O igual =, é o operador associativo em si, é ele quem diz o que é o que.
1x = 3 # x é igual a 3
2a = "ye" # a é igual a "ye"
3v = True # v é igual a True
4
5print(x, a, v)
6
Saida
3 ye True
Mais-Igual
A lógica usada aqui se aplica aos demais (menos ao Walrus Operator).
basicamente o operador Mais-Igual += é a versão reduzida/equivalente a x = x + y, você adiciona o valor de forma reassociativa, veja um exemplo:
1x = 9
2print(x)
3x += 33
4print(x)
5
Saida
9 42
essencialmente, pegamos o valor inicial de x, somamos 33 e reassociamos o resultado a variavel novamente, exemplificando em etapas:
-
x = 9 -
x = x + 33 -
x = 9 + 33 -
x = 42
Associação de expressão (Walrus Operator)
O operador de associação de expressão := (Walrus Operator) foi introduzindo no Python 3.8, seu uso é bastante peculiar.
ele permite executarmos e associarmos expressões em uma mesma linha.
por exemplo, veja o seguinte codigo:
12 + 4 # Uma expressão é executada
2
Saida
6
ao rodarmos o codigo recebemos o numero 6, pois, expressões são codigos que retornam um valor, associações não. Contudo, podemos associar o valor resultante de uma expressão a uma variavel.
1n = (
2 2 + 5
3) # Uma expressão é de soma executada e tem seu valor associado a uma variavel n
4
para vermos o valor, precisamos acessa-lo através de funções como a print()
1n = 2 + 5
2print(n)
3
Saida
7
se nós tentarmos acessar o valor e executar a expressão ao mesmo erro teremos um erro.
1print(n=3 + 8)
2# TypeError: 'n' is an invalid keyword argument for print()
3
O Walrus Operator nos permite fazer isso.
1print(n := 3 + 8)
2print(n)
3
Saida
11 11
Ele nos permite acessar o valor enquanto ele esta sendo associado, e mais a frente podemos acessar a variavel contendo o valor.
Precedencia de Operadores Geral
O Python segue uma precedencia de operadores especifica, eis aqui a lista:
| Precedencia | Operador | Descrição |
|---|---|---|
| 1 | (), [] ou {} | Parenteses, Exibição de listas, dicionários e conjuntos |
| 2 | x[index], x[index:index] x(arguments...), x.attribute | atributos, fatiamentos e chamada de funções |
| 3 | await | espera asycrona |
| 4 | ** | exponenciação |
| 5 | +, -, ~ | Positivo, negativo e bitwise NOT |
| 6 | *, @, /, //, % | Multiplicação, multiplicação de matrizes, divisão, "divisão verdadeira", modulo |
| 7 | +, - | Soma e subtração |
| 8 | <<, >> | Bitwise Shifts |
| 9 | & | Bitwise AND |
| 10 | ^ | Bitwise XOR |
| 11 | | | Bitwise OR |
| 12 | in, not in, is, is not, <, <=, >, >=, !=, == | Comparadores |
| 13 | not x | NOT Booleano |
| 14 | and | AND Booleano |
| 15 | or | OR Booleano |
| 16 | if - else | Expressão Condicional |
| 17 | lambda | Funções/Expressões Lambda |
| 18 | := | Expressão de associação (Walrus Operator) |