Python PEP 8

Os dois programas abaixo fazem exatamente a mesma coisa e mostram o mesmo resultado. Um deles você entende em segundos; o outro, você precisa decifrar. A diferença não está no que o código faz, está no jeito de escrever. A comunidade Python combinou um jeito, escrito num documento chamado PEP 8, e quase todo código Python que você vai encontrar segue esse combinado. Nesta aula você aprende as regras principais e as ferramentas que conferem (e até arrumam) o código por você.

Você vai aprender
  • O que é a PEP 8 e por que estilo importa, se o Python roda de qualquer jeito.
  • Indentação, tamanho da linha e linhas em branco.
  • Como dar nome a variáveis, funções, classes, constantes e módulos.
  • Onde vão (e onde não vão) os espaços.
  • Como organizar os imports, e por que evitar from modulo import *.
  • Comparações no estilo do Python: is None, if lista:, sem == True.
  • As ferramentas ruff e black, com a saída real.
  • Os erros de verdade que um nome mal escolhido causa.

A ideia: letra legível e ortografia combinada

Um bilhete escrito às pressas, sem pontuação, com abreviações que só você entende, até passa a mensagem. Mas quem lê sofre. Por isso existem a ortografia e as regras de pontuação: todo mundo escreve do mesmo jeito, e todo mundo lê sem esforço.

Com código é igual. O Python aceita muita coisa, mas um programa é lido muito mais vezes do que é escrito: por você mesma daqui a um mês, por quem vai continuar o seu trabalho, por quem vai ajudar a achar um erro. A PEP 8 é a "ortografia" do Python.

TermoO que é
PEPPython Enhancement Proposal, "proposta de melhoria do Python": os documentos numerados em que a linguagem é discutida e decidida. Toda novidade do Python nasce de uma PEP (veja as Novidades)
PEP 8A de número 8: o "Guia de Estilo para Código Python", escrito em 2001 por Guido van Rossum (o criador do Python) e outros, e atualizado desde então. Está em peps.python.org/pep-0008
EstiloTudo o que muda a aparência do código sem mudar o que ele faz: espaços, nomes, quebras de linha

Antes e depois

import sys, os
def CalculaTotal( Itens,Desc ):
  T=0
  for i in Itens :
      T=T+i[ 0 ]*i[1]
  if Desc==True : T = T*0.9
  return T
class carrinho:
    def __init__(self,dono): self.dono=dono;self.itens=[]
print(CalculaTotal([(27.90,2),(8.49,3)],True))
DESCONTO_CLIENTE_FIEL = 0.10


def calcular_total(itens, cliente_fiel):
    total = 0
    for preco, quantidade in itens:
        total += preco * quantidade
    if cliente_fiel:
        total *= 1 - DESCONTO_CLIENTE_FIEL
    return total


class Carrinho:
    def __init__(self, dono):
        self.dono = dono
        self.itens = []


print(calcular_total([(27.90, 2), (8.49, 3)], True))
$ python3 baguncado.py
73.143
$ python3 arrumado.py
73.143

Mesmo resultado. Mas no segundo dá para ler como um texto: "para cada preço e quantidade nos itens, some ao total; se for cliente fiel, aplique o desconto". As seções a seguir mostram, uma por uma, as regras que transformaram o primeiro no segundo.


1. Indentação: 4 espaços

No Python, a indentação (o recuo no começo da linha) não é enfeite: é ela que diz o que está dentro do for, do if, da função (veja Sintaxe e Indentação). O Python aceita qualquer quantidade de espaços, desde que você seja coerente dentro do bloco. O baguncado.py usa 2 espaços num lugar, 6 em outro e 4 na classe, e funciona. A PEP 8 combina: 4 espaços por nível, sempre.

def calcular_total(itens):
    total = 0  # 1 nível: 4 espaços
    for preco, quantidade in itens:
        total += preco * quantidade  # 2 níveis: 8 espaços
    return total

E a tecla Tab? A PEP 8 prefere espaços. Os editores de código (VS Code, o editor deste guia e outros) já trocam o Tab por 4 espaços sozinhos. O que não pode é misturar os dois no mesmo bloco: o Python recusa (veja Quando dá errado).


2. Nomes

Cada tipo de coisa tem o seu jeito de escrever o nome. Assim, só de olhar, você sabe se Carrinho é uma classe e se calcular_total é uma função.

O quêEstiloExemploErrado (pela PEP 8)
Variáveisminusculas_com_sublinhado (o "snake_case", "jeito de cobra")preco_unitario, totalPrecoUnitario, precoUnitario, T
Funções e métodosminusculas_com_sublinhadocalcular_total()CalculaTotal()
Parâmetrosminusculas_com_sublinhadoitens, cliente_fielItens, Desc
ClassesPalavrasComMaiuscula (o "CapWords")Carrinho, ContaBancariacarrinho, conta_bancaria
ConstantesMAIUSCULAS_COM_SUBLINHADODESCONTO_CLIENTE_FIEL, TAXA_ENTREGAdesconto (para um valor que nunca muda)
Módulos (arquivos .py)minúsculas, curtos, sublinhado se ajudarloja.py, notas_turma.pyLoja.py, Notas Turma.py

Mais quatro conselhos sobre nomes:

  • Diga o que é. for preco, quantidade in itens: se lê sozinho; for i in Itens: com i[0] e i[1] obriga quem lê a adivinhar. Letras soltas (i, x) só para contadores e contas curtas.
  • Nunca use l (L minúsculo), O (o maiúsculo) ou I (i maiúsculo) sozinhos como nome. Em muitas fontes eles se confundem com 1 e 0: l = 1, O = 0.
  • Não use nomes que o Python já usa, como list, str, sum, id, input: você esconde a função embutida (o erro está em Quando dá errado). Se o nome bom é uma palavra reservada, a PEP 8 sugere um sublinhado no fim: class_, id_.
  • Sem acento, por costume. O Python 3 aceita preço = 10, mas quase ninguém escreve assim (é difícil de digitar em teclados de outros países). Este guia usa preco.

Missão: renomeie, no estilo da PEP 8, a função CalcularMedia para calcular_media e a variável NotasDaTurma para notas_da_turma (em todos os lugares em que elas aparecem). O resultado continua o mesmo.

def CalcularMedia(Notas):
    return sum(Notas) / len(Notas)


NotasDaTurma = [8, 7, 9, 6]
print("Média:", CalcularMedia(NotasDaTurma))
def calcular_media(notas):
    return sum(notas) / len(notas)


notas_da_turma = [8, 7, 9, 6]
print("Média:", calcular_media(notas_da_turma))

3. Espaços

A regra geral: espaço onde ajuda a separar as ideias, nenhum espaço onde só atrapalha.

RegraAssimAssim não
Um espaço de cada lado da atribuição e das comparaçõestotal = 0, if desconto == 10:total=0, if desconto==10:
Um espaço de cada lado dos operadores (+, -, +=, and…)total = total + precototal=total+preco
Espaço depois da vírgula, nunca antes(27.90, 2), def f(a, b):(27.90,2), (27.90 , 2)
Nada logo depois de abrir nem antes de fechar ( [ {itens[0], calcular(itens)itens[ 0 ], calcular( itens )
Nada entre o nome da função e o (print("oi")print ("oi")
Nada antes do : do if, for, deffor item in itens:for item in itens :
Argumento nomeado e valor padrão: sem espaço no =print(a, end=""), def f(x=0):print(a, end = "")
Uma instrução por linhaif desconto: e, na linha de baixo, o que fazerif desconto: total *= 0.9, a = 1; b = 2

Em contas misturadas, a PEP 8 permite tirar os espaços do operador que é feito primeiro, para mostrar a precedência: x = a*2 + b fica mais claro que x = a * 2 + b. As duas formas são aceitas.


4. Linhas: tamanho e linhas em branco

No máximo 79 caracteres

A PEP 8 pede linhas de até 79 caracteres (72 nos comentários e docstrings), para caber em janelas estreitas e em dois arquivos lado a lado. Muitas equipes combinam um limite um pouco maior (o black e o ruff usam 88; a própria PEP 8 aceita até 99 quando a equipe concorda). Para quebrar uma linha comprida, use os parênteses: dentro de ( ), [ ] ou { }, o Python deixa você continuar na linha de baixo.

# comprida demais (111 caracteres)
mensagem = f"Olá, {nome_do_cliente}! O seu pedido de número {numero_do_pedido} saiu para entrega e chega hoje."

# quebrada com parênteses: dois textos seguidos viram um só
mensagem = (
    f"Olá, {nome_do_cliente}! O seu pedido de número {numero_do_pedido} "
    "saiu para entrega e chega hoje."
)

# uma chamada com muitos argumentos: um por linha
total = calcular_total(
    itens=carrinho,
    cliente_fiel=True,
    cupom="PADARIA10",
)

A vírgula depois do último argumento (cupom="PADARIA10",) é permitida e costumeira quando cada item está na sua linha: acrescentar um item depois mexe numa linha só. Existe também a barra invertida \ no fim da linha para continuar na próxima, mas a PEP 8 prefere os parênteses.

Linhas em branco

  • Duas linhas em branco antes e depois de cada função e classe "de fora" (as que não estão dentro de outra coisa).
  • Uma linha em branco entre os métodos dentro de uma classe.
  • Dentro de uma função, uma linha em branco de vez em quando para separar as "etapas", como parágrafos de um texto. Sem exagero.

Volte ao arrumado.py e repare: duas linhas antes de def calcular_total, duas antes de class Carrinho, duas antes do print final.


5. imports

import csv
import json
from pathlib import Path

import requests

from loja import calcular_total
  1. No começo do arquivo (depois só da docstring do módulo, se houver).
  2. Um módulo por linha: import csv e import json, e não import csv, json. (Já from pathlib import Path, PurePath, vários nomes do mesmo módulo, pode.)
  3. Em três grupos, separados por uma linha em branco: primeiro a biblioteca padrão (o que vem com o Python: csv, json, pathlib); depois os pacotes instalados com o pip (como o requests); por último os seus módulos (loja). Dentro de cada grupo, em ordem alfabética, por costume.
  4. Evite from modulo import * ("importe tudo"): ninguém sabe de onde veio cada nome, e um nome importado pode esconder outro, como o open do exemplo em Quando dá errado.
  5. Não importe o que não usa. O baguncado.py importava sys e os sem usar nenhum dos dois.

6. Comparações no estilo do Python

Para saber se…EscrevaEm vez dePor quê
uma variável é Noneif resposta is None:if resposta == None:None é um só; is confere se é ele mesmo (veja is e in)
um valor verdadeiro/falso é verdadeiroif cliente_fiel:if cliente_fiel == True:O if já testa a verdade; o == True não acrescenta nada
…é falsoif not cliente_fiel:if cliente_fiel == False:Idem
uma lista (ou texto, dicionário) está vaziaif not itens:if len(itens) == 0:Coleções vazias valem "falso" (veja Booleanos)
uma lista tem algoif itens:if len(itens) > 0:Idem
o tipo de um valorif isinstance(x, int):if type(x) == int:isinstance também aceita os tipos "filhos" (veja type)

7. O Zen do Python

Por trás da PEP 8 há uma "filosofia", escondida dentro do próprio Python. Digite import this:

$ python3 -c "import this"
The Zen of Python, by Tim Peters

Beautiful is better than ugly.
Explicit is better than implicit.
Simple is better than complex.
Complex is better than complicated.
Flat is better than nested.
Sparse is better than dense.
Readability counts.
Special cases aren't special enough to break the rules.
Although practicality beats purity.
Errors should never pass silently.
Unless explicitly silenced.
In the face of ambiguity, refuse the temptation to guess.
There should be one-- and preferably only one --obvious way to do it.
Although that way may not be obvious at first unless you're Dutch.
Now is better than never.
Although never is often better than *right* now.
If the implementation is hard to explain, it's a bad idea.
If the implementation is easy to explain, it may be a good idea.
Namespaces are one honking great idea -- let's do more of those!

"O Zen do Python", 19 frases de Tim Peters. Algumas, traduzidas: "Bonito é melhor que feio." "Explícito é melhor que implícito." "Simples é melhor que complexo." "A legibilidade conta." "Erros nunca devem passar em silêncio." "Deve haver um (e de preferência só um) jeito óbvio de fazer." "Se a implementação é difícil de explicar, é uma má ideia." (A brincadeira do "a não ser que você seja holandês" é com o Guido van Rossum, que é holandês.)

Experimente: rode e leia o Zen do Python. Qual frase combina mais com o baguncado.py?

import this

8. As ferramentas: ruff e black

Ninguém confere a PEP 8 de olho. Há programas que fazem isso: os linters (de lint, os fiapos de tecido que grudam na roupa) apontam os problemas, e os formatadores reescrevem o código no estilo certo. Os dois mais usados hoje são o ruff (linter e formatador) e o black (formatador). Eles não vêm com o Python: a Ana instalou os dois num ambiente virtual.

$ python3 -m venv .venv
$ .venv/bin/pip install -q ruff black
$ .venv/bin/ruff --version
ruff 0.17.1
$ .venv/bin/black --version
black, 26.10.1 (compiled: yes)
Python (CPython) 3.14.4

(As versões mudam com o tempo; as saídas abaixo são destas versões.)

O linter: ruff check

Por padrão, o ruff check confere só um grupo pequeno de regras (nesta versão, entre outras, os imports desnecessários e a ordem dos imports):

$ .venv/bin/ruff check --output-format concise baguncado.py
baguncado.py:1:1: I001 [*] Import block is un-sorted or un-formatted
baguncado.py:1:8: F401 [*] `sys` imported but unused
baguncado.py:1:13: F401 [*] `os` imported but unused
Found 3 errors.
[*] 3 fixable with `--fix`.

Pedindo mais regras com --select (E e W: os erros e avisos de estilo da PEP 8; F: os erros prováveis; N: os nomes), aparece quase tudo o que você viu nesta aula:

$ .venv/bin/ruff check --select E,W,F,N --output-format concise baguncado.py
baguncado.py:1:1: E401 [*] Multiple imports on one line
baguncado.py:1:8: F401 [*] `sys` imported but unused
baguncado.py:1:13: F401 [*] `os` imported but unused
baguncado.py:2:5: N802 Function name `CalculaTotal` should be lowercase
baguncado.py:2:19: N803 Argument name `Itens` should be lowercase
baguncado.py:2:25: N803 Argument name `Desc` should be lowercase
baguncado.py:3:3: N806 Variable `T` in function should be lowercase
baguncado.py:5:7: N806 Variable `T` in function should be lowercase
baguncado.py:6:6: E712 Avoid equality comparisons to `True`; use `Desc:` for truth checks
baguncado.py:6:17: E701 Multiple statements on one line (colon)
baguncado.py:6:19: N806 Variable `T` in function should be lowercase
baguncado.py:8:7: N801 Class name `carrinho` should use CapWords convention
baguncado.py:9:44: E702 Multiple statements on one line (semicolon)
Found 13 errors.
[*] 3 fixable with `--fix` (1 unsafe fix available with `--fix --unsafe-fixes`).
baguncado.py:2:5:
Arquivo, linha 2, coluna 5: onde está o problema.
N802
O código da regra. Cada letra é uma família (E, W, F, N, I de imports…). Com o código, você acha a explicação completa na documentação do ruff.
Function name `CalculaTotal` should be lowercase
"O nome da função CalculaTotal deveria ser em minúsculas."
[*]
O ruff sabe consertar este sozinho, com --fix.

Algumas traduções: E401 "vários imports numa linha"; F401 "sys importado mas não usado"; E712 "evite comparar com True; use Desc:"; E701/E702 "várias instruções numa linha (dois-pontos / ponto e vírgula)"; N801 "o nome da classe carrinho deveria usar CapWords". E o tamanho da linha tem a regra E501:

$ .venv/bin/ruff check --select E501 --output-format concise linha_longa.py
linha_longa.py:1:89: E501 Line too long (111 > 88)
Found 1 error.

"Linha comprida demais (111 > 88)". O ruff usa 88 como limite, a não ser que você configure outro. Um código sem nenhum problema recebe: All checks passed! ("tudo conferido"), como o arrumado.py:

$ .venv/bin/ruff check --output-format concise arrumado.py
All checks passed!

O formatador: ruff format (ou black)

Com --diff, o formatador mostra o que mudaria, sem mexer no arquivo. As linhas com - saem, as com + entram:

$ .venv/bin/ruff format --diff baguncado.py
--- baguncado.py
+++ baguncado.py
@@ -1,10 +1,19 @@
 import sys, os
-def CalculaTotal( Itens,Desc ):
-  T=0
-  for i in Itens :
-      T=T+i[ 0 ]*i[1]
-  if Desc==True : T = T*0.9
-  return T
+
+
+def CalculaTotal(Itens, Desc):
+    T = 0
+    for i in Itens:
+        T = T + i[0] * i[1]
+    if Desc == True:
+        T = T * 0.9
+    return T
+
+
 class carrinho:
-    def __init__(self,dono): self.dono=dono;self.itens=[]
-print(CalculaTotal([(27.90,2),(8.49,3)],True))
+    def __init__(self, dono):
+        self.dono = dono
+        self.itens = []
+
+
+print(CalculaTotal([(27.90, 2), (8.49, 3)], True))

1 file would be reformatted

Indentação de 4, espaços nos lugares certos, uma instrução por linha, duas linhas em branco entre as definições. O black --diff baguncado.py propõe exatamente as mesmas mudanças (o ruff format foi feito para imitar o black). Sem o --diff, o arquivo é reescrito. A Ana fez isso numa cópia, e depois pediu ao ruff check --fix que consertasse o que soubesse:

$ cp baguncado.py copia.py
$ .venv/bin/ruff format copia.py
1 file reformatted
$ .venv/bin/ruff check --fix --output-format concise copia.py
Found 2 errors (2 fixed, 0 remaining).
$ .venv/bin/ruff format copia.py
1 file reformatted
$ cat copia.py
def CalculaTotal(Itens, Desc):
    T = 0
    for i in Itens:
        T = T + i[0] * i[1]
    if Desc == True:
        T = T * 0.9
    return T


class carrinho:
    def __init__(self, dono):
        self.dono = dono
        self.itens = []


print(CalculaTotal([(27.90, 2), (8.49, 3)], True))
$ python3 copia.py
73.143

O --fix apagou os dois imports inúteis, e o segundo format limpou as linhas em branco que sobraram no começo. O programa continua dando 73.143. Mas repare no que ficou: CalculaTotal, T, carrinho, i[0], == True. As ferramentas cuidam da "pontuação"; dar bons nomes e escrever com clareza continua sendo trabalho seu.

FerramentaTipoComando
ruff checkLinter: aponta problemasruff check arquivo.py (--fix conserta o que souber)
ruff formatFormatador: reescreve o estiloruff format arquivo.py (--diff só mostra)
blackFormatador (o que popularizou a ideia)black arquivo.py (--diff só mostra)
pycodestyle, flake8, pylintLinters mais antigosVocê vai encontrá-los em projetos mais velhos
O seu editor de códigoQuase todos rodam essas ferramentas ao salvarProcure "format on save" nas configurações

Formas diferentes de seguir o estilo

JeitoVantagemQuando usar
De cabeça, conhecendo as regrasNada para instalar; o código já nasce arrumadoSempre: é o que esta aula ensina
Linter (ruff check)Pega o que você deixou passar, inclusive erros prováveis (F401)Antes de entregar ou guardar o código
Formatador (ruff format, black)Ninguém discute espaços: a ferramenta decideEm equipe, todos com a mesma ferramenta
O estilo do projetoCoerênciaNum projeto que já existe, siga o estilo dele, mesmo que difira da PEP 8
"Uma coerência tola…"

A própria PEP 8 tem uma seção com o título "Uma coerência tola é o duende das mentes pequenas". Ela diz: o mais importante é a coerência dentro de um projeto, e uma regra pode ser quebrada quando segui-la deixaria o código menos legível. As regras servem à legibilidade, e não o contrário.


Quando dá errado

Estilo ruim, na maior parte das vezes, não quebra nada: só dificulta a leitura. Mas algumas escolhas de estilo provocam erros de verdade.

Tab e espaços misturados

No arquivo abaixo, a linha 2 começa com 4 espaços e a linha 3, com um Tab. No editor de texto as duas parecem alinhadas:

def saudacao(nome):
    mensagem = "Olá, " + nome
	return mensagem

print(saudacao("Ana"))
$ python3 tab.py
  File "/home/ana/tab.py", line 3
    	return mensagem
    ^
TabError: inconsistent use of tabs and spaces in indentation

"Uso inconsistente de tabs e espaços na indentação." O Python não tem como saber quantos espaços "vale" um Tab, então recusa a mistura. Repare que o erro aparece antes de o programa rodar (não há Traceback): é um erro de sintaxe. Como consertar: troque o Tab por 4 espaços. Configure o seu editor para inserir espaços quando você aperta Tab (quase todos já vêm assim).

Um nome que esconde uma função embutida
list = ["arroz", "feijão", "café"]
print(list)
letras = list("Ana")
$ python3 sombra.py
['arroz', 'feijão', 'café']
Traceback (most recent call last):
  File "/home/ana/sombra.py", line 3, in <module>
    letras = list("Ana")
TypeError: 'list' object is not callable

"Um objeto list não pode ser chamado." Na linha 1, o nome list passou a ser a lista de compras, e não mais a função que cria listas. Na linha 3, list("Ana") tenta "chamar" a lista de compras como se fosse função. O erro acontece longe da causa, o que o torna difícil de achar. Como consertar: dê um nome que diga o que é: compras = [...]. O mesmo vale para str, sum, max, input, id, type.

from os import * e o open trocado
from os import *

with open("compras.txt", "w") as arquivo:
    arquivo.write("arroz\n")
$ python3 estrela.py
Traceback (most recent call last):
  File "/home/ana/estrela.py", line 3, in <module>
    with open("compras.txt", "w") as arquivo:
         ~~~~^^^^^^^^^^^^^^^^^^^^
TypeError: 'str' object cannot be interpreted as an integer

"Um texto não pode ser interpretado como número inteiro"? O open de sempre aceita "w". Só que o módulo os também tem uma função chamada open (uma mais "baixa", que espera números no segundo argumento), e o import * a trouxe por cima do open embutido, sem avisar. Como consertar: import os e use os. na frente do que for do módulo; ou importe só os nomes de que precisa: from os import listdir.

Uma palavra reservada como nome
class = "3º ano B"
print(class)
$ python3 palavra_chave.py
  File "/home/ana/palavra_chave.py", line 1
    class = "3º ano B"
          ^
SyntaxError: invalid syntax

"Sintaxe inválida." class é uma palavra-chave: ela já quer dizer "aqui começa uma classe", e o Python não entendeu o = depois dela. Como consertar: use outro nome (turma = "3º ano B") ou, se o nome for mesmo o melhor, a sugestão da PEP 8: um sublinhado no fim, class_.

Missão: o programa quebra com 'list' object is not callable. Dê à lista de compras um nome que não esconda a função list (por exemplo, compras), nos dois lugares em que ela aparece.

list = ["arroz", "feijão", "café"]
print(list)
letras = list("Ana")
print(letras)
compras = ["arroz", "feijão", "café"]
print(compras)
letras = list("Ana")
print(letras)

Referência rápida

AssuntoA regra da PEP 8
Indentação4 espaços por nível; nunca misturar com Tab
Tamanho da linhaAté 79 caracteres (72 em comentários); quebre com parênteses
Linhas em branco2 em volta de funções e classes de fora; 1 entre métodos
Variáveis, funções, parâmetros, módulosminusculas_com_sublinhado
ClassesPalavrasComMaiuscula
ConstantesMAIUSCULAS_COM_SUBLINHADO
Nomes a evitarl, O, I sozinhos; nomes embutidos (list, str, sum…); palavra-chave → class_
EspaçosEm volta de = e operadores; depois da vírgula; nunca dentro de ( ), antes de ( ou :; nenhum no = de argumento nomeado
InstruçõesUma por linha (sem ;, sem if x: faça na mesma linha)
ImportsNo topo, um por linha, em grupos (padrão, de terceiros, seus); sem *
Comparaçõesis None; if x: em vez de == True; if not lista: em vez de len(lista) == 0
Ferramentasruff check (aponta), ruff format / black (arrumam)

Exercícios

1. O nome da função

Reescreva o nome CalcularFrete no estilo da PEP 8 para funções:

def (peso):

2. A constante

A taxa de entrega é sempre 7.5 e nunca muda no programa. Como a PEP 8 manda escrever o nome?

= 7.5

3. Qual linha está no estilo da PEP 8?

4. Como a PEP 8 manda testar se a variável resposta é None?

5. Qual destes nomes é o certo para uma classe?

6. Depois de ruff format e ruff check --fix, o que ainda pode precisar de você?

Missão final: arrumando o baguncado.py

Missão: reescreva o programa no estilo da PEP 8: a função passa a se chamar calcular_total, com os parâmetros itens e cliente_fiel; a classe, Carrinho; a comparação vira if cliente_fiel:, sem == True; tire os imports que não são usados; 4 espaços de indentação, uma instrução por linha, os espaços e as linhas em branco nos lugares certos. Bons nomes para T e i também contam. O resultado tem de continuar 73.143.

import sys, os
def CalculaTotal( Itens,Desc ):
  T=0
  for i in Itens :
      T=T+i[ 0 ]*i[1]
  if Desc==True : T = T*0.9
  return T
class carrinho:
    def __init__(self,dono): self.dono=dono;self.itens=[]
print(CalculaTotal([(27.90,2),(8.49,3)],True))
def calcular_total(itens, cliente_fiel):
    total = 0
    for preco, quantidade in itens:
        total = total + preco * quantidade
    if cliente_fiel:
        total = total * 0.9
    return total


class Carrinho:
    def __init__(self, dono):
        self.dono = dono
        self.itens = []


print(calcular_total([(27.90, 2), (8.49, 3)], True))

Compare a sua versão com o arrumado.py do começo da aula. Lá, o 0.9 virou uma constante com nome (DESCONTO_CLIENTE_FIEL = 0.10), o que conta para quem lê de onde veio aquele número.


Resumo

  • A PEP 8 é o guia de estilo do Python: o jeito combinado de escrever, porque o código é lido muito mais vezes do que é escrito.
  • 4 espaços de indentação (nunca misturados com Tab); linhas de até 79 caracteres, quebradas com parênteses; 2 linhas em branco entre funções e classes, 1 entre métodos.
  • Nomes: minusculas_com_sublinhado para variáveis e funções, PalavrasComMaiuscula para classes, MAIUSCULAS para constantes. Nada de l/O/I sozinhos, nem de nomes embutidos como list.
  • Espaços em volta de = e operadores e depois da vírgula; nenhum dentro dos parênteses, nem no = de argumento nomeado. Uma instrução por linha.
  • Imports no topo, um por linha, em grupos; sem import *. Compare com is None; teste a verdade com if x:.
  • ruff check aponta os problemas; ruff format e black arrumam o estilo. Bons nomes continuam sendo com você.