Os Podres do Python: for-else
2015.01.12Felizmente o for-else é pouco conhecido mas de vez em quando encontro pessoas convencidas de que vale a pena ensinar esse recurso para iniciantes. Eu discordo desse ponto de vista e nesse post apresento alguns argumentos para considerar o for-else um dos grandes podres do Python.
Nota: Tudo o que eu falar aqui sobre o for-else vale também para o while-else. O else
nesses dois casos funcionam da mesma forma.more on matched betting
1. É um mecanismo bastante anti-intuitivo
for x in lista:
print(‘dentro do bloco do for’)
print(x)
else:
print(‘dentro do bloco do else’)
# SAÍDA:
# dentro do bloco do else
Invariavelmente quando alguém encontra esse else
pela primeira vez, naturalmente pensa no como o if-else funciona.
x = 1729
if x in lista:
print(‘dentro do bloco do if’)
print(x)
else:
print(‘dentro do bloco do else’)
# SAÍDA:
# dentro do bloco do else
Nesses dois exemplos as saídas são a mesma. Isso significa então que a estrutura é similar ao if-else e que posso usar o bloco do else para ser executado caso a lista seja vazia, certo? Errado.
x = 1729
if x in lista:
print(‘dentro do bloco do if’)
print(x)
else:
print(‘dentro do bloco do else’)
# SAÍDA:
# dentro do bloco do if
# 1729
for x in lista:
print(‘dentro do bloco do for’)
print(x)
else:
print(‘dentro do bloco do else’)
# SAÍDA:
# dentro do bloco do for
# 1729
# dentro do bloco do else
Então aqui está demonstrado que o else
associado ao for
tem um modelo de funcionamento diferente do que normalmente esperaríamos do else
associado ao if
.
Mas a confusão ainda não acabou, é aqui onde a maioria das pessoas param de estudar e se convencem de que o else
sempre será executado no final de todo for
. Se isso fosse verdade o uso do else
seria completamente redundante, afinal para todo código da forma:
# …
# código aqui
# …
else:
print(‘blablabla’)
…Eu poderia escrever a mesma coisa de uma forma mais direta e óbvia assim:
# …
# código aqui
# …
print(‘blablabla’)
O que não é verdade. Voltando no exemplo que estávamos trabalhando antes, veja o que acontece se colocarmos um break
.
for x in lista:
print(‘dentro do bloco do for’)
print(x)
if x == 1729:
break
else:
print(‘dentro do bloco do else’)
# SAÍDA:
# dentro do bloco do for
# 1729
O bloco do else
não foi executado.
A ideia é que o else
faz parte do laço. Então o break
não apenas sai do bloco do for
, o break
sai do laço.
Então uma conclusão importante aqui é que só faz sentido usar o for-else se você estiver usando break
.
Agora que (talvez) você tenha entendido como o for-else funciona, o próximo problema fica bem óbvio…
2. O nome “else” foi um equívoco
Acredito que mesmo os grandes proponentes do uso do for-else concordam que “else” foi um nome infeliz pra se dar nesse contexto. Um nome sugerido por várias pessoas para substituir “else” nesse contexto é “nobreak”. Isso resolveria o típico erro de compará-lo com o else
do if-else que conhecemos tão bem. Qualquer um ao ler “nobreak” daria um passo para trás e limparia a mente de preconceitos para aprender esse mecanismo novo. Além de já dar uma pista de que ele tem alguma relação importante com o break.
Vou aproveitar e ressaltar esse fato aqui: Python não é uma linguagem perfeita. Python não é um presente que deuses perfeitos da programação deram para nós que somos meros mortais usuários da linguagem. Caras como o Guido van Rossum e Don Knuth também cometem erros como se pode ver.
Mas eu quero ir além, vou ser mais radical e afirmar que…
3. É totalmente inútil na prática
Aqui está o único exemplo mais ou menos prático que já vi de for-else. Todos os outros parecem ser pequenas variações dele.
encontrado = False
for i, elemento in enumerate(lista):
if elemento == alvo:
encontrado = True
break
if not encontrado:
return –1
return i
A ideia dessa função é buscar numa lista
um certo alvo
e retornar o índice i
correspondente. Se alvo
não estiver dentro da lista a função retorna -1
. O funcionamento é bastante similar ao str.find()
.
Não vejo problemas com este código. Estamos usando encontrado
como uma variável de flag e não tem nada de errado com isso. É bastante claro porque é universalmente idiomático.
O argumento para se usar o for-else aqui é eliminar esse uso de flag.
for i, elemento in enumerate(lista):
if elemento == alvo:
break
else:
return –1
return i
A não ser que você já seja um macaco velho de python, esse código não é imediatamente óbvio. Mas o argumento principal é que é mais eficiente, como se uma mísera variável de flag fosse um peso considerável dentro do contexto de um programa feito em python…
Mas isso não importa se você considerar esta alternativa:
for i, elemento in enumerate(lista):
if elemento == alvo:
return i
return –1
Eficiente. Menos linhas de código. Universalmente fácil de entender. Não tem como deixar melhor que isso.
Se você lembrar de quebrar seu código em funções e que return
é um comando poderoso então você terá muitas oportunidades de simplificar o código assim. Não é possível eliminar todo uso de flags e breaks mas eu acredito que com essa técnica você possa cobrir qualquer uso de for-else.
Se você conhece um caso onde o for-else é melhor do que usar uns returns estrategicamente posicionados por favor me mostre para eu reconsiderar minha opinião. Até hoje não vi.
4. Não é suficientemente expressivo e vai contra a filosofia da linguagem
Em Ruby existe o comando unless
que na prática poderia ser considerado inútil. Pois no lugar dele você poderia muito bem se limitar a usar apenas os bem conhecidos if
e else
. Mas ao contrário do for-else do Python, eu acredito que o unless
do Ruby é consideravelmente expressivo. Em alguns casos a leitura do código flui de uma maneira mais natural. Eu não sinto a mesma coisa com o for-else, porque este não é um mecanismo inspirado pela forma que conversamos em linguagem natural como o unless
. Pelo contrário, o for-else veio de uma lógica bem particular de linguagem de máquina.
Expressividade é importante na filosofia de Ruby, é celebrado ter várias maneiras alternativas de fazer a mesma coisa. Em contraste, Python tem uma filosofia extremamente aversa a isso.
Explicit is better than implicit.
Simple is better than complex.
…
There should be one– and preferably only one –obvious way to do it.
…
If the implementation is hard to explain, it’s a bad idea.
Não sou o maior fã de discutir o que é considerado “pythonico” ou não, mas dessa vez vou abrir uma exceção e apontar que o for-else claramente vai contra essas 4 linhas do The Zen of Python.
Muita gente gostaria que o Python tivesse alguma espécie de mecanismo como o “do-while” mas toda tentativa é barrada porque vai contra a filosofia. Se me perguntassem pra escolher entre o do-while e o for-else, eu não hesitaria em ficar com o do-while.
O for-else é legado de uma época onde a filosofia do Python não estava amadurecida do jeito que está hoje.
Conclusão
Resumindo o que penso do for-else:
1. É um mecanismo bastante anti-intuitivo
2. O nome “else” foi um equívoco
3. É totalmente inútil na prática
4. Não é suficientemente expressivo e vai contra a filosofia da linguagem
else-clauses in #python‘s for/while loops are trivially easy to explain but some minds just rebel at the thought: http://t.co/HUhn1GrG
— raymondh (@raymondh) October 18, 2011
Eu não sou um rebelde! Raymond Hettinger que é um lunático pra pensar que for-else é “trivialmente fácil de explicar”.
Python é a única linguagem que possui esse mecanismo do for-else. E eu aposto que vai continuar assim.