Простые циклы, подобные вашему примеру , не должны использовать выражения присваивания .У PEP есть рекомендации по стилю раздел , на которые следует обратить внимание:
- Если можно использовать либо операторы присваивания, либо выражения присваивания, предпочтите операторы;они являются четкой декларацией намерения.
- Если использование выражений присваивания приведет к неоднозначности порядка выполнения, реструктурируйте его, чтобы использовать вместо него операторы.
Должны быть реализованы простые циклыиспользуя итерации и for
, они гораздо более четко предназначены для зацикливания, пока итератор не будет завершен.Для вашего примера, итерация выбора будет range()
:
for a in range(10):
# ...
, что на намного чище, лаконичнее и удобочитаемее , чем, скажем,
a = -1
while (a := a + 1) < 10:
# ...
.Выше требуется дополнительная проверка, чтобы выяснить, что в цикле a
начнется с 0
, а не с -1
.
Суть в том, что вы не должны поддаваться искушению«найти способы использовать операторы присваивания».Используйте оператор присваивания, только если он делает код проще, а не сложнее.Нет хорошего способа сделать ваш цикл while
более простым, чем цикл for
.
Ваши попытки переписать простой цикл также отражены в выводах Тима Петерса Приложение , которое цитирует Тима Питерса на тему стиля и выражений присваивания.Тим Питерс является автором Zen of Python (среди многих других значительных вкладов в Python и разработку программного обеспечения в целом), поэтому его слова должны иметь некоторый дополнительный вес:
В других случаях объединение связанной логики усложняло понимание, например, переписывание:
while True:
old = total
total += term
if old == total:
return total
term *= mx2 / (i*(i+1))
i += 2
в качестве краткого:
while total != (total := total + term):
term *= mx2 / (i*(i+1))
i += 2
return total
Тест while
также существуеттонкий, в значительной степени опирающийся на строгую оценку слева направо в контексте короткого замыкания или цепочки методов.Мой мозг не подключен таким образом.
Мой жирный акцент.
Гораздо лучший вариант использования для выражений присваивания - assigment-then-testШаблон , особенно когда необходимо выполнить несколько тестов для проверки последовательных объектов.В эссе Тима цитируется пример, приведенный Кириллом Балуновым из стандартной библиотеки, который фактически выигрывает от нового синтаксиса.copy.copy()
функция должна найти подходящий метод подключения для создания копии пользовательского объекта:
reductor = dispatch_table.get(cls)
if reductor:
rv = reductor(x)
else:
reductor = getattr(x, "__reduce_ex__", None)
if reductor:
rv = reductor(4)
else:
reductor = getattr(x, "__reduce__", None)
if reductor:
rv = reductor()
else:
raise Error("un(shallow)copyable object of type %s" % cls)
Отступ здесь является результатом вложенногоif
операторов, потому что Python не дает нам более приятного синтаксиса для тестирования различных опций, пока один не найден, и в то же время назначает выбранную опцию переменной (вы не можете использовать цикл здесь чисто, так как не все тестыдля имен атрибутов).
Но выражение присваивания позволяет использовать структуру flat if / elif / else
:
if reductor := dispatch_table.get(cls):
rv = reductor(x)
elif reductor := getattr(x, "__reduce_ex__", None):
rv = reductor(4)
elif reductor := getattr(x, "__reduce__", None):
rv = reductor()
else:
raise Error("un(shallow)copyable object of type %s" % cls)
Эти 8 строк намного чище и их прощеследовать (по моему мнению), чем текущий 13.
Другой часто цитируемый хороший вариант использования - , если после фильтрации есть соответствующий объект, сделайте что-нибудь с этим объектом , который в настоящее времятребуется функция next()
с выражением генератора, запасное значение по умолчанию и тест if
:
found = next((ob for ob in iterable if ob.some_test(arg)), None)
if found is not None:
# do something with 'found'
, который вы можете очистить с помощью any()
функция
if any((found := ob).some_test(arg) for ob in iterable):
# do something with 'found'