
Это седьмая подборка советов про Python и программирование из моего авторского канала @pythonetc.
Предыдущие подборки:
Множественные контексты
Иногда бывает нужно запустить какой-то блок кода в нескольких менеджерах контекста:
with open('f') as f: with open('g') as g: with open('h') as h: pass
Со времён Python 2.7 и 3.1 это можно сделать с помощью одного выражения:
o = open with o('f') as f, o('g') as g, o('h') as h: pass
А раньше можно было воспользоваться функцией
contextlib.nested:with nested(o('f'), o('g'), o('h')) as (f, g, h): pass
Если вы работаете с неопределённым количеством менеджеров контекста, то лучше выбрать более продвинутые инструменты.
contextlib.ExitStack позволяет в любое время входить в любое количество контекстов и гарантирует выход из них по окончании исполнения:with ExitStack() as stack: f = stack.enter_context(o('f')) g = stack.enter_context(o('g')) other = [ stack.enter_context(o(filename)) for filename in filenames ]
Объекты в памяти интерпретатора
Ко всем объектам, которые в данный момент находятся в памяти интерпретатора, можно получить доступ с помощью
gc.get_objects():In : class A: ...: def __init__(self, x): ...: self._x = x ...: ...: def __repr__(self): ...: class_name = type(self).__name__ ...: x = self._x ...: return f'{class_name}({x!r})' ...: In : A(1) Out: A(1) In : A(2) Out: A(2) In : A(3) Out: A(3) In : [x for x in gc.get_objects() if isinstance(x, A)] Out: [A(1), A(2), A(3)]
Символы-цифры
In : int('୧৬༣') Out: 163
0 1 2 3 4 5 6 7 8 9 — это не единственные символы, считающиеся цифрами. Python соблюдает правила Unicode и считает цифрами несколько сотен символов. Полный список здесь.Это имеет значение для функций вроде
int, unicode.isdecimal и даже re.match:In : int('௯') Out: 9 In : '٢'.isdecimal() Out: True In : bool(re.match('\d', '౫')) Out: True
Полночь по UTC
>>> bool(datetime(2018, 1, 1).time()) False >>> bool(datetime(2018, 1, 1, 13, 12, 11).time()) True
До Pyhon 3.5 объекты
datetime.time() считались ложными, если они представляли полночь по UTC. Это может приводить к неочевидным багам. В следующем примере if not может не выполниться не потому, что create_timeявляется
None, а потому что это полночь.def create(created_time=None) -> None: if not created_time: created_time = datetime.now().time()
Обойти этот баг можно с помощью явной проверки на
None: if created_time is None.Асинхронная работа в ФС
Python не поддерживает асинхронные операции с файлами. Чтобы сделать их неблокирующими, приходится использовать треды.
Для асинхронного исполнения кода в потоке нужно использовать метод
loop.run_in_executor.За вас это может сделать сторонний модуль
aiofiles, предоставляющий удобный и простой интерфейс:async with aiofiles.open('filename', mode='r') as f: contents = await f.read()
