16.04.2018       Выпуск 226 (16.04.2018 - 22.04.2018)       Статьи

assert и исключения в Python

Читать>>




Экспериментальная функция:

Ниже вы видите текст статьи по ссылке. По нему можно быстро понять ссылка достойна прочтения или нет

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

I recently had a discussion about the difference of Python’sassertkeyword vs. raising “normal” exceptions. For quite a long time, I was uncertain when to useassert, too. In this article, I’ll present some examples and rules of thumb for when usingassertor raising exceptions is – in my opinion – more appropriate.

When do I useassert?

You useassertto check and document invariants of your code. Invariants are conditions in the middle of your code that should always be true (For example: “At this point in timexshould always be a number between 0 and 10”).

Assertions can help others (including future-you) to understand your code and they should make your program crash if the assertion becomes false (because of a programming error).

Here are some examples for whenassertis useful:

# Bad:

def func(a, b):
    x = [a, b]
    ...  # Some operations on "x"
    # x should still contain 2 elements:
    return *x


# Good:

def func(a, b):
    x = [a, b]
    ...  # Some operations on "x"
    assert len(x) == 2, f'len(x) == {len(x)}'
    return *x
# We have widget like this:
#
#   Step size: ( ) auto   (•) manual: [___300] seconds

# Bad:

if self.button_auto.isChecked():
    return None
else:
    # The user provided a manual step size:
    return self.step_size_input.value()


# Good:

if self.button_auto.isChecked():
    return None
else:
    assert self.button_manual.isChecked()
    return self.step_size_input.value()
# Bad:

def fill_list(self):
    # self.dest should be empty, but self.source not:
    while self.source_not_empty():
        self.dest.append(self.take_from_source())


# Good:

def fill_list(self):
    assert len(self.dest) == 0
    assert len(self.source) > 0
    while self.source_not_empty():
        self.dest.append(self.take_from_source())
    assert len(self.dest) > 0

Validation of input parameters

Sometimes,assertis used to validate input parameters for functions. You should avoid this becauseassertstatements are not executed when Python is invoked with-O[O]. In this case, you would suddenly loose all input validation.

AssertionErrorsalso carry less semantics than “normal” exceptions – likeTypeErrorwhen the user passed a string and not a number, orValueErrorwhen the user passed a negative number when only positive numbers are allowed.

# Bad:

def sum(a, b):
    assert isinstance(a, int), 'a must be an int'
    assert a >= 0, 'a must be >= 0'
    ...

# Good:

def sum(a, b):
    if not isinstance(a, int):
        raise TypeError(f'a must be an int but is a {type(a)}')
    if a < 0:
        raise ValueError(f'a must be >= 0 but is {a}')
    ...

There are, however, exceptions possible for this rule. Imagine the following snippet where we want to add/delete a user from a group on an LDAP server:

def add_to_group(conn, user, group):
    _modify_group(conn, user, group, ldap3.MODIFY_ADD)

def delete_from_group(conn, user, group)
    _modify_group(conn, user, group, ldap3.MODIFY_DELETE)

def _modify_group(conn, user, group, op):
    assert op in {ldap3.MODIFY_ADD, ldap3.MODIFY_DELETE}
    conn.modify(group.dn, {'uniqueMember': [(op, [user.username])]})

In this case, it’s appropriate to useassertbecause_modify_group()should never be called by our users but only byadd_to_group()anddelete_from_group().

What kind of exceptions should I raise?

Within you own code, the built-in exception types – especiallyValueError,AttributeError,TypeErrorandRuntimeError– are usually sufficient.

Libraries often define their own exception hierarchy with their own base class. For example, Requests hasRequestErrorfrom which all other exception types inherit.

This makes it a lot easier for users to catch your library’s exceptions. If you raise all kinds of exceptions (and then you don’t document that, too), your users will end up using a broad/bare excepts which is bad.

except [[Base]Exception]is bad because it results in all kinds of nasty side effects. For example, it would also catch (and possibly ignore)KeyboardInterrupt(Ctrl+C) orAsstionError.

And that’s why you should also never raise[Base]Exception.

TLDR

  • Useassertto detect programming errors and conditions that should never occur and which should crash your program immediately, e.g., invariants of an algorithm.

    Warning: Assertion checks are stripped and not executed if Python is invoked with the-Oor--OOoption!

  • Raise an exception for errors that are caused by invalid user input or other problems with the environment (e.g., network errors or unreadable files).

  • Never raise or catch[Base]Exception.



Лучшая Python рассылка




Разместим вашу рекламу

Пиши: mail@pythondigest.ru

Нашли опечатку?

Выделите фрагмент и отправьте нажатием Ctrl+Enter.

Система Orphus