Size: a a a

PyWay – гуру Python 🐉

2019 August 31
PyWay – гуру Python 🐉
Юнит-тесты #2

Тестовые фикстуры (test fixtures) – особые условия, которые создаются для выполнения тестов. Сюда могут входить такие вещи:
▸ Подготовка тестовых данных
▸ Создание подключений к БД, сервисам и т.п.
▸ Создание заглушек (mock) для имитации компонентов программы
▸ Другие действия по поддержке рабочего окружения для проведения теста

Пример: у вас программа, которая вычисляет вычисляет число π до n-знака, и вам нужно протестировать, как будет выведен на экран миллионнный знак. Вы же не будете в тесте ждать вычисления всех предыдущих 999,999 знаков часами, а просто загрузите какие-то данные в память, чтобы создать условия, как будто мы уже на миллионом знаке.

В unittest фикстуры можно создавать на уровне модуля с тестами, отдельного класса (от unittest.TestCase) и каждого метода в классе теста.

Метод setUp() вызывается перед каждым вызовом метода test* в классе тестового случая.
Классовый метод setUpClass() вызывается один раз перед запуском тестов в классе тестового случая.
Функция setUpModule() вызывается перед выполнением тестовых случаев в этом модуле.

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

tearDown() – после каждого метода-теста в классе.
tearDownClass() – после всех тестов в классе.
tearDownModule() – после всех классов в модуле.

📎 В примере изучим порядок вызовов этих функций:

import unittest

class StringTestCase(unittest.TestCase):
   @classmethod
   def setUpClass(cls):
       print(' - set up class')

   def setUp(self):
       print(' - - set up method')
       self.foo = "foo"
       self.bar = "bar"

   def test_sum(self):
       self.assertEqual(self.foo + self.bar, "foobar")

   def test_lower(self):
       self.assertTrue(self.foo.islower())

   def tearDown(self):
       print(' - - tear down method')

   @classmethod
   def tearDownClass(cls):
       print(' - tear down class')

def setUpModule():
   print('set up module')

def tearDownModule():
   print('tear down module')

if name == '__main__':
   unittest.main()

Да
ст такую схему вызовов:

set up module
- set up class
- - set up method
- - tear down method
- - set up method
- - tear down method
- tear down class
tear down module


Даже если в одной из этих или тестовых функций произошло исключение, то прочие методы tearDown*() будут все равно запущены, чтобы освобождение ресурсов произошло корректно.
источник
2019 September 04
PyWay – гуру Python 🐉
Юнит-тесты #3: пропуск тестов

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

@unittest.skip("причина") – всегда пропускать тест.
@unittest.skipIf(условие, "причина") – пропускать тест, если условие сработало (True).
@unittest.skipUnless(условие, "причина") – пропускать тест, если условие НЕ сработало (False).
self.skipTest("причина") – если нужно остановить выполнение метода, выйти из него и не учитывать его в результатах. Так же может быть вызван в методе setUp(), который вызывается перед каждым тестовым методом.

📎 Пример:

class MyTestCase(unittest.TestCase):
   @unittest.skip("всегда пропустить")
   def test_nothing(self):
       self.fail("не случится")

   @unittest.skipIf(mylib.__version__ < (1, 3),
                    "эта версия библиотеки не поддерживается")
   def test_format(self):
       # этот тест работает только для определенных версий
       pass

   @unittest.skipUnless(sys.platform.startswith("win"), "надо Windows")
   def test_windows_support(self):
       # тест работает только на Windows
       pass

   def test_maybe_skipped(self):
       if not external_resource_available():
           self.skipTest("ресурс недоступен")
       # код дальше будет тестировать, если ресурс доступен
       pass


📎 Пример пропуска класса:

@unittest.skip("как пропустить класс")
class MySkippedTestCase(unittest.TestCase):
   def test_not_run(self):
       pass


Вы можете написать свой декоратор. Например, данный декоторатор пропускает тест, если объект obj не имеет атрибут attr:

def skipUnlessHasattr(obj, attr):
   if hasattr(obj, attr):
       return lambda func: func
   return unittest.skip("{!r} не имеет {!r}".format(obj, attr))

class SkipAttrTestCase(unittest.TestCase):
   @skipUnlessHasattr(mylib, "foofunc")
   def test_with_foofunc():
       # у mylib нет атрибута foofunc, тест будет пропущен
       pass


Еще один декоратор @unittest.expectedFailure говорит системе тестирования, что следующий метод должен провалиться (один из self.assert должен не сработать). Таким образом, разработчик говорит, что он осведомлен, что данный тест пока проваливается, и в будущем к этому примут меры.

class ExpectedFailureTestCase(unittest.TestCase):
   @unittest.expectedFailure
   def test_fail(self):
       self.assertEqual(1, 0, "сломано")


В конце выполнения будут счетчики пропусков и ожидаемых провалов тестов:

OK (skipped=5, expected failures=1)
источник
2019 September 07
PyWay – гуру Python 🐉
​​Юнит-тесты #4: проверки

В первой части мы обсудили методы проверки assertEqual, assertTrue и assertFalse, так как они самые распространенные на практике. Вообще достаточно одного assertTrue. Действительно, одно и тоже:

assertNotIn(item, list)
assertTrue(item not in list)


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

self.assertEqual(2 + 2, 5, "я не учил математику")

Однако, стоит упомянуть метод self.assertRaises(SomeException), который проверяет, возбуждает ли код нужное исключение. Обычно он применяется как контекст-менеджер (с with).

📎 Пример: деление на 0 должно бросать исключение ZeroDivisionError:

import unittest

def my_div(a, b):
   return a // b

class MyDivTestCase(unittest.TestCase):
   def test_1(self):
       self.assertEqual(my_div(10, 2), 5)

       # при делении на 0 ждем исключение:
       with self.assertRaises(ZeroDivisionError):
           my_div(7, 0)

       # или так: исключение, ф-ция, аргументы
       self.assertRaises(ZeroDivisionError, my_div, 5, 0)

unittest.main()


Если из исключения нужно извлечь данные (к примеру, код ошибки), то делают так:

with self.assertRaises(SomeException) as cm:
   do_something()

self.assertEqual(cm.exception.error_code, 3)
источник
2019 September 21
PyWay – гуру Python 🐉
Юнит-тесты #5: PyTest

Ранее мы обсуждали тестирование средствами встроенного модуля unittest. Естественно, есть и сторонние библиотеки для тестирования. Например, библиотека PyTest предоставляет более лаконичный и удобный инструментарий для написания тестов. Однако, ее нужно установить:

pip install pytest

Преимущества PyTest:

▸ Краткий и красивый код
▸ Только один стандартный assert
▸ Подробный отчет
▸ Разнообразие фикстур на всех уровнях
▸ Плагин и интеграции с другими системами

Сравните этот код с кодом из предыдущих постов про unittest:

import pytest

def setup_module(module):
   #init_something()
   pass

def teardown_module(module):
   #teardown_something()
   pass

def test_upper():
   assert 'foo'.upper() == 'FOO'
   
def test_isupper():
   assert 'FOO'.isupper()
   
def test_failed_upper():
   assert 'foo'.upper() == 'FOo'


Для тестов можно применять и классы (как в unittest), так и отдельные функции.

Запускать тесты тоже просто. В окружении, где установлен pytest, появится команда py.test. Из терминала пишем:

py.test my_test_cases.py

py.test обнаружит и выполнит тесты из этого файла.

Есть очень хорошая статья на Хабре про PyTest на русском, не вижу смысла дублировать ее сюда, а просто оставлю ссылку.
источник
2019 September 28
PyWay – гуру Python 🐉
✖️Умножение списка на число

Студент Макс узнал, что в Python умножать можно не только числа, но и другие объекты, например, строку на число:

>>> "Max" * 3
'MaxMaxMax'

"Вау!" - подумал Макс - "А что если умножить список на число?":

>>> [42, 26] * 3
[42, 26, 42, 26, 42, 26]

Значит можно создать двумерный массив очень кратко и элегантно?

>>> [[]] * 3
[[], [], []]

Заполнить его:

arr = [[]] * 3
arr[0].append(10)
arr[1].append(20)
arr[2].append(30)

Макс ожидал получить:

[[10], [20], [30]]

А вышло:

[[10, 20, 30], [10, 20, 30], [10, 20, 30]]

😯 Как же так?! Дело в том, что умножение списка на число не копирует сам объект, а лишь ссылку на него. Все три элемента arr ссылаются на один и тот же список. Легко проверить, сравнив адреса объектов:

>>> arr[0] is arr[1]
True
>>> id(arr[0]), id(arr[1])
(4400840776, 4400840776)


Аналогично в случае классов:

class Dummy: ...
arr = [Dummy()] * 2
arr[0].x = 10
arr[1].x = 20
print(arr[0].x, arr[0] is arr[1])  # 20 True


А вот с числами, строками и кортежами умножение списка будет работать как ожидал Макс, потому что это неизменяемые типы. Вот такая тонкость, которую нужно знать. Максу следовало бы написать так:

arr = [[] for _ in range(3)]  
arr[0].append(10)
arr[1].append(20)
arr[2].append(30)
>>> arr
[[10], [20], [30]]


Менее кратко, но зато работает без сюрпризов: каждую итерацию создается новый пустой список.
источник
PyWay – гуру Python 🐉
источник
2019 October 03
PyWay – гуру Python 🐉
Ура! Нас больше 500! 🥳🎈🍻👍

Не переживайте, скоро будут новые посты!
(Просто мы были в свадебном путешествии)

А пока напомню, что ознакомиться с постами можно не только в телеграм, но и на сайте.
источник
PyWay – гуру Python 🐉
Деление с остатком

Деление с остатком – часто используемая операция в программировании. Начиная от классических заданий для начинающих на вычисление минут и секунд:

total_seconds = 119
seconds = total_seconds % 60
minutes = total_seconds // 60
print(f'{minutes}:{seconds}')  # 1:59


Заканчивая тем, что на остатках построена львиная доля криптографии. Нахождения остатка часто называют modulo (или коротко mod).

При делении a на b неполное частное q и остаток r связаны формулой:

a = b · q + r, где b ≠ 0

В Python 3 частное и остаток вычисляются операторами:

q = a // b
r = a % b


Именно двойной слэш, одинарный слэш – деление без остатка (до конца). Иногда двойной слэш называют целочисленным делением, что не очень справедливо, потому что мы можем без проблем делить числа с запятой. Если оба числа целые (int), то частное будет тоже целым числом (int), иначе float. Посмотрите примеры:

10 / 3 == 3.3333333333333335
10 // 3 == 3
10.0 / 3.0 == 3.3333333333333335
10.0 // 3.0 == 3.0
10.0 % 3.0 == 1.0
10 % 3 == 1

2.4 // 0.4 == 5.0
2.4 / 0.4 == 5.999999999999999
2.4 % 0.4 == 0.3999999999999998

Последние три примера немного обескураживают из-за особенностей вычислений с плавающей точкой на компьютере, но формула a = b · q + r всегда остается справедлива.

Поговорим об отрицательных числах. Математически остаток не должен быть меньше нуля и больше или равен модулю делителя b: 0 ≤ r < |b|. Однако, Intel в своих процессорах случайно либо намеренно ввела отрицательные остатки в реализации ассемблерных команд деления. Компиляторы языков C и С++, являясь платформо-зависимыми, обычно полагаются на процессорное поведение. Пример на С++. И вообще посмотрите на эту огромную таблицу, каждый язык программирования пляшет, как хочет. Не будем спорить, кто из них прав. Просто узнаем, как у нас в Python:

a, b = [10, -10], [3, -3]
for x in a:
 for y in b:
   print(f'{x} // {y} = {x // y}')
   print(f'{x} % {y} = {x % y}')
   print()

10 // 3 = 3
10 % 3 = 1

10 // -3 = -4
10 % -3 = -2

-10 // 3 = -4
-10 % 3 = 2

-10 // -3 = 3
-10 % -3 = -1


Формула выполняется всегда, но результаты отличаются для С++ и Python, где при делении на положительное число – остаток всегда положителен, а на отрицательное число – отрицателен. Если бы мы сами реализовали взятие остатка, то получилось бы так:

def mod_python(a, b):
 return int(a - math.floor(a / b) * b)

# на С++ работает так:
def mod_cpp(a, b):
 return int(a - math.trunc(a / b) * b)


Где floor – ближайшее целое число не превышающее аргумент: floor(-3.3) = -4, а trunc – функция отбрасывания целой части: trunc(-3.3) = -3. Разница проявляется между ними только для отрицательных числел. Отсюда и разные остатки и частные – все зависит от того, с какой стороны числовой оси мы приближаемся к частному.

Вывод: если вам доведется писать или портировать код, где возможно деление отрицательных чисел с остатком, будьте предельно аккуратны, и помните про разницу поведения деления в разных языках.
источник
2019 October 05
PyWay – гуру Python 🐉
timedelta

В модуле datatime содержатся классы для работы с датой и временем. В частности часто используются datetime для хранения даты и времени некоторого события и timedelta для хранения интервала времени между каким-то двумя событиями.

Удобно работать с datetime и timedelta путем математических операций.

📎 Примеры. Добавить к дате один день, год или отнять 2:20 (функция str тут для человекочитаемого формата):

>>> str(datetime.now() + timedelta(days=1))
'2019-10-06 15:51:09.089691'
>>> str(datetime.now() + timedelta(days=365))
'2020-10-04 15:52:04.618896'
>>> str(datetime.now() - timedelta(hours=2, minutes=20))
'2019-10-05 13:41:27.617589'


Разница во времени между событиями:

>>> a = datetime.now()
>>> b = datetime.now() + timedelta(minutes=5)
>>> b - a
datetime.timedelta(0, 317, 99915)
>>> str(b - a)
'0:05:17.099915'


timedelta внутренне хранит только секунды от начала дня и сами дни, хотя в конструкторе принимает дни, секунды, микросекунды, миллисекунды, минуты, часы и недели (можно выбрать любые их сочетания для задания интервала):

datetime.timedelta(days=0, seconds=0, microseconds=0, milliseconds=0, minutes=0, hours=0, weeks=0)

>>> str(timedelta(days=1, hours=2, milliseconds=333))
'1 day, 2:00:00.333000'


Причем мы не обязаны нормализовывать аргументы: он сам поймет, что 200 минут – это 3 часа 20 минут:

>>> str(timedelta(minutes=200))
'3:20:00'


Достать часы и минуты (странно, что у объекта нет свойств hours и minutes):

def hours_minutes(td):
   return td.seconds // 3600, (td.seconds // 60) % 60

>>> hours_minutes(timedelta(0, 12345))
(3, 25)


Сколько всего секунд в интервале:

>>> timedelta(minutes=200, seconds=21, hours=25).total_seconds()
102021.0


Можно даже уможнать timedelta на числа или поделить два timedelta или взять остаток. Допустим рабочая смена длится 7 часов 30 минут, сколько полных смен в 3-х сутках?

>>> a = timedelta(days=3)
>>> b = timedelta(hours=7, minutes=30)
>>> a // b
9
>>> str(a % b)
'4:30:00'


Ответ 9 полных смен и еще останется 4 часа 30 минут лишних.

Бонус. Формат даты по-нашенскому (ДД.ММ.ГГГГ):

>>> datetime.strftime(datetime.now(), '%d.%m.%Y')
'05.10.2019'
источник
2019 October 08
PyWay – гуру Python 🐉
Перенос строк кода Python

PEP-8 не рекомендует писать строки кода длиннее, чем 79 символов. С этим можно не согласиться, однако, встречаются строки, которые не влезают даже на наши широкоформатные мониторы.

👨‍🎓 Старайтесь не делать очень длинные строки, разбивая сложные условия или формулы на отдельные части, вынося их в переменные или функции с осмысленными названиями.

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

Если, перед выражением открыта скобка (круглая, квадратная или фигурная в зависимости от контекста), но она не закрыта в этой строке, то Python будет сканировать последующие строки, пока не найдет соответствующую закрывающую скобку (англ. implicit line joining). Примеры:

# вычисления
income = (gross_wages
         + taxable_interest
         + (dividends - qualified_dividends)
         - ira_deduction
         - student_loan_interest)

if (student_loan_interest > ira_deduction
       and qualified_dividends == 0):
   ...

# словари
d = {
   "hello": 10,
   "world": 20,
   "abc": "foo"
}

# аргументы функции
some_func(arg1,
   arg2,
   more_arg,
   so_on_and_on)


Обратите внимание, что в первом примере скобки очень важны. Без скобок код не скомпилируется из-за отступов, а если их убрать, то результат будет неверен: income станет gross_wages, а последующие строки не будут иметь эффекта!

# неправильно!
income = gross_wages
+ taxable_interest
+ (dividends - qualified_dividends)
- ira_deduction
- student_loan_interest

Метод переноса обратным слэшем. Ставим обратный слэш конце строки и сразу энтер (перенос строки): тогда следующая строка будет включена в текущую (англ. explicit line joining), не взирая на отступы, как будто бы они написаны в одну строку:

income = gross_wages \
        + taxable_interest \
        + (dividends - qualified_dividends) \
        - ira_deduction \
        - student_loan_interest


Еще примеры со слэшем:

if student_loan_interest > ira_deduction \
       and qualified_dividends == 0:
   ...

# допустимо, согласно PEP-8
with open('/path/to/some/file/you/want/to/read') as file_1, \
    open('/path/to/some/file/being/written', 'w') as file_2:
   file_2.write(file_1.read())

# пробелы в строку попадут, а энтер - нет!
str = "Фу\
     < вот эти пробелы тоже в строке"


Почему скобки лучше для переноса:

• Лучше восприятие
• Скобок две, а слэшей надо по одному на каждый перенос
• Можно забыть слэш и сломать код
• Можно поставить пробел после слэша и тоже сломать
источник
2019 October 10
PyWay – гуру Python 🐉
🥁 PyBuka

Время проектов! Немного увлекаюсь барабанами, в частности дарбукой (или думбеком). Написал небольшой проектик на Python для проигрывания ритмов дарбуки. Он преобразует общепринятую текстовую запись в зацикленный звук с заданным ритмом.

Для работы нужен pygame (pip install pygame). Запустить плеер можно из терминала (первый аргумент – ритм, второй – число ударов в минуту):

python pybuka.py "D-T---T-D---T-tkD-T---T-D--kS---" 160

D – низкий глубокий удар
T – звонкий громкий удар об обод
t или k – звонкие, но тише, чем t
S – слэп (удар плашмя по центру)
Дефис – пауза.

Особенность воспроизведения звука в pygame: для каждого типа удара о барабан создается отдельный канал channel = mixer.Channel(ch_id), чтобы рядом стоящие по времени ноты не мешали друг другу.

Пример записи звука прилагается.

Ссылка на исходник проекта на GitHub.  
⭐ Если вам понравился проект, поставьте звездочку, пожалуйста. Вам не сложно, а мне очень приятно и огромная мотивация для развития своих проектов с открытым исходным кодом.
источник
PyWay – гуру Python 🐉
источник
2019 October 14
PyWay – гуру Python 🐉
​​🌷Декораторы: часть 1

Вероятно, почти каждый разработчик на Python сталкивался с декораторами, видя конструкцию с со знаком @:

@app.route('/')
def index():
   return "Hello, World!"


Разберемся, что такое декоратор, и как он работает. Этот вопрос часто спрашивают на собеседованиях.

Декоратор – это функция, которая принимает как аргумент другую функцию. Цель декоратора – расширить функциональность переданной ему функции без непосредственного изменения кода самой функции. Вот и все!
* Примечание: декорировать можно класс, и декоратором может быть тоже класс, но об этом в другой раз!

В Python функция – тоже объект, и ее можно передавать как аргумент, возвращать из другой функции, ей также можно назначать атрибуты.

Символ собачка (@) – всего лишь синтаксический сахар:

@decorator
def foo():
   ...


# эквивалентно:

def foo():
   ...
foo = decorator(foo)


Теперь, зная, что декоратор всего лишь функция – попробуем разобраться, что она может делать и как должна выглядеть. Декоратор может возвращать, что угодно, но по смыслу вернуть надо тоже функцию, чтобы заменить ей оригинальную функцию. Тривиальный декоратор возвращает свой аргумент, не делая ничего:

def decorator(f):
   return f


А можно вернуть и вообще другую функцию. Например, определенную внутри декоратора (да, внутри функций можно определять другие функции):

def decorator(f):
   def inner():
       print('inner')
   return inner


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

def decorator(f):
   def inner():
       print('begin')
       f()
       print('end')
   return inner


@decorator
def foo():
   print('foo')

foo()


Выдаст:

# begin foo
# foo
# end foo


Продолжу тему в следующих постах.
источник
2019 October 15
PyWay – гуру Python 🐉
🌷 Декораторы: wraps

Продолжаем тему. Часто неизвестно, какие аргументы принимает декорируемая функция f. Поэтому эти аргументы обычно обобщают, называя их *args (все позиционные аргументы, как список), **kwargs (все именованные аргументы, как словарь). Эти две штуки охватывают все возможные аргументы. Также у функции может быть возвращаемое значение, которое неплохо бы вернуть из inner. Улучшим наш декоратор, добавив прозрачную передачу любых (заранее неизвестных) аргументов и возвращение результата выполнения функции:

def decorator(f):
   def inner(*args, **kwargs):
       print('begin')
       result = f(*args, **kwargs)
       print('end')
       return result
   return inner

@decorator
def foo(x, y):
   print(f'summing {x} and {y}...')
   return x + y

print(foo(5, y=10))
# begin
# summing 5 and 10...
# end
# 15

Помимо кода функции и ее аргументов, у нее также есть и другие свойства, например ее настоящее имя и «docstring» (это строчка с описанием функции в начале ее тела, которая хранится потом в атрибуте __doc__ и выводится при вызове help). Декоратор из прошлого примера потеряет документацию и имя функции (она станет зваться inner):

@decorator
def foo(x, y):
   """Doc string here"""
   return x + y

help(foo)
# Help on function inner in module main:
# inner(*args, **kwargs)

Для того, чтобы предотвратить потерю атрибутов декорированной функции есть декоратор wraps в модуле functools. И да, это еще один декоратор, которым мы декорируем inner в нашем декораторе. Вот теперь название и документация поступят в обертку из оригинальной функции:

from functools import wraps

def decorator(f):
   @wraps(f)
   def inner(*args, **kwargs):
       print('begin')
       result = f(*args, **kwargs)
       print('end')
       return result
   return inner

@decorator
def foo(x, y):
   """Doc string here"""
   return x + y

help(foo)
# Help on function foo in module main:
# foo(x, y)
#    Doc string here

Композиция декораторов

Можно применить несколько декораторов к одной функции. Вообще говоря, результат зависит от порядка следования декораторов: тот, что ближе к определению функции воздействует на нее раньше того, что дальше. Пример декораторов для пары HTML тэгов (для простоты я опустил формальности передачи аргументов и атрибутов из предыдущего раздела). foo и bar декорированы в разном порядке:

def bold(f):
   def inner():
       return '<b>' + f() + '</b>'
   return inner

def italic(f):
   def inner():
       return '<i>' + f() + '</i>'
   return inner

@bold
@italic
def foo():
   return 'foo text'

@italic
@bold
def bar():
   return 'bar text'

print(foo())  # <b><i>foo text</i></b>
print(bar())  # <i><b>bar text</b></i>


И результат разный, потому что по сути:

foo = italic(bold(foo))
bar = bold(italic(bar))


В следующем посте я расскажу о декораторах с параметрами (там будут трех-этажные функции).
Эта заметка доступна также на сайте.
источник
2019 October 16
PyWay – гуру Python 🐉
🌷 Декораторы с параметрами

Простые декораторы мы научились делать, но как передать в них параметр?

@repeat(n=5)
def foo():
   print('foo')


Нужно обернуть объявление декоратора еще в одну функцию, принимающую эти параметры:

from functools import wraps

def repeat(n=5):
   def _repeat(f):
       @wraps(f)
       def inner(*args, **kwargs):
           for _ in range(n):
               f(*args, **kwargs)
       return inner
   # не забываем ее вернуть!
   return _repeat


Благодаря механизму замыканий параметр n будет доступен во вложенных функциях.

Как это работает, подробнее я рассказал в статье по кнопке. Еще там показано, как создать универсальный декоратор, работающий с параметрами и без. Я, наконец-то, прикрутил превью к сайту, поэтому открывать статьи стало супер удобно!
источник
PyWay – гуру Python 🐉
источник
2019 October 17
PyWay – гуру Python 🐉
🐍 Python 3.8 здесь!

Вау-вау! Отложим дела ради классной новости! Python версии 3.8 официально релизнулся!

Что в новой версии?

1️⃣ Оператор морж (писал о нем ранее). Присваивание переменной внутри других выражений:

if (n := len(a)) > 10:
   print("слишком длинно")

while (block := f.read(256)) != '':
   process(block)

[clean_name.title() for name in names
if (clean_name := normalize('NFC', name)) in allowed_names]


2️⃣ Разделитель позиционных аргументов (слэш /). Указывает, что первые несколько аргументов могут быть только позиционными (в строгом порядке, без указания имени). Напомню, что именные аргументы передаются с указанием имени, и не важно в каком порядке. В примере ниже a и b – только позиционные, c и d - могут быть позиционные или переданы по имени, а e и f – исключительно именные:

def f(a, b, /, c, d, *, e, f):
   print(a, b, c, d, e, f)

# разрешенный вызов:
f(10, 20, 30, d=40, e=50, f=60)

# НЕЛЬЗЯ передать b по имени
# (b стоит до слэша)
f(10, b=20, c=30, d=40, e=50, f=60)

# НЕЛЬЗЯ передать e без указания имени
# (e стоит после звездочки)
f(10, 20, 30, 40, 50, f=60)


3️⃣ Спецификатор = для f-строк. Тут проще на примере, раньше мы писали с повторами:

>>> user = 'eric_idle'
>>> since = date(1975, 7, 31)
>>> f'user={user} since={since}'
"user='eric_idle' since=datetime.date(1975, 7, 31)"


А теперь можно так:

>>> f'{user=} {since=}'
"user='eric_idle' since=datetime.date(1975, 7, 31)"


После знака равно можно добавлять и прочие спецификаторы форматирования:

>>> delta = date.today() - since
>>> f'{user=!s} {delta.days=:,d}'
'user=eric_idle delta.days=16,075'


Для отладки принтами - просто восторг!

4️⃣ Теперь можно continue внутри finally

Еще есть множество улучшений со стороны C-API, всякие хуки аудита, вектор-коллы. Новая настройка PYTHONPYCACHEPREFIX, чтобы вынести кэш байткода из стандартной директории pycache куда вам удобно. Очень-очень много разных мелких изменений в стандартных модулях и функциях, о которых расскажу при случае.

Что нового по-английски.

Как вам новая версия?
источник
2019 October 21
PyWay – гуру Python 🐉
источник
2019 October 22
PyWay – гуру Python 🐉
starmap

Встроенная функция map принимает функцию и итерируемый объект, а возвращает тоже итератор, применяя ту функцию к каждому элементу исходного итератора. А, чтобы получить список, мы извлекаем из итератора все значения, приведя его к списку функцией list. Пример map: прибавлятор единички ко всем элементам массива:

>> list(map(lambda x: x + 1, [1, 2, 3, 4]))
[2, 3, 4, 5]


Что делать, если нужно применить функцию, которая принимает большее количество аргументов? Например, возведение в степень pow принимает основание и показатель:

>>> pow(2, 4)
16


Как и требуют, мы даем в map функцию с одним аргументом, но каждый элемент t – кортеж из двух элементов, мы распаковываем его в аргументы pow звездочкой:

>>> list(map(lambda t: pow(*t), [(2, 4), (3, 2), (5, 2)]))
[16, 9, 25]


Если вы не знали: pow(*t) то же самое, что и pow(t[0], t[1]), если в t два элемента.

К счастью, не обязательно делать этот хак с лямбдой, потому что в модуле itertools есть функция starmap, которая как раз звездочкой распаковывает каждый элемент исходного итератора в аргументы функции:

>>> from itertools import starmap
>>> list(starmap(pow, [(2, 4), (3, 2), (5, 2)]))
[16, 9, 25]
источник
PyWay – гуру Python 🐉
источник