Подборка @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 

ссылка на оригинал статьи https://habr.com/ru/company/mailru/blog/487728/


Комментарии

Добавить комментарий

Ваш адрес email не будет опубликован. Обязательные поля помечены *