- PVSM.RU - https://www.pvsm.ru -

Tips and tricks from my Telegram-channel @pythonetc, January 2020

Tips and tricks from my Telegram-channel @pythonetc, January 2020 - 1

It is a new selection of tips and tricks about Python and programming from my Telegram-channel @pythonetc.

Previous publications [1].

Tips and tricks from my Telegram-channel @pythonetc, January 2020 - 2

The order of except blocks matter: if exceptions can be caught by more than one block, the higher block applies. The following code doesn’t work as intended:

import logging

def get(storage, key, default):
    try:
        return storage[key]
    except LookupError:
        return default
    except IndexError:
        return get(storage, 0, default)
    except TypeError:
        logging.exception('unsupported key')
        return default

print(get([1], 0, 42))  # 1
print(get([1], 10, 42))  # 42
print(get([1], 'x', 42))  # error msg, 42

except IndexError never works since IndexError is a subclass of LookupError. More concrete exception should always be higher:

import logging

def get(storage, key, default):
    try:
    return storage[key]
    except IndexError:
    return get(storage, 0, default)
    except LookupError:
    return default
    except TypeError:
    logging.exception('unsupported key')
    return default

print(get([1], 0, 42))  # 1
print(get([1], 10, 42))  # 1
print(get([1], 'x', 42))  # error msg, 42

Tips and tricks from my Telegram-channel @pythonetc, January 2020 - 3

Python supports parallel assignment meaning that all variables are modified at once after all expressions are evaluated. Moreover, you can use any expression that supports assignment, not only variables:

def shift_inplace(lst, k):
    size = len(lst)
    lst[k:], lst[0:k] = lst[0:-k], lst[-k:]

lst = list(range(10))

shift_inplace(lst, -3)
print(lst)
# [3, 4, 5, 6, 7, 8, 9, 0, 1, 2]

shift_inplace(lst, 5)
print(lst)
# [8, 9, 0, 1, 2, 3, 4, 5, 6, 7]

Tips and tricks from my Telegram-channel @pythonetc, January 2020 - 4

Python substitution does not fallback to addition with negative value. Consider the example:

class Velocity:
    SPEED_OF_LIGHT = 299_792_458

    def __init__(self, amount):
        self.amount = amount

    def __add__(self, other):
        return type(self)(
            (self.amount + other.amount) /
            (
                1 +
                self.amount * other.amount /
                self.SPEED_OF_LIGHT ** 2
            )
        )

    def __neg__(self):
        return type(self)(-self.amount)

    def __str__(self):
        amount = int(self.amount)
        return f'{amount} m/s'

That doesn’t work:

v1 = Velocity(20_000_000)
v2 = Velocity(10_000_000)

print(v1 - v2)
# TypeError: unsupported operand type(s) for -: 'Velocity' and 'Velocity

Funny enough, that does:

v1 = Velocity(20_000_000)
v2 = Velocity(10_000_000)

print(v1 +- v2)
# 10022302 m/s

Tips and tricks from my Telegram-channel @pythonetc, January 2020 - 5

Today's post is written by Telegram-user orsinium [2].

Function can't be generator and regular function at the same time. If yield is presented anywhere in the function body, the function turns into generator:

def zeros(*, count: int, lazy: bool):
        if lazy:
            for _ in range(count):
                yield 0
            else:
                return [0] * count

zeros(count=10, lazy=True)
# <generator object zeros at 0x7ff0062f2a98>

zeros(count=10, lazy=False)
# <generator object zeros at 0x7ff0073da570>

list(zeros(count=10, lazy=False))
# []

However, regular function can return another iterator:

def _lazy_zeros(*, count: int):
    for _ in range(count):
        yield 0
    
def zeros(*, count: int, lazy: bool):
    if lazy:
        return _lazy_zeros(count=count)
    return [0] * count

zeros(count=10, lazy=True)
# <generator object _lazy_zeros at 0x7ff0062f2750>

zeros(count=10, lazy=False)
# [0, 0, 0, 0, 0, 0, 0, 0, 0, 0]

Also, for simple cases generator expressions could be useful:

def zeros(*, count: int, lazy: bool):
    if lazy:
        return (0 for _ in range(count))
    return [0] * count

Tips and tricks from my Telegram-channel @pythonetc, January 2020 - 6

Brackets are required to create a generator comprehension:

>>> g = x**x for x in range(10)
    File "<stdin>", line 1
        g = x**x for x in range(10)
            ^
SyntaxError: invalid syntax
>>> g = (x**x for x in range(10))
>>> g
<generator object <genexpr> at 0x7f90ed650258>

However they can be omitted if a generator comprehension is the only argument for the function:

>>> list((x**x for x in range(4)))
[1, 1, 4, 27]
>>> list(x**x for x in range(4))
[1, 1, 4, 27]

That doesn’t work for function with more than one argument:

>>> print((x**x for x in range(4)), end='n')
<generator object <genexpr> at 0x7f90ed650468>
>>>
>>>
>>> print(x**x for x in range(4), end='n')
    File "<stdin>", line 1
SyntaxError: Generator expression must be parenthesized if not sole argument

Автор: pushtaev

Источник [3]


Сайт-источник PVSM.RU: https://www.pvsm.ru

Путь до страницы источника: https://www.pvsm.ru/python/346193

Ссылки в тексте:

[1] Previous publications: https://habr.com/ru/search/?q=%5Bpythonetc%20eng%5D&target_type=posts

[2] orsinium: https://habr.com/ru/users/orsinium/

[3] Источник: https://habr.com/ru/post/487732/?utm_campaign=487732&utm_source=habrahabr&utm_medium=rss