As compreensões estão tendo algumas interações inesperadas com o escopo. Este é o comportamento esperado?
Eu tenho um método:
def leave_room(self, uid):
u = self.user_by_id(uid)
r = self.rooms[u.rid]
other_uids = [ouid for ouid in r.users_by_id.keys() if ouid != u.uid]
other_us = [self.user_by_id(uid) for uid in other_uids]
r.remove_user(uid) # OOPS! uid has been re-bound by the list comprehension above
# Interestingly, it's rebound to the last uid in the list, so the error only shows
# up when len > 1
Correndo o risco de choramingar, esta é uma fonte brutal de erros. Enquanto escrevo um novo código, ocasionalmente encontro erros muito estranhos devido à religação - mesmo agora que sei que é um problema. Eu preciso criar uma regra como "sempre prefacie variáveis temporárias em compreensões de lista com sublinhado", mas mesmo isso não é à prova de idiotas.
O fato de haver essa bomba-relógio aleatória esperando nega toda a boa "facilidade de uso" da compreensão de listas.
python
binding
list-comprehension
Jabavu Adams
fonte
fonte
for
construção -loop efor
-loops variáveis de vazamento . Portanto, não foi explícito, mas foi declarado implicitamente.Respostas:
Compreensões de lista vazam a variável de controle de loop no Python 2, mas não no Python 3. Aqui está Guido van Rossum (criador do Python) explicando a história por trás disso:
fonte
break
- mas irrelevante para comprehesions. Lembro-me de algumas discussões em comp.lang.python em que as pessoas queriam atribuir variáveis no meio da expressão. A forma menos insana encontrada foi de valor único para as cláusulas, por exemplo.sum100 = [s for s in [0] for i in range(1, 101) for s in [s + i]][-1]
, mas só precisa de uma var local de compreensão e funciona tão bem no Python 3. Acho que "vazar" era a única maneira de definir a variável visível fora de uma expressão. Todos concordaram que essas técnicas são horríveis :-)Sim, as compreensões de lista "vazam" suas variáveis no Python 2.x, assim como os loops for.
Em retrospecto, isso foi reconhecido como um erro e foi evitado com expressões geradoras. EDIT: Como Matt B. observa , também foi evitado quando as sintaxes de compreensão de conjunto e dicionário foram portadas do Python 3.
O comportamento das compreensões de lista teve que ser deixado como está no Python 2, mas está totalmente corrigido no Python 3.
Isso significa que em todos:
o
x
é sempre local para a expressão enquanto estes:no Python 2.x, todos vazam a
x
variável para o escopo circundante.ATUALIZAÇÃO para Python 3.8 (?) : PEP 572 introduzirá o
:=
operador de atribuição que vaza deliberadamente de compreensões e expressões geradoras! É motivado por essencialmente 2 casos de uso: capturar uma "testemunha" de funções de encerramento antecipado comoany()
eall()
:e atualizando o estado mutável:
Consulte o Apêndice B para obter o escopo exato. A variável é atribuída nos arredores mais próximos
def
oulambda
, a menos que a função declarenonlocal
ouglobal
.fonte
Sim, a atribuição ocorre lá, exatamente como em um
for
loop. Nenhum novo escopo está sendo criado.Este é definitivamente o comportamento esperado: em cada ciclo, o valor é vinculado ao nome que você especificar. Por exemplo,
Depois que isso for reconhecido, parece fácil de evitar: não use nomes existentes para as variáveis dentro das compreensões.
fonte
Curiosamente, isso não afeta o dicionário ou a compreensão do conjunto.
No entanto, foi corrigido em 3 conforme observado acima.
fonte
alguma solução alternativa, para python 2.6, quando esse comportamento não é desejável
fonte
Em python3, enquanto em compreensão de lista, a variável não obtém mudança após seu escopo terminar, mas quando usamos o loop for simples, a variável é reatribuída fora do escopo.
i = 1 print (i) print ([i no intervalo (5)]) print (i) O valor de i permanecerá 1 apenas.
Agora basta usar simplesmente o loop for, o valor de i será reatribuído.
fonte