Если ты хочешь запустить код с изменённой глобальной переменной, лучше использовать контекстный менеджер, а не менять её напрямую:
👉@BookPython
from contextlib import contextmanager
QUIT_MESSAGE = 'Bye'
def print_quit_mesage():
global QUIT_MESSAGE
print(QUIT_MESSAGE)
@contextmanager
def global_variable_changed(name, value):
orig_value = globals()[name]
globals()[name] = value
yield
globals()[name] = orig_value
with global_variable_changed(
'QUIT_MESSAGE',
'Tschüss'
):
print_quit_mesage()
👉@BookPython
👍6
Ты можешь создавать словари двумя способами: с помощью литералов или функции
Литералы работают быстрее, чем
Во-первых, не нужно ставить дополнительные кавычки. Однако это работает только в том случае, если все ключи — допустимые идентификаторы Python:
Во-вторых, ты не сможешь случайно указать один и тот же ключ дважды:
В-третьих, легко создать новый словарь на основе уже существующего:
Но учти, что ключи нельзя переопределять при таком способе:
👉@BookPython
dict
:
>>> dict(a=1, b=2)
{'a': 1, 'b': 2}
>>> {'a': 1, 'b': 2}
{'a': 1, 'b': 2}
Литералы работают быстрее, чем
dict
, но у функции есть свои преимущества.Во-первых, не нужно ставить дополнительные кавычки. Однако это работает только в том случае, если все ключи — допустимые идентификаторы Python:
>>> dict(a=1)
{'a': 1}
>>> dict(1='a')
File "<stdin>", line 1
SyntaxError: keyword can't be an expression
Во-вторых, ты не сможешь случайно указать один и тот же ключ дважды:
>>> {'a': 1, 'a': 1}
{'a': 1}
>>> dict(a=1, a=1)
File "<stdin>", line 1
SyntaxError: keyword argument repeated
В-третьих, легко создать новый словарь на основе уже существующего:
>>> d = dict(b=2)
>>> dict(a=1, **d)
{'a': 1, 'b': 2}
Но учти, что ключи нельзя переопределять при таком способе:
>>> dict(b=3, **d)
Traceback (most recent call last):
File "<stdin>", line 1, in <module>
TypeError: type object got multiple values for keyword argument
👉@BookPython
👍4👏1
Начиная с Python 3.5, стало возможно использовать распаковку в литералах словарей и списков.
Пример со словарём:
Пример со списком:
Для словарей такая форма даже мощнее, чем функция
👉@BookPython
Пример со словарём:
{**{'a': 1}, 'b': 2, **{'c': 3}}
# Результат: {'a': 1, 'b': 2, 'c': 3}
Пример со списком:
[1, 2, *[3, 4]]
# Результат: [1, 2, 3, 4]
Для словарей такая форма даже мощнее, чем функция
dict
, потому что позволяет переопределять значения:
{**{'a': 1, 'b': 1}, 'a': 2, **{'b': 3}}
# Результат: {'a': 2, 'b': 3}
👉@BookPython
👍7❤1
Если ты хочешь, чтобы контекстный менеджер при входе или выходе из контекста приостанавливал выполнение корутины, следует использовать асинхронные контекстные менеджеры. Вместо вызова
Асинхронные контекстные менеджеры нужно использовать с синтаксисом
В этом примере класс
👉@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
Иногда программное обеспечение начинает вести себя странно в продакшене. Вместо того чтобы просто перезапустить его, вы, вероятно, захотите понять, что именно происходит, чтобы позже это исправить.
Очевидный способ сделать это — проанализировать, что делает программа, и попытаться угадать, какой участок кода выполняется. Безусловно, корректная система логирования облегчает эту задачу, но логи приложения могут быть недостаточно подробными — либо по замыслу, либо из-за настроек, ограничивающих уровень логирования.
В таком случае может пригодиться
Каждая строка в выводе
В следующем примере вывод приостанавливается до тех пор, пока кто-то не введёт данные в STDIN:
👉@BookPython
Очевидный способ сделать это — проанализировать, что делает программа, и попытаться угадать, какой участок кода выполняется. Безусловно, корректная система логирования облегчает эту задачу, но логи приложения могут быть недостаточно подробными — либо по замыслу, либо из-за настроек, ограничивающих уровень логирования.
В таком случае может пригодиться
strace
. Это утилита Unix, которая отслеживает системные вызовы. Вы можете запустить её заранее — strace python script.py
— но чаще бывает удобнее подключиться к уже работающему процессу: strace -p PID
.
$ cat test.py
with open('/tmp/test', 'w') as f:
f.write('test')
$ strace python test.py 2>&1 | grep open | tail -n 1
open("/tmp/test", O_WRONLY|O_CREAT|O_TRUNC|O_CLOEXEC, 0666) = 3
Каждая строка в выводе
strace
содержит имя системного вызова, его аргументы в скобках и возвращаемое значение. Поскольку некоторые аргументы являются выходными параметрами (используются для возврата результата, а не для передачи данных), вывод строки может быть прерван до завершения системного вызова.В следующем примере вывод приостанавливается до тех пор, пока кто-то не введёт данные в STDIN:
$ strace python -c 'input()'
...
read(0,
👉@BookPython
👍4❤2
Please open Telegram to view this post
VIEW IN TELEGRAM
Чтобы немедленно остановить выполнение программы на Python, следует использовать
И
Поскольку это может быть проблемой, можно использовать функцию
👉@BookPython
sys.exit()
. Альтернативой является функция exit()
, однако она предназначена для использования в интерактивном режиме. Благодаря строковому представлению, она может помочь пользователям, которые пытаются завершить сессию, используя exit
(что поддерживается многими оболочками):
>>> exit
Use exit() or Ctrl-D (i.e. EOF) to exit
>>> str(exit)
'Use exit() or Ctrl-D (i.e. EOF) to exit'
И
exit()
, и sys.exit()
на самом деле не завершают программу, а просто выбрасывают исключение SystemExit
. SystemExit
— это прямой подкласс BaseException
, а значит, он не может быть перехвачен через except Exception
, но может быть перехвачен через except BaseException
или через голый except:
.
>>> try:
... exit()
... except:
... 'Nothing'
...
'Nothing'
Поскольку это может быть проблемой, можно использовать функцию
os._exit
. Она не выбрасывает никаких исключений — просто завершает текущий процесс. Однако это означает, что блоки finally
, а также завершающие действия менеджеров контекста не будут выполнены.
$ python3
Python 3.4.3 (default, Apr 28 2015, 13:37:07)
[GCC 4.8.3 20140911 (Red Hat 4.8.3-9)] on linux
Type "help", "copyright", "credits" or "license" for more information.
>>> try:
... os._exit(42)
... finally:
... print('Bye!')
...
$ ...
👉@BookPython
👍2