Если ты хочешь, чтобы контекстный менеджер при входе или выходе из контекста приостанавливал выполнение корутины, следует использовать асинхронные контекстные менеджеры. Вместо вызова
Асинхронные контекстные менеджеры нужно использовать с синтаксисом
В этом примере класс
👉@BookPython
m.__enter__()
и m.__exit__()
Python в этом случае выполняет await m.__aenter__()
и await m.__aexit__()
соответственно.Асинхронные контекстные менеджеры нужно использовать с синтаксисом
async with
:
import asyncio
class Slow:
def __init__(self, delay):
self._delay = delay
async def __aenter__(self):
await asyncio.sleep(self._delay / 2)
async def __aexit__(self, *exception):
await asyncio.sleep(self._delay / 2)
async def main():
async with Slow(1):
print('slow')
loop = asyncio.get_event_loop()
loop.run_until_complete(main())
В этом примере класс
Slow
симулирует задержку при входе и выходе из контекста.👉@BookPython
👍3❤1
Начиная с Python 3.7, модуль
Для более старых версий Python можно использовать
👉@BookPython
contextlib
предоставляет декоратор asynccontextmanager
, который позволяет определять асинхронные контекстные менеджеры точно так же, как contextmanager
:
import asyncio
from contextlib import asynccontextmanager
@asynccontextmanager
async def slow(delay):
half = delay / 2
await asyncio.sleep(half)
yield
await asyncio.sleep(half)
async def main():
async with slow(1):
print('slow')
loop = asyncio.get_event_loop()
loop.run_until_complete(main())
Для более старых версий Python можно использовать
@asyncio_extras.async_contextmanager
.👉@BookPython
👍4
В Python нет оператора
Суть в том, что в Python есть унарный плюс, и выражение
Здесь
👉@BookPython
++
, вместо него используется x += 1
. Однако синтаксис ++x
всё ещё допустим (в отличие от x++
, который вызывает ошибку).Суть в том, что в Python есть унарный плюс, и выражение
++x
на самом деле интерпретируется как x.__pos__().__pos__()
. Этим можно злоупотребить и заставить ++
работать как инкремент (хотя так делать не рекомендуется):
class Number:
def __init__(self, value):
self._value = value
def __pos__(self):
return self._Incrementer(self)
def inc(self):
self._value += 1
def __str__(self):
return str(self._value)
class _Incrementer:
def __init__(self, number):
self._number = number
def __pos__(self):
self._number.inc()
x = Number(4)
print(x) # 4
++x
print(x) # 5
Здесь
++x
вызывает дважды __pos__()
: сначала на x
, затем на возвращённом объекте _Incrementer
, в котором второй __pos__()
вызывает inc()
, увеличивая значение.👉@BookPython
👍3🌚2❤1
Иногда в тестах нужно сравнивать сложные структуры, игнорируя некоторые значения. Обычно это делается путем сравнения отдельных значений внутри структуры:
Однако можно создать специальное значение, которое будет считаться равным любому другому значению:
Это легко реализовать, определив метод
👉@BookPython
>>> d = dict(a=1, b=2, c=3)
>>> assert d['a'] == 1
>>> assert d['c'] == 3
Однако можно создать специальное значение, которое будет считаться равным любому другому значению:
>>> assert d == dict(a=1, b=ANY, c=3)
Это легко реализовать, определив метод
__eq__
:
>>> class AnyClass:
... def __eq__(self, another):
... return True
...
>>> ANY = AnyClass()
👉@BookPython
👍5🥱2❤🔥1❤1👎1
Если вы хотите итерироваться одновременно по нескольким итерируемым объектам, функция
Вывод:
Обратите внимание, что
👉@BookPython
zip
может быть хорошим выбором. Она возвращает генератор, который выдаёт кортежи, содержащие по одному элементу из каждого исходного итерируемого объекта:
In : eng = ['one', 'two', 'three']
In : ger = ['eins', 'zwei', 'drei']
In : for e, g in zip(eng, ger):
...: print('{e} = {g}'.format(e=e, g=g))
...:
Вывод:
one = eins
two = zwei
three = drei
Обратите внимание, что
zip
принимает итерируемые объекты как отдельные аргументы, а не в виде списка аргументов. Чтобы «развернуть» значения (unzip), можно использовать оператор *
:
In : list(zip(*zip(eng, ger)))
Out: [('one', 'two', 'three'), ('eins', 'zwei', 'drei')]
👉@BookPython
👍4
💡10 функций, для продвинутых Python-разработчиков
1. Разворачиваем вложенных списков любой глубины
2. Декоратор для мемоизации результатов функции
3. Разбиение списка на куски длины n
4. Уникализация последовательности с сохранением порядка
5. Глубокий доступ к вложенным ключам словаря
6. Преобразование Python-объекта в читаемый JSON
7. Чтение последних n строк файла (аналог tail)
8. Выполнение shell-команды и возврат вывода
9. Быстрое объединение путей
10. Группировка списка словарей по значению ключа
👉@BookPython
1. Разворачиваем вложенных списков любой глубины
flatten = lambda lst: [x for sub in lst for x in (flatten(sub) if isinstance(sub, list) else [sub])]
2. Декоратор для мемоизации результатов функции
memoize = lambda f: (lambda *args, _cache={}, **kwargs: _cache.setdefault((args, tuple(kwargs.items())), f(*args, **kwargs)))
3. Разбиение списка на куски длины n
chunked = lambda lst, n: [lst[i:i+n] for i in range(0, len(lst), n)]
4. Уникализация последовательности с сохранением порядка
uniq = lambda seq: list(dict.fromkeys(seq))
5. Глубокий доступ к вложенным ключам словаря
deep_get = lambda d, *keys: __import__('functools').reduce(lambda a, k: a.get(k) if isinstance(a, dict) else None, keys, d)
6. Преобразование Python-объекта в читаемый JSON
pretty_json = lambda obj: __import__('json').dumps(obj, ensure_ascii=False, indent=2)
7. Чтение последних n строк файла (аналог tail)
tail = lambda f, n=10: list(__import__('collections').deque(open(f), maxlen=n))
8. Выполнение shell-команды и возврат вывода
sh = lambda cmd: __import__('subprocess').run(cmd, shell=True, check=True, capture_output=True).stdout.decode().strip()
9. Быстрое объединение путей
path_join = lambda *p: __import__('os').path.join(*p)
10. Группировка списка словарей по значению ключа
group_by = lambda seq, key: {k: [d for d in seq if d.get(key) == k] for k in set(d.get(key) for d in seq)}
👉@BookPython
👍5🤔4
У Python очень короткий список встроенных констант. Одна из них —
В библиотеке NumPy
PEP 484 придаёт
Наконец,
👉@BookPython
Ellipsis
, которую также можно записать как ...
. Эта константа не имеет особого значения для интерпретатора, но используется в ситуациях, где такой синтаксис уместен.В библиотеке NumPy
Ellipsis
поддерживается в качестве аргумента для __getitem__
, например, x[...]
возвращает все элементы массива x
.PEP 484 придаёт
Ellipsis
дополнительный смысл: Callable[..., type]
используется для обозначения типа вызываемых объектов без указания типов аргументов.Наконец,
...
можно использовать, чтобы показать, что функция ещё не реализована. Это полностью допустимый Python-код:
def x():
...
👉@BookPython
👍5❤2👎1