Питонические способы использования "else" в цикле for
Я почти никогда не замечал программу python, которая использует else в цикле for.
Недавно я использовал его для выполнения действия на основе условия переменной цикла при выходе; как он находится в области.
Что такое питонический способ использования else в цикле for? Существуют ли какие-либо заметные варианты использования?
И, да. Мне не нравится использовать оператор break. Я предпочел бы установить условие цикла. Смогу ли я получить какую-либо выгоду из этого, если мне не нравится использовать оператор break в любом случае.
Стоит отметить, что для цикла имеет значение else с момента создания языка, первая версия.
Ответы
Ответ 1
Что может быть больше pythonic, чем PyPy?
Посмотрите, что я обнаружил, начиная с строки 284 в ctypes_configure/configure.py:
for i in range(0, info['size'] - csize + 1, info['align']):
if layout[i:i+csize] == [None] * csize:
layout_addfield(layout, i, ctype, '_alignment')
break
else:
raise AssertionError("unenforceable alignment %d" % (
info['align'],))
И здесь, из строки 425 в pypy/annotation/annrpython.py(clicky)
if cell.is_constant():
return Constant(cell.const)
else:
for v in known_variables:
if self.bindings[v] is cell:
return v
else:
raise CannotSimplify
В pypy/annotation/binaryop.py, начиная с строки 751:
def is_((pbc1, pbc2)):
thistype = pairtype(SomePBC, SomePBC)
s = super(thistype, pair(pbc1, pbc2)).is_()
if not s.is_constant():
if not pbc1.can_be_None or not pbc2.can_be_None:
for desc in pbc1.descriptions:
if desc in pbc2.descriptions:
break
else:
s.const = False # no common desc in the two sets
return s
Не-однострочный в pypy/annotation/classdef.py, начиная с строки 176:
def add_source_for_attribute(self, attr, source):
"""Adds information about a constant source for an attribute.
"""
for cdef in self.getmro():
if attr in cdef.attrs:
# the Attribute() exists already for this class (or a parent)
attrdef = cdef.attrs[attr]
s_prev_value = attrdef.s_value
attrdef.add_constant_source(self, source)
# we should reflow from all the reader position,
# but as an optimization we try to see if the attribute
# has really been generalized
if attrdef.s_value != s_prev_value:
attrdef.mutated(cdef) # reflow from all read positions
return
else:
# remember the source in self.attr_sources
sources = self.attr_sources.setdefault(attr, [])
sources.append(source)
# register the source in any Attribute found in subclasses,
# to restore invariant (III)
# NB. add_constant_source() may discover new subdefs but the
# right thing will happen to them because self.attr_sources
# was already updated
if not source.instance_level:
for subdef in self.getallsubdefs():
if attr in subdef.attrs:
attrdef = subdef.attrs[attr]
s_prev_value = attrdef.s_value
attrdef.add_constant_source(self, source)
if attrdef.s_value != s_prev_value:
attrdef.mutated(subdef) # reflow from all read positions
Позже в том же файле, начиная с строки 307, пример с подсветкой комментария:
def generalize_attr(self, attr, s_value=None):
# if the attribute exists in a superclass, generalize there,
# as imposed by invariant (I)
for clsdef in self.getmro():
if attr in clsdef.attrs:
clsdef._generalize_attr(attr, s_value)
break
else:
self._generalize_attr(attr, s_value)
Ответ 2
Если у вас есть цикл for, у вас действительно нет оператора условий. Поэтому перерыв - это ваш выбор, если вы хотите прервать, а затем еще можете отлично справиться с ситуацией, когда вы не были счастливы.
for fruit in basket:
if fruit.kind in ['Orange', 'Apple']:
fruit.eat()
break
else:
print 'The basket contains no desirable fruit'
Ответ 3
В принципе, он упрощает любой цикл, который использует логический флаг следующим образом:
found = False # <-- initialize boolean
for divisor in range(2, n):
if n % divisor == 0:
found = True # <-- update boolean
break # optional, but continuing would be a waste of time
if found: # <-- check boolean
print n, "is composite"
else:
print n, "is prime"
и позволяет пропустить управление флагом:
for divisor in range(2, n):
if n % divisor == 0:
print n, "is composite"
break
else:
print n, "is prime"
Обратите внимание, что уже существует естественное место для выполнения кода, когда вы находите делитель - прямо перед break
. Единственная новая функция здесь - место для кода, которое нужно выполнить, когда вы пробовали все делители и не нашли.
Это помогает только в сочетании с break
. Вам все еще нужны логические операции, если вы не можете сломаться (например, потому что вы ищете последнее совпадение или должны отслеживать несколько условий параллельно).
О, и BTW, это работает так же, как и петли.
любые/все
В настоящее время, если единственная цель цикла - ответ "да" или "нет", вы можете записать его намного короче с помощью функций any()
/all()
с выражением генератора или генератора, которое дает логические значения:
if any(n % divisor == 0
for divisor in range(2, n)):
print n, "is composite"
else:
print n, "is prime"
Обратите внимание на элегантность! Код 1:1, что вы хотите сказать!
[Это так же эффективно, как и цикл с break
, потому что функция any()
закорочена, работает только выражение генератора до тех пор, пока оно не будет True
. На самом деле это обычно даже быстрее, чем цикл. Упрощенный код Python имеет тенденцию меньше слышать.]
Это менее эффективно, если у вас есть другие побочные эффекты - например, если вы хотите найти делитель. Вы все еще можете это сделать (ab), используя тот факт, что значение не-0 истинно в Python:
divisor = any(d for d in range(2, n) if n % d == 0)
if divisor:
print n, "is divisible by", divisor
else:
print n, "is prime"
но, как вы видите, это становится неустойчивым - не работает, если 0 - возможное значение делителя...
Ответ 4
Без использования break
блоки else
не имеют преимуществ для операторов for
и while
. Следующие два примера эквивалентны:
for x in range(10):
pass
else:
print "else"
for x in range(10):
pass
print "else"
Единственная причина использования else
с for
или while
заключается в том, чтобы что-то делать после цикла, если оно заканчивается нормально, т.е. без явного break
.
После долгих размышлений я могу, наконец, придумать случай, когда это может быть полезно:
def commit_changes(directory):
for file in directory:
if file_is_modified(file):
break
else:
# No changes
return False
# Something has been changed
send_directory_to_server()
return True
Ответ 5
Возможно, лучший ответ исходит из официального руководства Python:
разбить и продолжить сообщения, а также Клаузы на циклах:
Операторы цикла могут иметь другое оговорка; он выполняется, когда цикл заканчивается исчерпанием list (with for) или когда условие становится ложным (пока), но не когда цикл завершается перерывом Заявление
Ответ 6
Я познакомился с замечательной идиомой, в которой вы можете использовать схему for
/break
/else
с итератором, чтобы сохранить как время, так и LOC. Приведенный пример искал кандидата для неполного пути. Если вы хотите увидеть исходный контекст, см. исходный вопрос.
def match(path, actual):
path = path.strip('/').split('/')
actual = iter(actual.strip('/').split('/'))
for pathitem in path:
for item in actual:
if pathitem == item:
break
else:
return False
return True
Что делает использование for
/else
настолько отличным, что это элегантность, позволяющая избежать жонглирования запутанным булевым. Без else
, но надеясь достичь такого же количества короткого замыкания, это может быть написано так:
def match(path, actual):
path = path.strip('/').split('/')
actual = iter(actual.strip('/').split('/'))
failed = True
for pathitem in path:
failed = True
for item in actual:
if pathitem == item:
failed = False
break
if failed:
break
return not failed
Я думаю, что использование else
делает его более элегантным и более очевидным.
Ответ 7
Здесь вы идете:
a = ('y','a','y')
for x in a:
print x,
else:
print '!'
Это для caboose.
изменить:
# What happens if we add the ! to a list?
def side_effect(your_list):
your_list.extend('!')
for x in your_list:
print x,
claimant = ['A',' ','g','u','r','u']
side_effect(claimant)
print claimant[-1]
# oh no, claimant now ends with a '!'
изменить:
a = (("this","is"),("a","contrived","example"),("of","the","caboose","idiom"))
for b in a:
for c in b:
print c,
if "is" == c:
break
else:
print