Size: a a a

PyWay – гуру Python 🐉

2019 June 20
PyWay – гуру Python 🐉
🎚️Интерактивность в Jupyter Notebook

Как я и говорил, в Jupyter Notebook очень много всяких удобностей. В частности в блокнот можно добавить элементы управления, такие как:
• Слайдер для выбора значения числа
• Текстовое поле для ввода чисел или строк
• Выпадающий список выбора
• Чекбоксы (галочка да/нет)
• Выбор даты
• Выбор цвета и другие...

Установка виджетов (если еще не установлены):
pip install ipywidgets
jupyter nbextension enable --py widgetsnbextension


Или через conda одной командой:

conda install -c conda-forge ipywidgets

📎 Пример. Нарисуем синусоиду с изменяемой частотой и фазой:
%matplotlib inline
import numpy as np
import matplotlib.pyplot as plt

from ipywidgets import interact

@interact(f=(1, 5, 0.1), phase=(0, 3.14, 0.1))
def plot_f(f, phase):
   x = np.linspace(0, 10, 100)
   y = np.sin(f * x + phase)
   plt.plot(x, y, 'r')
   plt.show()


Мы делаем интерактивными параметры f и phase. Их имена совпадают с аргументами обернутой функции plot_f(f, phase). А значения - кортеж вида (min, max, step), т.е. минимальное значение, максимальное и шаг слайдера. Как только мы изменим положение движков, то график будет автоматически перерисован.

Полный список виджетов (на англ.)

Вот сделал ноутбук, чтобы посмотреть и поиграться с интерактивными элементами. Там больше разных примеров. Нажмите "Open in playground", если на дает запустить код.
источник
2019 June 27
PyWay – гуру Python 🐉
Многоликий else

Все знают, что ключевое слово else служит для выполнения альтернативной ветки кода, если условие if не выполнилось:

x = 5
if x < 3:
   print("x < 3")
else:
   print("x >= 3")


Но знали ли вы, что есть еще два примения else?

1. for/else, while/else

Если поставить else после тела цикла, то код по else будет выполнен только в том случае, если цикл завершился "нормально", т.е. в цикле не исполнилось break. Пример:

stack = [1, 3, 5, 7]
while stack:
   if stack.pop() == 4:
       break
else:
   print('not found!')


Пример. Простые числа и множители:

for n in range(2, 10):
   for x in range(2, n):
       if n % x == 0:
           # есть делитель, уходим
           print(n, '=', x, '*', n/x)
           break
   else:
       # цикл не нашел делителей
       print(n, 'простое число!')


2. try/else

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

try:
   ...
except Exception:
   print('Exception!')
else:
   print('Ok!')
finally:
   print('Bye!')


Причем else не будет также вызван, если сработавшее исключение не подпало под перечисленные except и не было обработано.
источник
2019 June 29
PyWay – гуру Python 🐉
➰ Перенаправление стандартного вывода

Случается так, что некий код (возможно, не ваш) пишет в стандартный вывод какую-то нужную информацию. Ее нетрудно перехватить с помощью функции redirect_stdout из стандартного модуля contextlib.

redirect_stdout является контекст менеджером (применяется совместно с with) и принимает аргументом файло-подобный объект (это может быть и дескриптор файла, и StringIO).

📎 Пример. Сохраним вывод функции help в строку (в интерпретаторе пример работает некорректно, запускайте с файла):
import io
from contextlib import redirect_stdout

f = io.StringIO()
with redirect_stdout(f):
 help(pow)
s = f.getvalue()
print(s) # в s будет вывод


📎 Пример. Или в файл:
with open('help.txt', 'w') as help_file:
 with redirect_stdout(help_file):
   help(pow)    


📎 Пример. stdout в stderr:
import sys
with redirect_stdout(sys.stderr):
 help(pow)


Во время работы redirect_stdout вывод в терминал попадать не будет.

Функция redirect_stderr аналогично перехватывает вывод из стандартного потока ошибок (stderr).
источник
2019 July 04
PyWay – гуру Python 🐉
​​🍧 Куча и очередь с приоритетом

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

Очередь с приоритетом эффективно реализовать с помощью кучи. Двоичная куча – бинарное дерево, где значение в любой вершине не меньше (больше), чем значения её потомков, при этом заполнение уровней должно быть последовательным (без дырок и перескоков на следующий уровень, если текущий уровень не закончен). Работа с кучей осуществляется за время порядка O(log n). А построение кучи из массива – за O(n), где n – число элементов.

Кучу удобно хранить в массивах. Напомню, что списки (list) в Python – это и есть массивы переменной длины, а не связные списки (linked list), как в других языках. Поэтому доступ к элементу по индексу осуществляется за O(1). Пускай в корень дерева мы положим наименьший элемент, а корнем дерева будет нулевой элемент списка heap[0]. Его потомками будут элементы heap[1] и heap[2]. В общем виде потомками элемента heap[k] будут heap[2*k+1] и heap[2*k+2]. Такая индексация позволяет хранить в массиве двоичное дерево компактно и удобно получать доступ к его элементам. По условиям кучи должны соблюдаться условия heap[k] <= heap[2*k+1] и heap[k] <= heap[2*k+2].

В Python есть модуль heapq, который предоставляет процедурный интерфейс по работе с двоичными кучами, причем его функции принимают в качестве кучи именно обычные объекты типа list.

Создать кучу можно двумя способами:
1) начать с пустого списка [] и заполнить его методом heappush
2) взять существующий список и превратить его в кучу на месте (in-place) функцией heapify. heapify не возвращает новый список, а модифицирует переданный:

import heapq
x = [3, 1, 4, 2, 5]
heapq.heapify(x)
>>> x
[1, 2, 4, 3, 5]


Основные функции:

heappush(heap, item) – добавляет в кучу элемент item.
heappop(heap) - возвращает наименьший элемент и удаляет его из кучи. Если не надо удалять, то просто heap[0].
▶ heappushpop(heap, item) – добавляет в кучу item, а после возвращает наименьший элемент (работает немного быстрее, чем в две операции).
heapreplace(heap, item) – возвращает наименьший элемент, а потом уже добавляет новый item (работает немного быстрее, чем в две операции).

ℹ️ Помните! Отсортированный список является кучей – это лекго проверить по условиям. Поэтому если ваш массив заранее отсортирован, то не надо вызывать heapq.heapify.

ℹ️ Но не любая куча – отсортированный список. Построение из массива – кучи, а из кучи – отсортированного массива – это сортировка кучей: мы наполняем последовательно кучу, а потом по порядку извлекаем наименьшие элементы, пока куча не истощится:

def heapsort(iterable):
   h = []
   for value in iterable:
       heapq.heappush(h, value)
   return [heapq.heappop(h) for i in range(len(h))]

>>> heapsort([1, 3, 5, 7, 9, 2, 4, 6, 8, 0])
[0, 1, 2, 3, 4, 5, 6, 7, 8, 9]


В примерах выше, мы пользовались числами, как элементами кучи, потому что числа легко сравнимы. В реальной жизни мы оперируем произвольными объектами с их приоритетами. Для перехода от кучи к очереди с приоритетами можно использовать следующий трюк: использовать как элементы кортежи вида (приоритет, объект) или (приоритет, идентификатор, объект), если объекты несравнимы. Приоритет – число (например: важность задачи или время запуска какого-либо процесса). Идентификатором может служить порядковый номер.

q = []
heapq.heappush(q, (-priotiry, ident, obj))
ident += 1


Куча возвращает минимальный элемент (heappop), а кортежи сравниваются поэлементно слева направо. Сначала идет -priotiry. Знак минус, чтобы сначала возвращался не минимальный приоритет, а максимальный. Если приоритеты равны, то будет возвращен элемент с наименьшим идентификатором ident, т.е. тот, что был добавлен раньше всех для данного значения приоритета.

Полный пример кода очереди с приоритетами доступен по ссылке.
источник
2019 July 10
PyWay – гуру Python 🐉
🖐️ Перечисления (Enum)

В Python нет специального синтаксиса для перечислений, зато есть модуль enum и класс Enum в нем, откоторого можно отнаследоваться для создания собственного перечисления:

from enum import Enum
class Color(Enum):
   RED = 1
   GREEN = 2
   BLUE = 3


Задавать переменные этого типа можно несколькими способами:

c = Color(2)     # по значению
c = Color['RED'] # по строковому имени
c = Color.RED    # по члену класса

Значения из Enum человеко-читаемы при печати:

>>> print(Color.RED)
Color.RED

А также:

>>> Color.RED.name
'RED'
>>> Color.RED.value
1

Для сравнения эквивалентности используют оператор is (хотя == и != тоже работают):

if c is Color.RED:
   print('Red!')
if c is not Color.BLUE:
   print('Not blue!')


Для нескольких значений можно использовать in:

if c in (Color.BLUE, Color.GREEN):
   print('No red at all!')

Если неохота задавать значение самостоятельно, можно делать это автоматически:

from enum import Enum, auto
class Numbers(Enum):
   ONE = auto()
   TWO = auto()
   THREE = auto()
   FOUR = auto()


Члены перечислений хэшируемы и могут быть ключами словаря:

apples = {}
apples[Color.RED] = 'sweet'
apples[Color.GREEN] = 'sour'
>>> apples
{<Color.RED: 1>: 'sweet', <Color.GREEN: 2>: 'sour'}
источник
2019 July 12
PyWay – гуру Python 🐉
🖐️ Функциональный Enum

Кратко создать перечислимый тип можно в функциональном стиле:

from enum import Enum
Animal = Enum('Animal', 'ANT BEE CAT DOG')


Семантика такого определения напонимает namedtuple. Первый аргумент – название перечисления, а второй – строка, где через пробел указаны названия вариантов. Пользоваться таким Enum можно также, как и заданным, через класс (см. предыдущий пост), единственное, что могут быть проблемки с pickle.

Допускается множество способов определить имен и значений вариантов перечисления: в строке через пробел или запятую, списком, списком кортежей имя-значение, словарем:

Animal = Enum('Animal', 'ANT, BEE, CAT, DOG')
Direction = Enum('Direction', ['NORTH', 'SOUTH', 'WEST', 'EAST'])
Color = Enum('Color', [('CYAN', 4), ('MAGENTA', 5), ('YELLOW', 6)])
Mood = Enum('Mood', {'HAPPY': ':-)', 'SAD': ':-('})


P.S. К сожалению, не все современные IDE понимают такое определение Enum, даже последний PyCharm ругается на аргументы и не активирует автодополнение по вариантам. Надеюсь, в будущем ситуация изменится в лучшую сторону.
источник
2019 July 17
PyWay – гуру Python 🐉
​​🔵 Сортировка пузырьком

Сегодня простая, но важная тема. Алгоритм сортировки пузырьком, его проходят на курсах, его часто спрашивают на собеседованиях. Сортировка - это процесс выстраивания массива или списка по возрастанию или убыванию. На примере чисел: [3, 1, 4, 2] → [1, 2, 3, 4].

Смысл пузырьковой сортировки заключается в следующем: мы начинаем с начала списка и сравниваем элементы попарно (нулевой и первый), если нулевой больше первого, то меняем их местами. Независимо от того, была ли замена или нет, мы шагаем вправо и сравниваем элементы вновь. Если на прошлом шаге была замена, то на этом шаге у нас окажется тот же элемент, и если он опять оказался больше, то "всплывет" снова вправо. Так за один проход наибольший элемент всплывет в самый-самый конец списка, подобно тому, как пузырек воздуха всплывает в бутылке воды. Когда все пузырьки всплывут – список будет отсортирован.

📎 Пример: a = [3, 1, 4, 2] – 4 элемента:

Первый проход:
1. Сравним a[0] = 3 и a[1] = 1, 3 > 1. Меняем их местами. Теперь a = [1, 3, 4, 2].
2. Сравним a[1] = 3 и a[2] = 4, 3 < 4. Менять не надо.
3. Сравним a[2] = 4 и a[3] = 2, 4 > 2. Меняем. a = [1, 3, 2, 4].
Проход окончен. 4 "всплыла" в самый конец списка на свое место a[3]. Поэтому мы не трогаем больше конец списка, но список еще неотсортирован до конца, и следующий проход будет рассматривать только первые 3 элемента списка.

1. Сравним a[0] = 1 и a[1] = 3, 1 < 3. Менять не надо.
2. Сравним a[1] = 3 и a[2] = 2, 3 > 2. Меняем их. a = [1, 2, 3, 4]. Проход окончен.

1. Сравним a[0] = 1 и a[1] = 3, 1 < 3. Менять не надо. Cписок отсортирован. Можно выходить.

👨‍💻 Переходим к реализации на Python:

def bubble_sort(a):
   n = len(a)
   
   # номер прохода i = 0..(n-2), т.е. (n-1 раз):
   for i in range(n - 1):
       # номер сравнения j = 0..(n - i - 2)
       for j in range(n - i - 1):
           # сравниваем только соседние элементы
           if a[j] > a[j + 1]:
               a[j], a[j + 1] = a[j + 1], a[j]


Алгоритм прост, но можно запутаться в индексах: с какого элемента и куда бежать, что с чем сравнивать. Как лучше запомнить:
▻ Начинаем всегда с начала (0-го элемента).
▻ Число проходов меньше на 1, чем число элементов
▻ С каждым проходом мы делаем все меньше и меньше сравнений, так как сортированный хвост списка растет на 1 после каждого прохода
▻ Сравниваем только соседние элементы a[j] > a[j + 1], (а не i и j).
▻ Если знак сравнения перевернуть, то сортировка будет по убыванию.

Временная сложность алгоритма квадратичная O(n^2) – имеются два вложенных цикла по элементам. Поэтому алгоритм медлителен для больших списков.  В реальной жизни чаще применяются другие алгоритмы сортировки, но пузырек до сих пор не забывают преподавать и спрашивать.
источник
2019 July 20
PyWay – гуру Python 🐉
Универсального рецепта у меня нет, так как скрипт работает только для моего смартфона (координаты свайпа абсолютные). Мне понадобились adb и включение режима отладки по USB. Скрипт каждые пару секунд отправляет через adb команду сделать свайп по координатам:

import time
import os

COMMAND = 'adb shell input swipe 308 2028 900 2029 855'

while True:
   print('Go!')
   os.system(COMMAND)
   time.sleep(2)
источник
PyWay – гуру Python 🐉
источник
2019 July 22
PyWay – гуру Python 🐉
🚪 exit и компания

У каждого, наверное, было: пишешь в интерпретаторе exit, а он:

>>> exit
Use exit() or Ctrl-D (i.e. EOF) to exit


Что же такое exit? Оказывается это такой класс, а текст - это всего лишь его repr:

>>> type(exit)
<class '_sitebuiltins.Quitter'>
>>> repr(exit)
'Use exit() or Ctrl-D (i.e. EOF) to exit'


А еще есть quit – он тоже из этой семьи:

>>> type(quit)
<class '_sitebuiltins.Quitter'>


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

try:
   # выбери любое из:
   exit()
   quit()
except SystemExit:
   print('Невозможно покинуть Омск')


Есть еще sys.exit, который тоже бросает SystemExit, что может быть пойман.

🛑 Вывод: нельзя надеятся на exit() для гарантированного завершения программы, ведь ваш код может быть обернут в try / except Exception, который может подавить SystemExit. Как же быть? Есть способ – это os._exit, который завершит программу на системном уровне:

import os
try:
   os._exit(-1)
except SystemExit:
   print('Невозможно покинуть Омск')
finally:
   print('Я свободен!')


Ни первый, ни второй print не сработают!

✋ Надо упомянуть еще os.abort(), которая также немедленно завершает программу сигналом SIGABRT, что еще дополнительно приводит к созданию дампа памяти. Причем, не будет вызван даже обработчик сигнала, установленный через signal.signal(). Функция os.abort() подходит только для аварийного завершения приложения.
источник
2019 July 27
PyWay – гуру Python 🐉
🚩 Флаги преобразования

При форматировании строк доступны 3 флага преобразования объекта в строку: !r, !s и !a.

>>> x = "дом"
>>> f'{x}'
'дом'
>>> f'{x!r}'
"'дом'"
>>> f'{x!s}'
'дом'
>>> f'{x!a}'
"'\\u0434\\u043e\\u043c'"


Для фанатов format:

>>> '{0!r}'.format(x)
"'дом'"
>>> '{!r}'.format(x)
"'дом'"


Флаг !r вызывает repr(x), а флаг !s вызывает str(x). Флаг !a вызывает ascii(repr(x)). Функция ascii превращает все символы за пределами набора ASCII (включая русские буквы в юникоде) в их коды. Если флаг не указан, то по умолчанию считается, что он !s.

Для классов repr и str могут иметь различное определение:

class Foo:
   def __repr__(self):
       return "репр"
   def __str__(self):
       return "строка"
x = Foo()
print(f'{x!r}')  # репр
print(f'{x!s}')  # строка


Если str нет, то будет вызван repr.

Рекомендации: str должен давать нам человеко-читаемое описание объекта, а repr – уникальное представление объекта, по которому можно частично или полностью восстановить состояние этого объекта или хотя бы помочь с отладкой. str – для пользователей, repr - для питонистов.

📎 Отличный пример для наглядности – datetime:

>>> import datetime
>>> dt = datetime.datetime(2019, 7, 27)
>>> repr(dt)
'datetime.datetime(2019, 7, 27, 0, 0)'
>>> str(dt)
'2019-07-27 00:00:00'
>>> eval(repr(dt)) == dt
True


str от datetime просто покажет нам дату и время в удобном формате; repr от datetime вернет строку, в которой будет вызов описан конструктора конкретно этого объекта, да так, что при исполнении этой строки как кода на Python функцией eval – мы получим объект datetime для той же даты. Впрочем, никто нас не обязывает делать этот трюк для каждого объекта.
источник
2019 August 02
PyWay – гуру Python 🐉
🔱 Coffee, tee or me

Итератор, как известно, выдает значения по одному (например, методом next), и его нельзя "отмотать" назад. Это означает, что получать все подряд значения из итератора может только один потребитель. Однако, если несколько потребителей хотят читать из одного итератора, его можно разделить с помощью функции itertools.tee. Кстати, tee переводится как тройник (тройник похож на букву Т).

tee принимает исходный итератор и число – количество новых итераторов, на которые разделится исходный, а возвращает кортеж из новых итераторов. При этом извлечение значений из одного из итераторов не влияет на остальные.

📎 Пример:

from itertools import tee

def source():
   for i in range(5):
       print(f'next is {i}')
       yield i

# три потребителя
coffee, tea, me = tee(source(), 3)

# первый берет два числа
next(coffee); next(coffee)
# второй одно
next(tea)
# третий - все
for i in me:
   ...


Вывод:

next is 0
next is 1
next is 2
next is 3
next is 4


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

Предостережения:

1. Исходный итератор не следует итерировать, иначе производные итераторы лишатся некоторых значений.
2. Механизм tee таков, что он хранит в памяти извлеченные элементы, чтобы остальные потребители могли их получить, даже если исходный итератор уже сместился. Поэтому, если элементов много или они большие, это может серьезно повлиять на расход памяти.

Полный пример кода (плюс моя реализация tee на скорую руку) – по ссылке.
источник
2019 August 07
PyWay – гуру Python 🐉
🍤 Quine

Программисты тоже умеют развлекаться, так что давайте сегодня развлечемся и напишем quine (квайн). Квайн – это такая программа, которая выводит на экран свой же код, ни больше, ни меньше. Сразу договоримся, что пустая программа на Python, которая ничего не выводит, не считается квайном; это не интересно.

В Python у нас есть чудо-переменная, которая хранит путь к текущему интерпретируемому файлу, поэтому можно сделать так:

print(open(__file__).read())

Эта программа открывает свой же файл, читает и печает его целиком. Но это жульничество, потому что в квайнах не принято читать файлы. Хорошо, а что если назвать файл print(__file__), записать в него print(__file__) и выполнить python "print(__file__)". Будет работать, но можешь вот без этих трюков, чисто кодом? Да без проблем!

Нам нужно что-то печатать, значит берем print:

>>> print('?')
?

Программа начинается с print..., значит и печатать будем тоже самое:

>>> print('print()')
print()

Не получается, потому что у нас тут уже два print да кавычки, а печатается только один. Так можно плодить print до бесконечности, но все равно не будет хватать одного в выводе. Будем решать поэтапно. Давайте заведем переменную s с кодом нашей программы.

>>> s='print()';print(s)
print()

Но код теперь начинается с s=, исправим:

>>> s='s=?;print(s)';print(s)
s=?;print(s)


Смотрите, уже похоже, осталось только на место знака вопроса воткнуть содержимое строки s из оригинального кода. Это самый важный момент. Используем format, а точнее s.format(s), который в определенном месте строки s вставит саму же строку s, таким образом, мы "разрываем рекурсию":

>>> s='s={};print(s)';print(s.format(s))
s=s={};print(s);print(s)


Отлично! Но тут два недостатка: во-первых, не забыть добавить s.format(s) в саму строку s:

>>> s='s={};print(s.format(s))';print(s.format(s))
s=s={};print(s.format(s));print(s.format(s))


Во-вторых, нужно вернуть на место кавычки. Не зря я недавно рассказывал о флагах преобразования строк. Используем флаг {!r} в формате, чтобы вывести repr(s), который для строк содержит одинарные кавычки:

>>> s='s={!r};print(s.format(s))';print(s.format(s))
s='s={!r};print(s.format(s))';print(s.format(s))


Ура! Квайн готов и работает!

Вы можете сделать квайн короче, используя другой стиль форматирования строк через процент: {!r} заменяется на %r, s.format(s) на s%s, плюс экранируется процент внутри самой строки s%%s (%% понимается как сам знак процента, а не как место для подстановки):

>>> s='s=%r;print(s%%s)';print(s%s)
s='s=%r;print(s%%s)';print(s%s)
источник
2019 August 14
PyWay – гуру Python 🐉
💻 bin, oct, hex

Вспомним основы информатики и поговорим о системах исчисления. В жизни мы привыкли к десячичной системе (base-10 или decimal), железо компьютеров оперирует в системе двоичной (base-2 или binary) – нулями и единицами. Часто приходится иметь дело с системой шестнадцатиричной (base-16 или hexadecimal), она позволяет записывать данные в 8 раз короче, чем двоичная. Реже встречается система восьмеричная (base-8 или octal). Система исчисления – это всего лишь средство представления числа, т.е. то, как мы его в строчку запишем или считаем, само число остается самим собой, независимо от системы.

Чтобы получить целое число из строки, записанной в какой-то системе исчисления, используем функцию int (второй параметр – база системы):

>>> int('42')  # по умолчанию 10
42
>>> int('42', 10)  # тоже самое
42
>>> int('101010', 2)
42
>>> int('BEEF', 16)
48879
>>> int('7654', 8)
4012


Наоборот сделать из числа строку в какой-то системе – встроенные функции bin, oct и hex:

>>> bin(42)
'0b101010'
>>> hex(48879)
'0xbeef'
>>> oct(4012)
'0o7654'


В строке появились префиксы 0b, 0x и 0o. Как и в Си, в Python можно использовать эти префиксы для записи чисел в коде помимо обычного десятичного варианта, если это требуется:

>>> 0b11011, 0xDEAD, 0o777
(27, 57005, 511)


Однако, часто приходится иметь дело с чистыми HEX-данными без префиксов. Многие (да и я) делали вот так:

>>> hex(12345)[2:]
'3039'


Т.е. просто отрезали первые два символа. Это не очень интуитивно, да и может привести к неправильному поведению, если передать отрицательное число:

>>> hex(-12345)
'-0x3039'
>>> hex(-12345)[2:]
'x3039'


К счастью, есть встроенная функция format(value, format_spec) (не путать с str.format), которая форматирует значение, согласно спецификатору форматирования:

>>> format(3039, 'x'), format(3039, 'X'), format(3039, '#x')
('bdf', 'BDF', '0xbdf')
>>> format(120, 'b')
'1111000'
>>> format(79, 'o')
'117'

x - 16-ричное без префикса, маленькие буквы,
X - 16-ричное без префикса, заглавные буквы,
#x - 16-ричное с префиксом, маленьки буквы,
b - двоичное число без префикса и т.д.

format(value, format_spec) эквивалентная вызову type(value).__format__(value, format_spec).
источник
2019 August 22
PyWay – гуру Python 🐉
​​Размер окна терминала

Для оформления информации в терминале часто нужно знать размеры окна терминала (количество колонок и строк). Во встроенном модуле shutil можно найти функцию get_terminal_size, которая возвращает именованный кортеж:

>>> shutil.get_terminal_size()
os.terminal_size(columns=208, lines=25)

Или

>>> cols, lines = shutil.get_terminal_size()
>>> cols, lines
(208, 25)

Или

>>> tsz = shutil.get_terminal_size()
>>> tsz.columns, tsz.lines
(208, 25)

Например, сделаем разделитель с заголовком, как на фото.

1. Будем форматировать по центру значение в строку с заданной длинной, а пустые места заполнить каким-то символом. Для этого нужен особый формат:

>>> '{:^10}'.format('love')
'   love   '
>>> '{:-^10}'.format('life')
'---life---'

Знак после двоеточия – заполнитель (если его нет, то пробел); а число после крышечки – желаемая ширина строки. Крышечка указывает, что форматирование будет по центру.

2. Так как число неизвестно заранее, то его тоже надо вставить с помощью format, предварительно экранировав фигурные скобки (двойная фигурная скобка в формате воспринимается как соотвествующий символ, а не как место для подстановки):

>>> '{{:-^{}}}'.format(10)
'{:-^10}'
>>> '{{:-^{}}}'.format(10).format('love')
'---love---'
>>> '{{:-^{}}}'.format(shutil.get_terminal_size().columns).format('love')
'---------------------------love----------------------------'

3. Текст, что по центру сделаем заглавным, а также каждый символ отделим пробелами, чтобы заголовок казался заметнее:

>>> ' '.join('love'.upper())
'L O V E'
>>> ' ' + ' '.join('love'.upper()) + ' '
' L O V E '

4. Соеденим все вместе в однострочник, добавив print к итоговой строке:

def sep(s):
   print('{{:-^{}}}'.format(shutil.get_terminal_size().columns).format(' ' + ' '.join(str(s).upper()) + ' '))
источник
PyWay – гуру Python 🐉
Хочу уточнить, что shutil.get_terminal_size() не всегда способна определить размер терминала. Например, когда собственно и нет никакого окна терминала, а лишь есть поток вывода как при выводе в файл или в канал. У потока вывода нет таких характеристик как размер окна. При выполнении функции в среде PyCharm функция вернет размер по умолчанию (80 на 25), и разделитель будет не на всю ширину области вывода, если она шире 80 символов.
источник
2019 August 23
PyWay – гуру Python 🐉
🏓 Понг

В продолжение последней темы написал сегодня с утра игру "Понг" для терминала. Обошелся только встроенными модулями. Для графики и ввода использовал модуль curses (обертка над ncurses). Исходный код доступен здесь. Благодаря современным чудо-технологиям в игру можно поиграть прямо в браузере, хоть она и работает не очень стабильно (зависит от вашего интернет соединения). Управление: W - вверх, S - вниз (только английская раскладка).
источник
PyWay – гуру Python 🐉
Как вам такие посты? Стоит ли иногда выкладывать мои игры и демки, написанные на Python? А у меня много идей.
источник
PyWay – гуру Python 🐉
источник
2019 August 29
PyWay – гуру Python 🐉
​​Юнит-тесты #1

Программисты так или иначе тестируют свои программы. В простых случаях можно запустить программу несколько раз и проверить результаты. А если вы внесли изменение? Нужно проделать эту рутинную работу еще раз и не ошибиться самому. В сложных программах это просто нереально. Естественно, этот процесс автоматизируется. Сложная программа состоит из отдельных классов, функций и модулей, каждый из которых отвечает за свой ограниченный круг функциональности. Поэтому разумно написать несколько небольших программок, которые будут подавать на вход разнообразные типичные комбинации данных и сравнивать с ожидаемым результатом. Это и будут юнит-тесты. После изменения кода запуск юнит-тестов покажет, не сломалось ли поведение программы.

В Python поставляется модуль unittest, который облегчает написание тестов:
▸ Обнаружение и автоматическое исполнение тестов
▸ Настройка теста и его завершение
▸ Группирование тестов
▸ Статистика тестирования

Чтобы создать тестовый случай, нужно создать класс, отнаследованный от unittest.TestCase. А внутри этого класса можно добавить несколько методов, начинающихся со слова test. Каждый из этих методов должен тестировать какой-то из аспектов кода. Для примера мы тестируем свойства строк Python: сложение строк (testsum) и преобразование к нижнему регистру (testlower):

import unittest

class StringTestCase(unittest.TestCase):
   def test_sum(self):
       self.assertEqual("" + "", "")
       self.assertEqual("foo" + "bar", "foobar")

   def test_lower(self):
       self.assertEqual("FOO".lower(), "foo")
       self.assertTrue("foo".islower())
       self.assertFalse("Bar".islower())


Самые распространенные проверки:

self.assertEqual – непосредственно проверяет, чтобы первый аргумент равнялся второму. Если это будет не так, то тест будет провален, и появится сообщение о том, что и где пошло не так.
self.assertTrue – ожидает, что аргумент будет эквивалентен правде (True), а self.assertFalse – проверяет на ложь (False).

Запуск делается либо непосредственно из самой программы:

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

А
можно из консоли:

python -m unittest my_test.py

Модуль unittest сам найдет все тестовые случаи и выполнит в них все тестовые функции.

В будущих постах расскажу более углубленно о возможностях unittest.
источник