Size: a a a

PyWay – гуру Python 🐉

2019 October 23
PyWay – гуру Python 🐉
Генераторные выражения лучше

Мы говорили про map и itertools.starmap, но я тут подумал... Зачем они, если есть замечательные генераторные выражения:

➣ Они умеют делать: генераторы, списки list, словари dict и множества set.
➣ Поддерживают вложенные циклы для обработки многомерных данных
➣ Умеют фильтровать данные, как filter
➣ Обладают лаконичным и понятным синтаксисом

По-английски они называются в зависимости от типа данных на выходе: generator expressions и list/dictionary/set comprehensions.

Если нам нужен генератор, то ставим круглые скобки. Если нужен сразу список – квадратные. Если нужен словарь или множество – фигурные. А внутри цикл for/in. Наш прибавлятор единицы стал короче и без лямбд:

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

>>> [x + 1 for x in [1, 2, 3, 4]]
[2, 3, 4, 5]


Пример на замену starmap не то чтобы сильно короче, но значительно понятнее, потому что виден фактический вызов pow и разумные имена переменных:

>>> from itertools import starmap
>>> list(starmap(pow, [(2, 4), (3, 2), (5, 2)]))
[16, 9, 25]

>>> [pow(base, exp) for base, exp in [(2, 4), (3, 2), (5, 2)]]
[16, 9, 25]


Если нужно множество (коллекция без повторов), то все то же самое, но скобки фигурные. Пример: все уникальные буквы слова:

>>> {r for r in 'BANANA'}
{'N', 'B', 'A'}


Если нужен словарь, то скобки также фигурные, но генерируем парами "ключ: значение". Пример: ключ – строка, значение – строка задом наперед:

>>> {key: key[::-1] for key in ["Mama", "Papa"]}
{'Mama': 'amaM', 'Papa': 'apaP'}


Наконец, если нужен генератор, то скобки круглые. Генератор вычисляет и выдает значения лениво (по одному, когда они требуются):

>>> g = (x ** 2 for x in [1, 2, 3, 4])
>>> next(g)
1
>>> print(*g)
4 9 16


Если функция принимает ровно 1 аргумент, то передавая в нее генератор можно опустить лишние круглые скобки:

>>> sum(x ** 2 for x in [1, 2, 3, 4])
30


Вот так проявляется красота и лаконичность языка Python.
источник
PyWay – гуру Python 🐉
Что лучше по вашему мнению?
Анонимный опрос
2%
map/starmap
39%
генераторные выражения
24%
я все циклами делаю
35%
по ситуации
Проголосовало: 84
источник
2019 October 25
PyWay – гуру Python 🐉
🦆 type() и isinstance()

В Python у нас утиная динамическая типизация, поэтому бывает что нужно узнать тип переменной. Функция type() возвращает тип аргумента или, учитывая, что в Python – все класс, то класс аргумента. Результат можно сравнить с известным типом:

>>> i, s = 10, "hello"
>>> type(i)
<class 'int'>
>>> type(i) is int
True
>>> type(s) is str
True
>>> class A: pass
...
>>> a = A()
>>> type(a) is A
True

Можно создать экземпляр объекта того же класса, что и переменная:

>>> new_a = type(a)()
>>> type(new_a) == type(a)
True


⚠️ Нужно знать! type() не принимает во внимание наследование. Тип наследника отличается от типа родителя:

>>> class B(A): pass
...
>>> type(A()) is type(B())
False


Лучший способ проверить типы – функция isinstance (instance англ. – экземпляр). Она возвращает True, если первый аргумент является экземпляром класса во втором аргументе:

>>> isinstance(i, int)
True
>>> isinstance(s, str)
True
>>> isinstance(a, A)
True


Функция поддерживает наследование:

class A: pass
class B(A): pass
b = B()
>>> isinstance(b, A)
True


И самое крутое: вторым аргументом допускается передать кортеж из типов, и isinstance вернет True, если хоть один из типов в кортеже подходит:

>>> isinstance(i, (int, float))
True
>>> isinstance(a, (A, B))
True


Загадка:

class A: ...
a = A()
class A: ...
print(isinstance(a, A))


Что будет True или False?
источник
2019 October 26
PyWay – гуру Python 🐉
Правильный ответ был False!

Объяснение. Динамическая натура Python позволяет переопределить класс во время интерпретации. Помните, как недавно я рассказывал про декораторы класса? Там мы подменяли один класс другим. Вот это из той же оперы. Тут мы подменили один класс, другим классом, отвязав имя А от старого класса и привязав его к новому. Старый класс А остался жив только как класс объекта в переменной a. Старого и нового классов разные адреса (id):

class A: ...
print(id(A))  # 140479777819720

a = A()

class A: ...
print(id(A))  # 140479777809672

isinstance(a, A)  # False


Другой вопрос. Пусть в файле my_module.py написано определение класса:

class A: ...

Пишем такой код в другом файле:

from my_module import A
a = A()
from my_module import A

print(isinstance(a, A))

Какой будет ответ в этом случае?
источник
PyWay – гуру Python 🐉
На этот раз большинство оказалось право! Ответ – True.

Система модулей Python только единожды будет запускать каждый импорируемый файл. Второй import не возымеет действия, и класс А будет тем же, что и был раньше.

Бонус: если вам нужно принудительно перезагрузить модуль – воспользуйтесь функцией reload из importlib. Попробуем. В файл mymodule.py напишем:

class A:
   # будем видеть, когда класс загружен
   print('loaded class A')


В другой файле:

from importlib import reload

import mymodule
a = my_module.A()
mymodule = reload(mymodule)

print(isinstance(a, mymodule.A))


Вывод программы:

loaded class A
loaded class A
False
источник
2019 October 28
PyWay – гуру Python 🐉
Идеально в холодный осенний день.
источник
2019 October 29
PyWay – гуру Python 🐉
Подборка декораторов

В завершение темы декораторов, как и обещал, публикую подборку полезных декораторов. Среди них как встроенные декораторы, так и самописные. Список:

➢ Свойства: @property
@staticmethod, @classmethod
➢ Конекстый менеджер
➢ Кэширование @lru_cache
➢ Свойство с кэшем @cached_property
➢ Измеритель времени @timeit
➢ Регистрация функции завершения
➢ Пометка старого с @deprecated
➢ Повторитель
➢ Замедлитель
➢ Помощник для отладки

Материал большой, много кода, в телеграм не влезет, поэтому читайте через INSTANT VIEW:
👇 👇 👇
https://t.me/iv?url=https://tirinox.ru/useful-decorators/&rhash=56b30beec7290d
источник
2019 October 31
PyWay – гуру Python 🐉
​​collections.deque

deque – коллекция двухсторонней очереди, которая похожа на список, за исключением того, что добавлять и удалять элементы можно либо в начало (слева), либо в конец (справа). Реализована обычно через двусвязный список. Благодаря этому операции добавления или удаления элемента с любого конца deque имеют сложность O(1). Доступ к произольному элементу – O(n).

Создание:

from collections import deque

d1 = deque() # пустая
d2 = deque([1, 2, 3])  # из любого iterable
d3 = deque(maxlen=5)  # максимальная длина


Методы:

append(x) – добавить элемент в конец.
appendleft(x) – добавить элемент в начало.
pop(x) – удалить и вернуть элемент с конца.
popleft(x) – удалить и вернуть элемент с начала.
clear() – очистить очередь.
reverse() – развернуть очередь наоборот:

d = deque([1, 2, 3])
d.reverse()
print(d)  # deque([3, 2, 1])

rotate(n) – последовательно переносит n элементов с конца в начало (если n отрицательно, то из начала в конец):

d = deque(range(8))
# deque([0, 1, 2, 3, 4, 5, 6, 7])
d.rotate(2)
# deque([6, 7, 0, 1, 2, 3, 4, 5])
d.rotate(-1)
# deque([7, 0, 1, 2, 3, 4, 5, 6])


extend(iterable) – добавляет в конец все элементы iterable.
extendleft(iterable) – добавляет в начало все элементы iterable (начиная с последнего элемента iterable):

d = deque()
d.extend([1, 2, 3])
d.extendleft([10, 20, 30])
print(d)  # deque([30, 20, 10, 1, 2, 3])


insert(index, x) – вставить элемент x в индекс i (медленно).
d[index] – доступ к элементу по индексу (медленно).
len(d) – число элементов в очереди (тоже работает медленно).
remove(value) – удаляет первое вхождение значения value (слева направо). Остальные элементы не трогаются.
count(value) – количество элементов со значением value в очереди.

Максимальная длина.

Если при создание deque задано maxlen, то длина очереди будет ограничена. Это значит, что если мы попытаемся вставить элемент в очередь длины (maxlen), то элемент с противоположного конца будет вытеснен, и длина очереди не изменится:

# максимальная длина 5
d = deque(maxlen=5)

d.extend(range(10))
# deque([5, 6, 7, 8, 9], maxlen=5)
# затерлись первые 5 элементов

d.appendleft(100)
# слева вставим 100, девятка вылетит справа
# deque([100, 5, 6, 7, 8], maxlen=5)

d.append(200)
# справа вставим 200, сотня вылетит слева
# deque([5, 6, 7, 8, 200], maxlen=5)


Резюме: deque хороша, когда частый доступ осуществляется только к концам коллекции. Подходит для очередей (например, задач), стэка, алгоритма round-robin, подсчета бегущих средних и прочего. А если нужен доступ к элементам по индексу – лучше брать list.
источник
2019 November 02
PyWay – гуру Python 🐉
​​Unicode по имени

Вы знали, что в строке Python 3 можно вставлять символы по их названию в юникод-таблице?

Допустим нужны вам стрелки:

>>> "\N{LEFTWARDS ARROW} EXIT \N{RIGHTWARDS ARROW}"
'← EXIT →'

Найти юникод символы и их имена удобно с помощью онлайн-сервисов.

Да, конечно, в Python 3 вы можете прямо в код вставлять любые юникод символы без их кодов и имен. Но профессиональнее – вставлять символы по именам, потому что читатель вашего кода может видеть его другим шрифтом, где начертание символов отличается от вашего, или вообще эти символы не отображаются. Ерунда? А вот вам пример:

>>> "Hello" == "Hello"
False

Строки выглядят одинаково, но я спрятал в одной из них символ "\N{ZERO WIDTH JOINER}", поэтому они неравны:

>>> len("Hello")
6
>>> len("Hello")
5
>>> "Hel
lo".encode('utf-8')
b'Hel\xe2\x80\x8dlo'
>>> "Hello".encode('utf-8')
b'Hello'

А вы знали про "\N{...}"?
источник
2019 November 04
PyWay – гуру Python 🐉
​​Переопределение свойств

Допустим есть класс со свойством:

class Base:
   def __init__(self):
       self._x = 100

   @property
   def x(self):
       print('Base get x')
       return self._x

   @x.setter
   def x(self, value):
       print('Base set x')
       self._x = value


Ситуация А. В классе-наследнике мы хотим переопределить ТОЛЬКО сеттер, чтобы он делал что-то еще помимо того, что умеет в базовом классе. Это не так и тривиально:

class Derived1(Base):
   @Base.x.setter
   def x(self, value):
       print('Derived1 set x')
       Base.x.fset(self, value)


Нужно найти декоратор сеттера свойства в базовом классе (по имени – @Base.x.setter). А старый сеттер хранится в Base.x.fset, которому нужно явно передать себя (self). Что забавно, PyCharm 2019.2 ругается на такой вызов, но не предлагает вариантов, как сделать лучше.

Ситуация B. Хотим переопределить ТОЛЬКО геттер:

class Derived3(Base):
   @Base.x.getter
   def x(self):
       print('Derived3 get x')
       return super().x


Ситуация C. Хотим переопределить и геттер, и сеттер.  

class Derived2(Base):
   @property
   def x(self):
       print('Derived2 get x')
       return super().x
   @x.setter
   def x(self, value):
       print('Derived2 set x')
       Base.x.fset(self, value)


В этом случае мы определяем свойство как бы с нуля. Старый геттер вызывается при доступе к super().x, а старый сеттер вызываем аналогично с ситуацией A. Разница только в @x.setter вместо @Base.x.setter (по-старому не работает, проверял).

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

Как сделать проще? Писать все геттеры и сеттеры явно, а потом делать из низ property; или вообще отказаться от property.

class Base:
   def __init__(self):
       self._x = 0
   def set_x(self, v):
       print('Base set x')
       self._x = v
   def get_x(self):
       print('Base get x')
       return self._x
   x = property(get_x, set_x)

class Derived(Base):
   def set_x(self, v):
       print('Derived set x')
       super().set_x(v)
   def get_x(self):
       print('Derived get x')
       return super().get_x()
   x = property(get_x, set_x)
источник
2019 November 08
PyWay – гуру Python 🐉
📕 Fake User Agent

Одна из примитивных защит сайтов от парсинга – проверка HTTP заголовка User-Agent, который содержит наименование веб-браузера или клиента, делающего запрос. Если этого заголовка нет, то сервер может не выполнить запрос, раскусив, что его делает робот, а не человек. Обход защиты – имитация реального User-Agent браузера библиотекой fake_useragent. Установка:

pip install fake_useragent

Использование:

from fake_useragent import UserAgent
ua = UserAgent()
print(ua.random)
# Mozilla/5.0 (Windows NT 6.1) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/27.0.1453.90 Safari/537.36


ua.random – агент случайного браузера (с учетом статистики распространенности браузеров по миру). Также досутпны агенты для конкретных браузеров: ua.ie, ua.msie, ua.opera, ua.chrome, ua.google, ua.firefox, ua.ff, ua.safari.

Пример отправки запроса через request:

from fake_useragent import UserAgent
import requests
ua = UserAgent()

# куда шлем (этот URL как раз ответит нам наш UA для проверки)
url = 'https://httpbin.org/user-agent'

# создаем заголовок
headers = {'User-Agent': ua.chrome}

# делаем запрос, передав заголовок
result = requests.get(url, headers=headers)
print(result.content)


Еще в классе есть метод ua.update(), что обновляет базу данных браузеров, если она устарела. Это медленный метод, он делает запрос на сервера. Его не нужно вызывать каждый раз.
источник
2019 November 11
PyWay – гуру Python 🐉
Абстрактный класс ABC

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

В Python нет синтаксической поддержки абстрактных классов, но есть встроенный модуль abc (расшифровка – abstract base classes), который помогает проектировать абстрактные сущности.

Абстрактный класс наследуют от ABC (Python 3.4+) или указывают метакласс ABCMeta (для Python 3.0+):

from abc import ABC, ABCMeta
class Hero(ABC):
...

# или:
class Hero(metaclass=ABCMeta):
...


Любой из вариантов работает, первый современнее и короче. На данном этапе мы можем создавать объекты этих классов, потому что в них пока не абстрактных методов. Добавим:

from abc import ABC, abstractmethod
class Hero(ABC):
@abstractmethod
def attack(self):
 pass


Hero() – выдаст ошибку "TypeError: Can't instantiate abstract class Hero with abstract methods attack", которая говорит, что в классе Hero есть абстрактный метод attack. Мы вставили в него заглушку pass, но вообще там может быть какая-то реализация. Отнаследуем от героя Hero – конкретный подкласс лучника Archer:

class Archer(Hero):
def attack(self):
 print('выстрел из лука')
Archer().attack()


Вот объект Archer мы можем уже создать и использовать реализацию метода attack.

Кроме обычных методов, абстрактными можно обозначить и статические, классовые методы, а также свойства:

class C(ABC):
  @classmethod
  @abstractmethod
  def my_abstract_classmethod(cls):
      ...

  @staticmethod
  @abstractmethod
  def my_abstract_staticmethod():
      ...

  @property
  @abstractmethod
  def my_abstract_property(self):
      ...

  @my_abstract_property.setter
  @abstractmethod
  def my_abstract_property(self, val):
      ...


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

Формально говоря, абстрактные классы для Python не являются чем-то необходимым в силу динамичности языка. Если мы выкинем все упоминания абстрактности классов и методов из рабочего кода, он продолжит работать, как и ранее. Абстрактные классы нужны на этапе проектирования или расширения кода, чтобы обеспечивать "правильные" взаимодействия новых классов, защищая от создания экземпляров абстрактных классов. Важно помнить, что эта защита срабатывает на этапе выполнения программы, а не компиляции, как в языках Java, C++ или C#!
источник
2019 November 13
PyWay – гуру Python 🐉
Временные файлы и директории нужны, если промежуточные данные слишком велики, чтобы держать их в оперативной памяти. Или бывают случаи, что программа или компонент обрабатывает только файлы и не может принимать данные по другим каналам.

Для создания временных файлов и директорий есть модуль tempfile. Удобно, что временные файлы создаются в специальном месте ФС и удаляются автоматически после закрытия. Нам можно не думать, куда положить временный файл, как его назвать и как почистить мусор после выполнения программы.

import tempfile

with tempfile.NamedTemporaryFile() as fp:
print(fp.name)  # путь к файлу
   fp.write(b'Hello world!')
   fp.seek(0)
   print(fp.read())


fp – файло-подобный объект, вроде того, что идет из open. Файл будет удален в момент закрытия.
Есть еще TemporaryFile. Отличие NamedTemporaryFile от TemporaryFile в том, что первый будет гарантированно виден в файловой системе и иметь атрибут name, тогда как второй может быть и не виден в ФС. NamedTemporaryFile можно создать с ключем delete=False, чтобы он не был удален. А TemporaryFile всегда будет удален при закрытии.

Режим открытия временного файла по умолчанию "w+b", т.е. можно писать и читать бинарный данные. Можно изменить передав аргумент mode:

tempfile.NamedTemporaryFile(mode='w')

TemporaryDirectory – создает временную директорию и возвращает строку – путь к ней. Мы можем создавать в директории любые файлы в любом количестве. После закрытия контекстного менеджера директория и все файлы в ней будут автоматически удалены.

with tempfile.TemporaryDirectory() as temp:
   with open(os.path.join(temp, '1.txt'), 'w') as f:
       f.write('hello')


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

tmp = tempfile.TemporaryDirectory()
with open(os.path.join(tmp.name, '1.txt'), 'w') as f:
   f.write('hello')
tmp.cleanup()  # очистка


Узнать где хранятся временные файлы:

>>> tempfile.gettempdir()
'/var/folders/m8/1_wxy73215q9n2vrjetnw0xjc0000gn/T'


Эта директория берется из переменных окружения TMPDIR, TEMP, TEMP или это директория C:\TEMP, C:\TMP, \TEMP и \TMP (для Windows) или /tmp, /var/tmp и /usr/tmp для остальных систем.

Как поменять место хранения временных данных процесса?
1. Изменить переменную окружения: TMPDIR="/home/me/temp" python my_program.my
2. Передать в функции создания временных файлов аргумент dir с нужным путем: tempfile.NamedTemporaryFile(dir='/home/me')
источник
2019 November 18
PyWay – гуру Python 🐉
А вы знали, что знак подчеркивания в интерпретаторе Python хранит последние вычисленное значение:

>>> 10
10
>>> _ + 20
30
>>> _ * 100
3000


Удобно, если используете интерпретатор в качестве калькулятора.

Или, что в коде Python 3.6+ вы можете отделить подчеркиванием разряды чисел для лучшей читаемости:

>>> 10_300_450
10300450
>>> 0xFF_10_EE_AD_88
1095500737928

Читайте о различных применениях подчеркивания в новой статье. 👇
источник
2019 November 21
PyWay – гуру Python 🐉
Визуализация данных — это большая часть работы специалистов в области data science. На ранних стадиях развития проекта часто необходимо выполнять разведочный анализ данных (РАД, Exploratory data analysis (EDA)), чтобы выявить закономерности, которые обнаруживают данные. Визуализация данных помогает представить большие и сложные наборы данных в простом и наглядном виде.

Об абсолютно качественно новом уровне визуализации читайте на канале Python Master

Качественно новый уровень визуализации данных
источник
2019 November 22
PyWay – гуру Python 🐉
​​Анимация Jupyter Notebook

Сегодня мы будем анимировать график прямо внутри Jupyter Notebook. Сперва сделаем плавную отрисовку графика. Переключим режим отображения графиков в notebook:

%matplotlib notebook

Импортируем все, что нужно:

import matplotlib.pyplot as plt
from matplotlib import animation
import numpy as np

Сгенерируем наши данные:

# время (200 точек)
t = np.linspace(0, 2 * np.pi, 200)
x = np.sin(t)  # синусоида

Создадим пустой график:

fig, ax = plt.subplots()
# пределы отображения
ax.axis([0, 2 * np.pi, -2, 2])
l, = ax.plot([], [])

Функция animate будет вызываться при отрисовка каждого кадра, аргумент i – номер кадра:

def animate(i):
# рисуем данные только от 0 до i
# на первом кадре будет 0 точек,
# а на последнем - все
   l.set_data(t[:i], x[:i])

Запускаем анимацию:

fps = 30  # карды в сек
# frames - число кадров анимации
ani = animation.FuncAnimation(fig, animate, frames=len(t), interval=1000.0 / fps)

Если мы хотим анимировать сами данные, например, заставить синусоиду "плясать", то на каждом шаге перегенерируем данные заново, используя переменную i:

def animate(i):
   x = np.sin(t - i / len(t) * np.pi * 2) * np.sin(t * 15)
   l.set_data(t, x)

Можно сохранить в GIF:

ani.save('myAnimation.gif', writer='imagemagick', fps=30)

Сам ноутбук я загрузил на GitHub, но поиграться онлайн с ним не получится, надо скачать себе и запустить локально. Анимированные графики отрисовываются в реальном времени, поэтому требуют достаточно много ресурсов. Пример 3D анимации:
источник
2019 November 28
PyWay – гуру Python 🐉
Короткое замыкание

Поговорим о логических операциях. Допустим у нас есть цепочка из or:

if x() or y() or z():
   print('bingo!')

Чтобы print сработал, нужно, чтобы хотя бы один из трех вызовов давал бы True (или приводился к True). Что если x() сразу вернет True? Тогда, очевидно, все выражение будет равняться True в любом случае и независимо от того, что будет в y() и z(). Если смысл их вычислять? Нет! Python и не вычисляет. Тем самым достигается некоторая оптимизация, которая называется short circuiting (или короткое замыкание).

Это хорошо, только, если в оставшихся логических выражениях нет побочных эффектов, потому они не будут выполнены, если вычисление логического выражение будет остановлено. Давайте проверим:

def check(b):
   print('check.')
   return b

if True or check(True):
   print('ok.')  # ok.

if False or check(True):
   print('ok.')  # check. ok.


В первом случае check не работает, потому что первый операнд True уже предопределит судьбу выражения. А во втором случае – сработает, потому первый операнд False не дает определенности и нужно вычислить check().

Аналогично все с оператором and: как только первый операнд в цепочке вернет False, выполнение прекратиться.

if True and False and check(True):
   ...  # не выполнится check

Встроенные функции all и any тоже используют короткое замыкание, то есть all перестает проверять на первом False, а any – на первом True.

all(check(i) for i in [1, 1, 0, 1, 1])  # выведет 3 check из 5
any(check(i) for i in [0, 1, 0, 0, 0])  # выведет 2 check из 5


Эту особенность стоит помнить. Лично я буквально вчера сталкивался с алгоритмом, где было что-то вроде:

while step(x, y, True) or step(x, y, False):...

По задумке оба step должны выполнятся на каждой итерации, но из-за короткого замыкания второй из них иногда не выполнялся; алгоритм работал неверно.
источник
2019 November 30
PyWay – гуру Python 🐉
На днях я рассказал про коротко-замкнутые and и or. Что если не нужно такое поведение?

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

def check(b):
   print('check.')
   return b

check(False) & check(False)  # & – битовое и
check(True) | check(False)   # | - битовое или


В этом случае оба check сработают!

❗Внимание: есть подводные камни. Этот прием работает корректно только с булевыми типами! Если мы подставим целые числа, то результат может быть не тот, что ожидается. Яркий пример – это числа 1 и 2:

>>> bool(1 and 2)
True
>>> bool(1 & 2)
False
>>> 1 & 2
0


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

while bool(step(x, y, True)) | bool(step(x, y, False)):
...

Второй подводный камень: приоритет операторов | и & гораздо выше, чем у not, and и or. Так что, если миксуем их, то всегда ставим скобки:

>>> not False or True
True
>>> not False | True
False
>>> (not False) | True
True


Не подумайте, что я призываю использовать побитовые операции вместо логических. Но в редких случаях это может быть оправдано.
источник
2019 December 06
PyWay – гуру Python 🐉
📕 #Библиотека schedule#Библиотека schedule

Вам приходилось работать с CRON? Это такой сервис в nix-системах, который позволяет регулярно в определенные моменты времени запускать скрипты или программы. Штука с долгой историей, в наследство которой достался странный синтаксиc для описания правил:

0 * * * * my_script

Что если бы мы хотели иметь свой CRON внутри программы Python, чтобы в нужные моменты времени вызывать функции? Да еще, чтобы у него был человеческий синтаксис? Такая библиотека есть и называется schedule.

pip install schedule

Рассмотрим пример:

import schedule
import time

def job():
   print("Работаю")

schedule.every(10).minutes.do(job)
schedule.every().hour.do(job)
schedule.every().day.at("10:30").do(job)
schedule.every(5).to(10).minutes.do(job)
schedule.every().monday.do(job)
schedule.every().wednesday.at("13:15").do(job)
schedule.every().minute.at(":17").do(job)

# нужно иметь свой цикл для запуска планировщика с периодом в 1 секунду:
while True:
   schedule.run_pending()
   time.sleep(1)


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

# спланируй.каждые(10).минут.сделать(работу)
schedule.every(10).minutes.do(job)

# спланируй.каждый().день.в(10:30).сделать(работу)
schedule.every().day.at("10:30").do(job)


В задания можно передавать параметры вот так:

def greet(name):
   print('Hello', name)
schedule.every(2).seconds.do(greet, name='Alice')


Если по какой-то причине нужно отменить задание, это делается так:

def job1():
   # возвращаем такой токен, и это задание снимается с выполниния в будущем
   return schedule.CancelJob
schedule.every().day.at('22:30').do(job1)

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

schedule.every().day.do(greet, 'Monica').tag('daily-tasks')
schedule.every().day.do(greet, 'Derek').tag('daily-tasks')

schedule.clear('daily-tasks')  # массовая отмена по тэгу

Метод to позволяет задать случайный интервал для выполнения задания, например от 5 до 10 секунд:

schedule.every(5).to(10).seconds.do(my_job)

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

Если задания занимают продолжительное время или должны выполняться параллельно, то вам самостоятельно придется организовать их выполнение в отдельных потоках. Примеры есть в официальном FAQ.
источник
2019 December 12
PyWay – гуру Python 🐉
источник