Есть ли более аккуратная альтернатива `кроме: пас`? - PullRequest
6 голосов
/ 16 сентября 2010

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

def get_random_foo_or_bar():
    "I'd rather have a foo than a bar."

    if there_are_foos():
        return get_random_foo()

    if there_are_bars():
        return get_random_bar()

    raise IndexError, "No foos, no bars"

Однако первое, что get_random_foo делает, это проверяет, есть ли foos, и поднимает IndexError, если нет, поэтому there_are_foos является избыточным.Кроме того, база данных задействована, и использование отдельных функций создает проблему параллелизма.Соответственно, я переписал это примерно так:

def get_random_foo_or_bar():
    "Still prefer foos."

    try:
        return get_random_foo()
    except IndexError:
        pass

    try:
        return get_random_bar()
    except IndexError:
        pass

    raise IndexError, "No foos, no bars"

Но я нахожу это гораздо менее читабельным, и, поскольку у меня никогда не было причин использовать pass, прежде чем он будет казаться инстинктивно неправильным.

Есть ли более эффективный шаблон или я должен научиться принимать pass?

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


Редактировать

Спасибо всем, кто сказал, что pass хорошо - это обнадеживает!

Также спасибо тем, кто предложил заменить исключение на возвращаемое значение None.Я вижу, насколько это полезный шаблон, но я бы сказал, что в этой ситуации он семантически неверен: функциям было предложено выполнить невыполнимую задачу, поэтому они должны вызвать исключение.Я предпочитаю следить за поведением модуля random (например, random.choice([])).

Ответы [ 7 ]

13 голосов
/ 16 сентября 2010

Именно так я и написал бы.Это просто и имеет смысл.Я не вижу проблем с операторами pass.

Если вы хотите уменьшить повторение и ожидаете добавления будущих типов, вы можете свернуть это в цикл.Тогда вы могли бы изменить pass на функционально эквивалентный оператор continue, если это более приятно для ваших глаз:

for getter in (get_random_foo, get_random_bar):
    try:
        return getter()
    except IndexError:
        continue  # Ignore the exception and try the next type.

raise IndexError, "No foos, no bars"
2 голосов
/ 16 сентября 2010

Мне немного странно, что get_random_foo() вызывает IndexError, когда индекс не принимает в качестве параметра (но это может иметь больше смысла в контексте). Почему бы не иметь get_random_foo() или оболочку, перехватить ошибку и вернуть вместо нее None?

def get_random_foo_wrapper():
    try:
        return get_random_foo()
    except IndexError:
        return None

def get_random_foo_or_bar():
    "I'd rather have a foo than a bar."

    return get_random_foo_wrapper() or get_random_bar_wrapper() or None

Редактировать : Я должен упомянуть, что если foo & bar являются объектами, которые могут иметь значение False (0 или '' скажем), то сравнение or пропустит их, что BAD

2 голосов
/ 16 сентября 2010

pass хорошо (есть причина это на языке! -), но альтернатива без пропуска просто требует немного больше вложенности:

try: return get_random_foo()
except IndexError:
    try: return get_random_bar()
    except IndexError:
        raise IndexError "no foos, no bars"

Python's Zen (import this из приглашения на интерактивный переводчик) говорит, что "flat лучше вложенного", но вложенность - это * на языке, который вы можете использовать, когда решаете (предположительно, будучи просвещенным) что вы можете сделать лучше, чем этот мудрый коан! -) (Например, «если вы встретите Будду на дороге» ...).

1 голос
/ 16 сентября 2010

Если это только те двое, всегда можно просто ...

try:
    return get_random_foo()
except IndexError:
    try:
        return get_random_bar()
    except IndexError:
        raise IndexError, "No foos, no bars"

Если их больше двух, то написанное вами кажется вполне приемлемым.

0 голосов
/ 16 сентября 2010

Необходимо ли, чтобы get_random_foo / bar () вызывал ошибку IndexError, если она не может быть успешной?

Если они вернули None, вы можете сделать:

def get_random_foo_or_bar():
    return get_random_foo() or get_random_bar()
0 голосов
/ 16 сентября 2010

Если вам не нужно сообщение об исключении (только тип):

def get_random_foo_or_bar():
    try:
        return get_random_foo()
    except IndexError:
        return get_random_bar()    # if failing at this point,
                                   # the whole function will raise IndexError
0 голосов
/ 16 сентября 2010

Основываясь на предложении Питера Гибсона, вы можете создать универсальную функцию-обертку, которая поглощает данное исключение. И тогда вы могли бы написать функцию, которая возвращает такую ​​обобщенную оболочку для предоставленного исключения. Или, черт возьми, для предоставленного списка исключений.

def maketrap(*exceptions):
    def trap(func, *args, **kwargs):
        try:
            return func(*args, **kwargs)
        except exceptions:
            return None
    return trap

def get_random_foo_or_bar():
    mytrap = maketrap(IndexError)
    return mytrap(get_random_foo) or mytrap(get_random_bar) or None
...