company_banner

Подборка @pythonetc, январь 2020



    Новая подборка советов про Python и программирование из моего авторского канала @pythonetc.

    Предыдущие публикации


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

    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 не будет работать, потому что IndexError является подклассом LookupError. Более конкретное исключение всегда должно находиться выше:

    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
    


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

    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]
    


    Python не будет автоматически использовать сложение с отрицательным числом вместо вычитания. Рассмотрим пример:

    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'
    

    Этот код не работает:

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

    Забавно, но этот код работает:

    v1 = Velocity(20_000_000)
    v2 = Velocity(10_000_000)
    
    print(v1 +- v2)
    # 10022302 m/s
    


    Эта часть написана Telegram-пользователем orsinium.

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

    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))
    # []
    

    Однако обычная функция может вернуть другой итератор:

    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]
    

    А такой вариант может быть полезен в случаях с простыми выражениями-генераторами:

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


    При создании 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>
    
    

    Однако их можно опустить, если comprehension является единственный аргумент функции:

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

    Это не верно для функций, у которых несколько аргументов:

    >>> 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
    
    Mail.ru Group
    Строим Интернет

    Похожие публикации

    Комментарии 0

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

    Самое читаемое