Pythonic способ избежать высказываний «if x: return x»

218

У меня есть метод, который вызывает 4 других метода в последовательности, чтобы проверить определенные условия, и возвращает сразу (не проверяя следующие) всякий раз, когда кто-то возвращает что-то Truthy.

def check_all_conditions():
    x = check_size()
    if x:
        return x

    x = check_color()
    if x:
        return x

    x = check_tone()
    if x:
        return x

    x = check_flavor()
    if x:
        return x
    return None

Это похоже на большой багажный код. Вместо каждого двухстрочного оператора if я бы предпочел сделать что-то вроде:

x and return x

Но это неверный Python. Я скучаю по простому, элегантному решению здесь? Кстати, в этой ситуации эти четыре метода проверки могут быть дорогими, поэтому я не хочу вызывать их несколько раз.

Бернард
источник
7
Что это за х? Являются ли они просто True / False или они представляют собой структуры данных, содержащие некоторую информацию, причем None или подобное используется в качестве особого случая для указания отсутствия каких-либо данных? Если это последнее, вы почти наверняка должны использовать исключения.
Натаниэль
13
@gerrit Код, представленный выше, является гипотетическим / псевдокодом, который не относится к теме обзора кода. Если автор поста хочет, чтобы его реальный, реальный рабочий код был пересмотрен, тогда он может опубликовать его на Code Review.
Фрэнсис
4
Почему вы думаете, что x and return xлучше, чем if x: return x? Последний гораздо более читабелен и, следовательно, удобен в обслуживании. Вам не нужно слишком беспокоиться о количестве символов или строк; удобочитаемость имеет значение. В любом случае, это точно такое же количество непробельных символов, и, если вам действительно нужно, они if x: return xбудут отлично работать только на одной строке.
Марсель
3
Пожалуйста, уточните, заботитесь ли вы о фактических значениях или вам просто нужно вернуть логическое значение. Это имеет значение, какие варианты доступны, а также какие из них более четко сообщают о намерениях. Наименование предполагает, что вам нужен только логический. Также имеет значение, важно ли избегать многократных вызовов этих функций. Также может иметь значение, принимают ли функции какой-либо или другой набор параметров. Без этих разъяснений, я думаю, что этот вопрос относится к непонятным, слишком широким или основанным на мнении.
jpmc26
7
@ jpmc26 OP явно говорит об истинных возвращаемых значениях, а затем его код возвращает x(в противоположность bool(x)), так что я думаю, что можно с уверенностью предположить, что функции OP могут возвращать что угодно, и он хочет в первую очередь все, что является правдивым.
Timgeb

Ответы:

278

Вы можете использовать цикл:

conditions = (check_size, check_color, check_tone, check_flavor)
for condition in conditions:
    result = condition()
    if result:
        return result

Это дает дополнительное преимущество: теперь вы можете изменять число условий.

Вы можете использовать map()+ filter()(версии Python 3, используйте future_builtinsверсии в Python 2), чтобы получить первое такое соответствующее значение:

try:
    # Python 2
    from future_builtins import map, filter
except ImportError:
    # Python 3
    pass

conditions = (check_size, check_color, check_tone, check_flavor)
return next(filter(None, map(lambda f: f(), conditions)), None)

но если это более читаемым спорно.

Другой вариант - использовать выражение генератора:

conditions = (check_size, check_color, check_tone, check_flavor)
checks = (condition() for condition in conditions)
return next((check for check in checks if check), None)
Мартейн Питерс
источник
27
если условия на самом деле являются только условиями, т. е. логическими значениями, то в своем первом предложении вы также можете использовать встроенную функцию anyвместо цикла. return any(condition() for condition in conditions)
4
@Leonhard: anyимеет почти такую ​​же реализацию внутри. Но это выглядит намного лучше, пожалуйста, отправьте это как ответ)
Ник Волынкин
13
Читаемость превосходит почти все другие соображения. Вы говорите, карта / фильтр "спорны", я проголосовал за безобразно некрасиво. Спасибо, конечно, но если кто-то в моей команде вставит карту / фильтр для этого кода, я перенесу их в другую команду или назначу на дежурство в постели.
Кевин Дж. Райс
15
Является ли этот нечитаемый блок кода действительно "питонским"? И особенно идея молнии conditionsи arguments? Это ИМХО намного хуже, чем оригинальный код, который занимает около 10 секунд, чтобы проанализировать мой анализатор мозгов.
'
34
«Предпочитаю Python», сказали они. «Perl не читается», сказали они. И тогда это случилось return next((check for check in checks if check), None).
jja
393

В качестве альтернативы хорошему ответу Мартина, вы можете зацепить or. Это вернет первое истинное значение, или Noneесли нет истинного значения:

def check_all_conditions():
    return check_size() or check_color() or check_tone() or check_flavor() or None

Демо-версия:

>>> x = [] or 0 or {} or -1 or None
>>> x
-1
>>> x = [] or 0 or {} or '' or None
>>> x is None
True
timgeb
источник
9
Конечно, но это будет утомительно читать быстро, если есть несколько вариантов. Плюс мой подход позволяет вам использовать переменное количество условий.
Мартин Питерс
14
@MartijnPieters вы можете использовать, \чтобы поставить каждую проверку в отдельной строке.
Caridorc
12
@MartijnPieters Я никогда не подразумевал, что мой ответ лучше твоего, мне тоже нравится твой ответ :)
timgeb
38
@Caridorc: мне очень не нравится использовать \для расширения логической линии. Вместо этого используйте круглые скобки; так return (....)с новыми строками, вставленными по необходимости. Тем не менее, это будет одна длинная логическая линия.
Мартин Питерс
47
Я думаю, что это лучшее решение. Аргумент «будет утомительно [..], если будет более нескольких вариантов», является спорным, потому что одна функция не должна делать непомерное количество проверок в любом случае. Если это требуется, проверки должны быть разделены на несколько функций.
BlueRaja - Дэнни Пфлугхофт
88

Не меняй

Есть другие способы сделать это, как показывают различные другие ответы. Ни один не так ясен, как ваш оригинальный код.

Джек Эйдли
источник
39
Я бы поспорил против этого, но ваше предложение является законным, чтобы быть озвученным. Лично я нахожу свои глаза напряженными, пытаясь прочитать ОП, в то время как, например, решение Тимгеба щелкает мгновенно.
Reti43
3
Это действительно вопрос мнения. Лично я удалил бы новые строки после :, потому что я считаю, if x: return xчто это довольно хорошо, и это делает функцию более компактной. Но это может быть только я.
'
2
Это не только ты. С помощьюor как сделал тимгеб, является правильной и понятной идиомой. У многих языков есть это; возможно, когда он вызывается, orelseэто даже более ясно, но даже старый or(или ||на других языках) подразумевается как альтернатива, если первый «не работает».
Рэй Тоал
1
@RayToal: импорт идиом из других языков - отличный способ запутать код.
Джек Эйдли
1
Иногда да, конечно! Кроме того, это может быть способом открыть свой разум и привести его к открытию новых и лучших моделей и парадигм, которые никто, возможно, не пробовал раньше. Стиль развивается, когда люди заимствуют, делятся и пробуют новые вещи. Работает в обе стороны. Во всяком случае, я никогда не слышал использованияor помеченного непифонического или каким-либо образом запутанного, но это вопрос мнения в любом случае - как и должно быть.
Рэй Тоал
83

По сути, тот же ответ, что и у Timgeb, но вы можете использовать круглые скобки для лучшего форматирования:

def check_all_the_things():
    return (
        one()
        or two()
        or five()
        or three()
        or None
    )
Уэйн Вернер
источник
8
всем, пожалуйста, помогите поднять этот ответ до 1-го места. сделай свое дело!
Gyom
74

Согласно закону Керли , вы можете сделать этот код более читабельным, разделив две задачи:

  • Какие вещи я проверяю?
  • Вернулась ли одна вещь верной?

на две функции:

def all_conditions():
    yield check_size()
    yield check_color()
    yield check_tone()
    yield check_flavor()

def check_all_conditions():
    for condition in all_conditions():
        if condition:
            return condition
    return None

Это позволяет избежать:

  • сложные логические структуры
  • действительно длинные очереди
  • репетиция

... сохраняя линейный, легко читаемый поток.

Вы также можете придумать еще лучшие имена функций, в зависимости от ваших конкретных обстоятельств, которые делают его еще более читабельным.

Фил Фрост
источник
Мне нравится этот, хотя True / False следует изменить на условие / None, чтобы соответствовать вопросу.
Малкольм
2
Это мой любимый! Он также справляется с разными проверками и аргументами. Вполне возможно, перегружен для этого конкретного примера, но действительно полезным инструментом для будущих проблем!
2016 года
4
Обратите внимание, что в return Noneэтом нет необходимости, поскольку функции возвращаются Noneпо умолчанию. Тем не менее, нет ничего плохого в том, чтобы вернуться Noneявно, и мне нравится, что вы решили это сделать.
Timgeb
1
Я думаю, что этот подход будет лучше реализован с определением локальной функции.
Джек Эйдли
1
@timgeb "Явное лучше, чем неявное", Zen of Python .
jpmc26
42

Это вариант первого примера Мартинса. Он также использует стиль "коллекции вызываемых", чтобы обеспечить короткое замыкание.

Вместо цикла вы можете использовать встроенный any.

conditions = (check_size, check_color, check_tone, check_flavor)
return any(condition() for condition in conditions) 

Обратите внимание, что anyвозвращается логическое значение, поэтому, если вам нужно точное возвращаемое значение чека, это решение не будет работать. anyне будет различать 14, 'red', 'sharp', 'spicy'как возвращаемые значения, все они будут возвращены как True.


источник
Вы можете сделать, next(itertools.ifilter(None, (c() for c in conditions)))чтобы получить фактическое значение, не приводя его к логическому значению.
Кодзиро
1
Действительно ли anyкороткое замыкание?
Звол
1
@zwol Да, попробуйте с некоторыми примерами
1
Это менее читабельно, чем объединение 4 функций с помощью «или», и окупается только в том случае, если число условий большое или динамическое.
RJH
1
@rjh Это отлично читается; это просто список букв и понимание. Я бы предпочел, потому что мои глаза остекленели примерно после третьегоx = bar(); if x: return x;
Blacklight Shining
27

Рассматривали ли вы просто писать if x: return xвсе в одну строку?

def check_all_conditions():
    x = check_size()
    if x: return x

    x = check_color()
    if x: return x

    x = check_tone()
    if x: return x

    x = check_flavor()
    if x: return x

    return None

Это не менее повторяюще, чем то, что вы имели, но IMNSHO читается немного более плавно.

zwol
источник
24

Я весьма удивлен, что никто не упомянул встроенное, anyкоторое сделано для этой цели:

def check_all_conditions():
    return any([
        check_size(),
        check_color(),
        check_tone(),
        check_flavor()
    ])

Обратите внимание, что хотя эта реализация, вероятно, является самой ясной, она оценивает все проверки, даже если первая из них True.


Если вам действительно нужно остановиться на первой неудачной проверке, рассмотрите возможность использования reduceкоторой для преобразования списка в простое значение:

def check_all_conditions():
    checks = [check_size, check_color, check_tone, check_flavor]
    return reduce(lambda a, f: a or f(), checks, False)

reduce(function, iterable[, initializer]): Применить функцию двух аргументов кумулятивно к элементам итерируемого слева направо, чтобы свести итерируемое к одному значению. Левый аргумент x - это накопленное значение, а правый аргумент y - это значение обновления из итерируемого. Если присутствует необязательный инициализатор, он помещается перед элементами итерируемого в вычислении

В твоем случае:

  • lambda a, f: a or f()это функция , которая проверяет , что либо аккумулятор aили текущий контроль f()является True. Обратите внимание, что если aесть True, f()не будет оцениваться.
  • checksсодержит функции проверки ( fэлемент из лямбды)
  • False является начальным значением, иначе проверка не произойдет, и результат всегда будет True

anyи reduceявляются основными инструментами для функционального программирования. Я настоятельно рекомендую вам обучить их, а также mapэто здорово!

ngasull
источник
9
anyработает только в том случае, если проверки действительно возвращают логическое значение, буквально Trueили False, но вопрос не определяет это. Вам нужно будет использовать, reduceчтобы вернуть фактическое значение, возвращаемое чеком. Также достаточно просто избежать оценки всех проверок с anyпомощью генератора, например any(c() for c in (check_size, check_color, check_tone, check_flavor)). Как и в ответе Леонарда
Дэвид Z
Мне нравится ваше объяснение и использование reduce. Как и @DavidZ, я считаю, что ваше решение anyдолжно использовать генератор, и нужно указать, что оно ограничено возвратом Trueили False.
Timgeb
1
@DavidZ на самом деле anyработает с правдивыми значениями: any([1, "abc", False]) == Trueandany(["", 0]) == False
ngasull
3
@blint извини, мне было не ясно. Цель этого вопроса - вернуть результат проверки (а не просто указать, прошла ли проверка успешно или нет). Я указывал, что anyдля этой цели работает , только если из функций проверки возвращены действительные логические значения.
Дэвид Z
19

Если вам нужна та же структура кода, вы можете использовать троичные операторы!

def check_all_conditions():
    x = check_size()
    x = x if x else check_color()
    x = x if x else check_tone()
    x = x if x else check_flavor()

    return x if x else None

Я думаю, это выглядит красиво и понятно, если вы посмотрите на это.

Демо-версия:

Скриншот этого работает

Phinet
источник
7
Что с маленькой рыбкой ASCII над вашим терминалом?
36
@LegoStormtroopr Я использую рыбную раковину, поэтому я украшаю ее аквариумом, чтобы сделать меня счастливым. :)
Phinet
3
Спасибо за прекрасную рыбу (и цвета, между прочим, что это за редактор?)
mathreadler
4
Вы можете получить рыбу на fishshell.com , и файл конфигурации для ascii здесь pastebin.com/yYVYvVeK , также редактор является возвышенным текстом.
Phinet
9
x if x else <something>можно просто уменьшить доx or <something>
5

Для меня лучший ответ - ответ @ phil-frost, за которым следует @ wayne-werner.

Что мне кажется интересным, так это то, что никто ничего не сказал о том факте, что функция будет возвращать много разных типов данных, что сделает обязательным выполнение проверок самого типа x для дальнейшей работы.

Поэтому я бы смешал ответ @ PhilFrost с идеей сохранить один тип:

def all_conditions(x):
    yield check_size(x)
    yield check_color(x)
    yield check_tone(x)
    yield check_flavor(x)

def assessed_x(x,func=all_conditions):
    for condition in func(x):
        if condition:
            return x
    return None

Обратите внимание, что xон передается как аргумент, но также all_conditionsиспользуется как переданный генератор проверяющих функций, где все они получают xпроверяемый объект и возвращают Trueили False. При использовании funcс в all_conditionsкачестве значения по умолчанию, вы можете использовать assessed_x(x), или вы можете пройти дальше персонализированный генератор черезfunc .

Таким образом, вы получите, xкак только пройдет одна проверка, но она всегда будет одного типа.

juandesant
источник
4

В идеале я бы переписал check_ функции для возврата Trueили Falseвместо значения. Ваши чеки становятся

if check_size(x):
    return x
#etc

Предполагая, что ваша xпеременная не является неизменной, ваша функция все равно может изменить ее (хотя они не могут переназначить ее), но вызываемая функция на checkсамом деле не должна изменять ее.

RoadieRich
источник
3

Небольшое изменение в первом примере Martijns выше, которое позволяет избежать if внутри цикла:

Status = None
for c in [check_size, check_color, check_tone, check_flavor]:
  Status = Status or c();
return Status
mathreadler
источник
Является ли? Вы все еще делаете сравнение. В вашей версии вы также будете проверять все условия независимо и не будете возвращать в первый раз истинное значение, в зависимости от того, насколько дорогими являются эти функции, что может быть нежелательно.
Reti43
4
@ Reti43: Status or c()будет пропускать / замыкать, оценивать вызовы, c()если Statusэто правда, поэтому код в этом ответе, по-видимому, не вызывает больше функций, чем код OP. stackoverflow.com/questions/2580136/…
Нил Слэйтер
2
@NeilSlater Правда. Единственный недостаток, который я вижу, состоит в том, что лучший случай теперь в O (n), потому что listiterator должен дать n раз, когда это было O (1) прежде, если первая функция возвращает что-то правдивое в O (1).
Тимгеб
1
Да, хорошие моменты. Мне просто нужно надеяться, что для вычисления c () требуется немного больше времени, чем для зацикливания почти пустого цикла. Проверка вкуса может занять, по крайней мере, целый вечер, если он хороший.
mathreadler
3

Мне нравится @ Timgeb's. В то же время я хотел бы добавить, что выражение Noneв returnвыражении не требуется, так как orоценивается коллекция разделенных выражений и возвращается первое ненулевое, пустое, пустое и если не существует, то Noneвозвращается есть лиNone или нет!

Итак, моя check_all_conditions()функция выглядит так:

def check_all_conditions():
    return check_size() or check_color() or check_tone() or check_flavor()

Использование timeitс number=10**7я посмотрел на время выполнения ряда предложений. Для сравнения я просто использовал random.random()функцию для возврата строки или Noneна основе случайных чисел. Вот весь код:

import random
import timeit

def check_size():
    if random.random() < 0.25: return "BIG"

def check_color():
    if random.random() < 0.25: return "RED"

def check_tone():
    if random.random() < 0.25: return "SOFT"

def check_flavor():
    if random.random() < 0.25: return "SWEET"

def check_all_conditions_Bernard():
    x = check_size()
    if x:
        return x

    x = check_color()
    if x:
        return x

    x = check_tone()
    if x:
        return x

    x = check_flavor()
    if x:
        return x
    return None

def check_all_Martijn_Pieters():
    conditions = (check_size, check_color, check_tone, check_flavor)
    for condition in conditions:
        result = condition()
        if result:
            return result

def check_all_conditions_timgeb():
    return check_size() or check_color() or check_tone() or check_flavor() or None

def check_all_conditions_Reza():
    return check_size() or check_color() or check_tone() or check_flavor()

def check_all_conditions_Phinet():
    x = check_size()
    x = x if x else check_color()
    x = x if x else check_tone()
    x = x if x else check_flavor()

    return x if x else None

def all_conditions():
    yield check_size()
    yield check_color()
    yield check_tone()
    yield check_flavor()

def check_all_conditions_Phil_Frost():
    for condition in all_conditions():
        if condition:
            return condition

def main():
    num = 10000000
    random.seed(20)
    print("Bernard:", timeit.timeit('check_all_conditions_Bernard()', 'from __main__ import check_all_conditions_Bernard', number=num))
    random.seed(20)
    print("Martijn Pieters:", timeit.timeit('check_all_Martijn_Pieters()', 'from __main__ import check_all_Martijn_Pieters', number=num))
    random.seed(20)
    print("timgeb:", timeit.timeit('check_all_conditions_timgeb()', 'from __main__ import check_all_conditions_timgeb', number=num))
    random.seed(20)
    print("Reza:", timeit.timeit('check_all_conditions_Reza()', 'from __main__ import check_all_conditions_Reza', number=num))
    random.seed(20)
    print("Phinet:", timeit.timeit('check_all_conditions_Phinet()', 'from __main__ import check_all_conditions_Phinet', number=num))
    random.seed(20)
    print("Phil Frost:", timeit.timeit('check_all_conditions_Phil_Frost()', 'from __main__ import check_all_conditions_Phil_Frost', number=num))

if __name__ == '__main__':
    main()

И вот результаты:

Bernard: 7.398444877040768
Martijn Pieters: 8.506569201346597
timgeb: 7.244275416364456
Reza: 6.982133448743038
Phinet: 7.925932800076634
Phil Frost: 11.924794811353031
Реза Додж
источник
2

Этот способ немного нестандартен, но я думаю, что конечный результат прост, удобочитаем и выглядит красиво.

Основная идея заключается в raiseисключении, когда одна из функций оценивается как верная и возвращает результат. Вот как это может выглядеть:

def check_conditions():
    try:
        assertFalsey(
            check_size,
            check_color,
            check_tone,
            check_flavor)
    except TruthyException as e:
        return e.trigger
    else:
        return None

Вам понадобится assertFalseyфункция, которая вызывает исключение, когда один из аргументов вызываемой функции оценивается как истинный:

def assertFalsey(*funcs):
    for f in funcs:
        o = f()
        if o:
            raise TruthyException(o)

Выше может быть изменено, чтобы также обеспечить аргументы для функций, которые будут оцениваться.

И конечно тебе понадобится TruthyExceptionсама. Это исключение обеспечивает то, objectчто вызвало исключение:

class TruthyException(Exception):
    def __init__(self, obj, *args):
        super().__init__(*args)
        self.trigger = obj

Конечно, вы можете превратить оригинальную функцию во что-то более общее:

def get_truthy_condition(*conditions):
    try:
        assertFalsey(*conditions)
    except TruthyException as e:
        return e.trigger
    else:
        return None

result = get_truthy_condition(check_size, check_color, check_tone, check_flavor)

Это может быть немного медленнее, потому что вы используете как ifоператор, так и обработку исключения. Однако исключение обрабатывается не более одного раза, поэтому снижение производительности должно быть незначительным, если вы не собираетесь запускать проверку и получать Trueзначение много-много тысяч раз.

Рик поддерживает Монику
источник
// , Милый! Считается ли "Pythonic" использовать обработку исключений для такого рода вещей?
Натан Басанезе
@NathanBasanese Sure - исключения используются постоянно для потока управления. StopIterationдовольно хороший пример: исключение возникает каждый раз, когда вы исчерпываете итерацию. То, чего вы хотите избежать, - это последовательно поднимать исключения снова и снова, что будет дорогостоящим. Но делать это один раз нет.
Рик поддерживает Монику
//, Ах, я понимаю , что вы имеете в виду что - то вроде programmers.stackexchange.com/questions/112463/... . Я проголосовал за этот вопрос и за этот ответ. Документы Python 3 для этого находятся здесь: я думаю, docs.python.org/3/library/stdtypes.html#iterator-types .
Натан Басанезе
1
Вы хотите определить универсальную функцию и исключение, просто сделать несколько проверок в какой-нибудь другой функции? Я думаю, что это немного.
Blacklight Shining
@BacklightShining Я согласен. Я бы никогда не сделал это сам. ОП попросил способы избежать повторения кода, но я думаю, что он начал с совершенно отлично.
Рик поддерживает Монику
2

Питонический способ - это использовать redu (как кто-то уже упоминал) или itertools (как показано ниже), но мне кажется, что простое использование короткого замыкания orоператора дает более четкий код

from itertools import imap, dropwhile

def check_all_conditions():
    conditions = (check_size,\
        check_color,\
        check_tone,\
        check_flavor)
    results_gen = dropwhile(lambda x:not x, imap(lambda check:check(), conditions))
    try:
        return results_gen.next()
    except StopIteration:
        return None
Дмитрий Рубанович
источник
0

Я собираюсь прыгнуть сюда и никогда не писал ни одной строки Python, но я предполагаю, что if x = check_something(): return xэто правильно?

если так:

def check_all_conditions():

    if (x := check_size()): return x
    if (x := check_color()): return x
    if (x := check_tone()): return x
    if (x := check_flavor()): return x

    return None
Richard87
источник
1
Это не действительный Python, нет. Python не позволяет вам использовать такой оператор присваивания. Однако новое специальное выражение присваивания было добавлено совсем недавно, так что теперь вы можете писать if ( x := check_size() ) :для того же эффекта.
Джек Эйдли,
0

Или используйте max:

def check_all_conditions():
    return max(check_size(), check_color(), check_tone(), check_flavor()) or None
U10-Forward
источник
-2

В прошлом я видел несколько интересных реализаций операторов switch / case с диктовками, которые привели меня к этому ответу. Используя приведенный вами пример, вы получите следующее. (Это безумие using_complete_sentences_for_function_names, поэтому check_all_conditionsпереименовано в status. См. (1))

def status(k = 'a', s = {'a':'b','b':'c','c':'d','d':None}) :
  select = lambda next, test : test if test else next
  d = {'a': lambda : select(s['a'], check_size()  ),
       'b': lambda : select(s['b'], check_color() ),
       'c': lambda : select(s['c'], check_tone()  ),
       'd': lambda : select(s['d'], check_flavor())}
  while k in d : k = d[k]()
  return k

Функция выбора избавляет от необходимости вызывать каждый check_FUNCTIONдважды, т.е. вы избегаетеcheck_FUNCTION() if check_FUNCTION() else next добавления другого функционального слоя. Это полезно для долго работающих функций. Лямбды в dict задерживают выполнение его значений до цикла while.

В качестве бонуса вы можете изменить порядок выполнения и даже пропустить некоторые тесты, изменив kи, sнапример,k='c',s={'c':'b','b':None} уменьшив количество тестов и отменив исходный порядок обработки.

timeit стипендиатов может торговаться стоимость добавления дополнительного слоя или два стека и стоимость для Dict посмотреть , но вы , кажется , больше озабочены красивости кода.

Альтернативно, более простая реализация может быть следующей:

def status(k=check_size) :
  select = lambda next, test : test if test else next
  d = {check_size  : lambda : select(check_color,  check_size()  ),
       check_color : lambda : select(check_tone,   check_color() ),
       check_tone  : lambda : select(check_flavor, check_tone()  ),
       check_flavor: lambda : select(None,         check_flavor())}
  while k in d : k = d[k]()
  return k
  1. Я имею в виду это не с точки зрения pep8, а с точки зрения использования одного краткого описательного слова вместо предложения. Конечно, OP может следовать некоторому соглашению о кодировании, работать с какой-то существующей кодовой базой или не заботиться о кратких терминах в их кодовой базе.
Carel
источник
1
Иногда люди сходят с ума от названий, когда подойдет одно слово. Используя код OP в качестве примера, маловероятно, что он вызвал бы функции, check_no/some/even/prime/every_third/fancy_conditionsно только эту функцию, так почему бы не вызвать ее statusили если кто-то настаивает check_status. Использование _all_излишне, он не обеспечивает целостность вселенных. При именовании обязательно следует использовать согласованный набор ключевых слов, используя, по возможности, интервал имен. Длинные предложения лучше всего служат строкой документов. Редко требуется более 8-10 символов для краткого описания чего-либо.
Карел
1
Я фанат длинных имен функций, потому что я хочу, чтобы высокоуровневые функции были самодокументированы. Но check_all_conditionsэто плохое имя, потому что оно не проверяет все условия, если они верны. Я бы использовал что-то вроде matches_any_condition.
Джон Хейзен
Это интересный такт. Я стараюсь свести к минимуму количество писем, которые я опишу позже :) Кажется, я набросал кучу мнений в свое решение, когда я действительно пытался дать полезную подсказку. Должно ли это быть вычеркнуто?
Карел
2
Это кажется слишком глупым, особенно учитывая другие решения по этому вопросу. То, что пытается сделать ОП, совсем не сложно; решение должно быть достаточно простым, чтобы понять полусонный. И я понятия не имею, что здесь происходит.
Blacklight Shining
Я стремился к гибкости. Модифицированный ответ, включающий менее «хакерский» вариант
Карел