В Python 3.4+ вы можете использовать
#codeexample
# Это эквивалентно:
# Вернуть менеджер контекста, который подавляет
# любое из указанных исключений, если они встречаются
# в теле оператора with, а затем возобновляет
# выполнение с первым оператором после конца
# оператора with."
contextlib.suppress()
, чтобы выборочно игнорировать определенные исключения:#codeexample
import contextlib
with contextlib.suppress(FileNotFoundError):
os.remove('somefile.tmp')
# Это эквивалентно:
try:
os.remove('somefile.tmp')
exept FileNotFoundError:
pass
# Вернуть менеджер контекста, который подавляет
# любое из указанных исключений, если они встречаются
# в теле оператора with, а затем возобновляет
# выполнение с первым оператором после конца
# оператора with."
Вместо изменения оформленной функции вы можете создать другой вызываемый класс, который будет возвращать его экземпляры вместо функции:
#codeexample
#codeexample
lass CallableWithOrig:
def __init__(self, to_call, orig):
self._to_call = to_call
self._orig = orig
def __call__(self, *args, **kwargs):
return self._to_call(*args, **kwargs
@property
def orig(self):
if isinstance(self._orig, type(self)):
return self._orig.orig
else:
return self._orig
class SavingOrig:
def __init__(self, another_decorator):
self._another = another_decorator
def __call__(self, f):
return CallableWithOrig(self._another(f), f)
saving_orig = SavingOrig
Вы можете добавлять символы Юникода в строковый литерал не только по его номеру, но и по его имени.
#codeexample
>>> '\N{EM DASH}'
'—'
>>> '\u2014'
'—'
Он также совместим с f-строками:
>>> width = 800
>>> f'Width \N{EM DASH} {width}'
'Width — 800'
#codeexample
>>> '\N{EM DASH}'
'—'
>>> '\u2014'
'—'
Он также совместим с f-строками:
>>> width = 800
>>> f'Width \N{EM DASH} {width}'
'Width — 800'
Если вы хотите, чтобы менеджер контекста приостанавливал сопрограмму при входе или выходе из контекста, вам следует использовать асинхронные менеджеры контекста. Вместо выхода
#codeexample
Асинхронные контекстные менеджеры должны использоваться с асинхронным синтаксисом:
m.__enter__()
и m.__exit__()
Python ожидает m.__aenter__()
и m.__aexit__() соответственно.#codeexample
Асинхронные контекстные менеджеры должны использоваться с асинхронным синтаксисом:
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())
👍1
Символ '\' в обычной строке имеет особое значение. \t - символ табуляции, \r - возврат каретки и т. д.
Вы можете использовать необработанные строки, чтобы отключить это поведение. r '\ t' это просто обратная косая черта и т.
#codeexample
Вы, очевидно, не можете использовать «внутри r» ... ». Тем не менее, он может быть экранирован '\', но '\' сохраняется в строке:
>>> print(r'It\'s insane!')
It\'s insane!
Вы можете использовать необработанные строки, чтобы отключить это поведение. r '\ t' это просто обратная косая черта и т.
#codeexample
Вы, очевидно, не можете использовать «внутри r» ... ». Тем не менее, он может быть экранирован '\', но '\' сохраняется в строке:
>>> print(r'It\'s insane!')
It\'s insane!
Начиная с Python 3.5, на самом деле можно использовать распаковку со словарем и списком литералов.
#codeexample
In : {**{'a': 1}, 'b': 2, **{'c': 3}}
Out: {'a': 1, 'b': 2, 'c': 3}
In : [1, 2, *[3, 4]]
Out: [1, 2, 3, 4]
Для словарей эта форма даже более мощная, чем функция dict, поскольку она позволяет переопределять значения:
In : {{'a': 1, 'b': 1}, 'a': 2, **{'b': 3}}
Out: {'a': 2, 'b': 3}
#codeexample
In : {**{'a': 1}, 'b': 2, **{'c': 3}}
Out: {'a': 1, 'b': 2, 'c': 3}
In : [1, 2, *[3, 4]]
Out: [1, 2, 3, 4]
Для словарей эта форма даже более мощная, чем функция dict, поскольку она позволяет переопределять значения:
In : {{'a': 1, 'b': 1}, 'a': 2, **{'b': 3}}
Out: {'a': 2, 'b': 3}
itertools.tee() создает несколько итераторов из одного. Это может быть полезно, если несколько потребителей должны читать один и тот же поток.
#codeexample
In : a, b, c = tee(iter(input, ''), 3)
In : next(a), next(c)
FIRST
Out: ('FIRST', 'FIRST')
In : next(a), next(b)
SECOND
Out: ('SECOND', 'FIRST')
In : next(a), next(b), next(c)
THIRD
Out: ('THIRD', 'SECOND', 'SECOND')
Данные, которые еще не используются итераторами, хранятся в памяти. Если некоторые из созданных итераторов еще не запущены во время завершения другого, это означает, что все сгенерированные элементы сохраняются в памяти для будущего использования. В этом случае проще и эффективнее использовать list (iter (input, '')) вместо tee.
#codeexample
In : a, b, c = tee(iter(input, ''), 3)
In : next(a), next(c)
FIRST
Out: ('FIRST', 'FIRST')
In : next(a), next(b)
SECOND
Out: ('SECOND', 'FIRST')
In : next(a), next(b), next(c)
THIRD
Out: ('THIRD', 'SECOND', 'SECOND')
Данные, которые еще не используются итераторами, хранятся в памяти. Если некоторые из созданных итераторов еще не запущены во время завершения другого, это означает, что все сгенерированные элементы сохраняются в памяти для будущего использования. В этом случае проще и эффективнее использовать list (iter (input, '')) вместо tee.
Если вы хотите измерить время между двумя событиями, вы должны использовать time.monotonic() вместо time.time(). time.monotonic() никогда не возвращается назад, даже если системные часы обновлены
#codeexample
from contextlib import contextmanager
import time
@contextmanager
def timeit():
start = time.monotonic()
yield
print(time.monotonic() - start)
def main():
with timeit():
time.sleep(2)
main()
#codeexample
from contextlib import contextmanager
import time
@contextmanager
def timeit():
start = time.monotonic()
yield
print(time.monotonic() - start)
def main():
with timeit():
time.sleep(2)
main()
Проблема с вызовом repr других объектов в вашем собственном методе repr заключается в том, что вы не можете гарантировать, что ни один из других объектов не равен self, и вызов не является рекурсивным:
#codeexample
In : p = Pair(1, 2)
In : p
Out: Pair(1, 2)
In : p.right = p
In : p
Out: [...]
RecursionError: maximum recursion depth exceeded while calling a Python object
Чтобы легко решить эту проблему, вы можете использовать декоратор reprlib.recursive_repr:
@reprlib.recursive_repr()
def __repr__(self):
class_name = type(self).__name__
return f'{class_name}({self.left!r}, {self.right!r})'
Now it works:
In : p = Pair(1, 2)
In : p.right = p
In : p
Out: Pair(1, ...)
#codeexample
In : p = Pair(1, 2)
In : p
Out: Pair(1, 2)
In : p.right = p
In : p
Out: [...]
RecursionError: maximum recursion depth exceeded while calling a Python object
Чтобы легко решить эту проблему, вы можете использовать декоратор reprlib.recursive_repr:
@reprlib.recursive_repr()
def __repr__(self):
class_name = type(self).__name__
return f'{class_name}({self.left!r}, {self.right!r})'
Now it works:
In : p = Pair(1, 2)
In : p.right = p
In : p
Out: Pair(1, ...)
Типизация позволяет определить тип для генераторов
Вы можете дополнительно указать, какой тип получен, какой тип может быть отправлен в генератор, а какой возвращен. Генератор [int, None, bool] - это генератор, который выдает целые числа, возвращает логическое значение и не поддерживает g.send ().
#codeexample
Вот немного более сложный пример. chain_ while возвращает другие генераторы, пока один из них не выдаст что-то, что является сигналом для остановки в соответствии с функцией условия:
from typing import Generator, Callable, Iterable, TypeVar
Y = TypeVar('Y')
S = TypeVar('S')
R = TypeVar('R')
def chain_while(
iterables: Iterable[Generator[Y, S, R]],
condition: Callable[[R], bool],
) -> Generator[Y, S, None]:
for it in iterables:
result = yield from it
if not condition(result):
break
def r(x: int) -> Generator[int, None, bool]:
yield from range(x)
return x % 2 == 1
print(list(chain_while(
[
r(5),
r(4),
r(3),
],
lambda x: x is True,
)))
Вы можете дополнительно указать, какой тип получен, какой тип может быть отправлен в генератор, а какой возвращен. Генератор [int, None, bool] - это генератор, который выдает целые числа, возвращает логическое значение и не поддерживает g.send ().
#codeexample
Вот немного более сложный пример. chain_ while возвращает другие генераторы, пока один из них не выдаст что-то, что является сигналом для остановки в соответствии с функцией условия:
from typing import Generator, Callable, Iterable, TypeVar
Y = TypeVar('Y')
S = TypeVar('S')
R = TypeVar('R')
def chain_while(
iterables: Iterable[Generator[Y, S, R]],
condition: Callable[[R], bool],
) -> Generator[Y, S, None]:
for it in iterables:
result = yield from it
if not condition(result):
break
def r(x: int) -> Generator[int, None, bool]:
yield from range(x)
return x % 2 == 1
print(list(chain_while(
[
r(5),
r(4),
r(3),
],
lambda x: x is True,
)))
Некоторые модули могут содержать такие загадочные конструкции:
try:
cache
except NameError:
cache = {}
Похоже, нет смысла делать что-то подобное. Кэш определенно вызывает NameError в начале модуля, так как он не был назначен ранее.
#codeexample
Однако это не тот случай, если модуль перезагружается. Когда это происходит, словарь, содержащий все атрибуты модуля, используется повторно, давая модулю возможность повторно использовать атрибуты своего предыдущего воплощения. Если модуль предназначен для перезагрузки, он может полагаться на эту функцию. Например, приведенный выше код помогает сохранить часть кеша неповрежденной при перезагрузке.
try:
cache
except NameError:
cache = {}
Похоже, нет смысла делать что-то подобное. Кэш определенно вызывает NameError в начале модуля, так как он не был назначен ранее.
#codeexample
Однако это не тот случай, если модуль перезагружается. Когда это происходит, словарь, содержащий все атрибуты модуля, используется повторно, давая модулю возможность повторно использовать атрибуты своего предыдущего воплощения. Если модуль предназначен для перезагрузки, он может полагаться на эту функцию. Например, приведенный выше код помогает сохранить часть кеша неповрежденной при перезагрузке.
👍1
Ваша задача - создать метод, который позволит перебирать части чего-то итерируемого. Каждая часть сама по себе является итеративной, которая повторяет только исходную итерируемую и не хранит никаких данных.
#codeexample
for batch in batches(range(9), lambda y: y > 4):
print('[{}]'.format(','.join(
str(x) for x in batch
)))
На выходе:
0,1,2,3,4]
[5,6,7,8]
Итерация должна быть строго последовательной. Пользователь не должен запрашивать следующую партию, пока предыдущая не будет исчерпана.
# RuntimeError
list(batches(range(9), lambda y: y > 4))
#codeexample
for batch in batches(range(9), lambda y: y > 4):
print('[{}]'.format(','.join(
str(x) for x in batch
)))
На выходе:
0,1,2,3,4]
[5,6,7,8]
Итерация должна быть строго последовательной. Пользователь не должен запрашивать следующую партию, пока предыдущая не будет исчерпана.
# RuntimeError
list(batches(range(9), lambda y: y > 4))
Функция itertools.chain - это способ перебора многих итерируемых элементов, как если бы они были склеены:
#codeexample
In : list(chain(['a', 'b'], range(3), set('xyz')))
Out: ['a', 'b', 0, 1, 2, 'x', 'z', 'y']
Иногда нужно знать, пуст ли генератор (скажем, исчерпан). Чтобы сделать это, вы должны попытаться получить следующий элемент из генератора. Если это работает, и вы хотите вернуть элемент обратно в генератор, что, конечно, невозможно. Вместо этого вы можете приклеить его обратно цепочкой:
def sum_of_odd(gen):
try:
first = next(gen)
except StopIteration:
raise ValueError('Empty generator')
return sum(
x for x in chain([first], gen)
if x % 2 == 1
)
Пример использования:
In : sum_of_odd(x for x in range(1, 6))
Out: 9
In : sum_of_odd(x for x in range(2, 3))
Out: 0
In : sum_of_odd(x for x in range(2, 2))
...
ValueError: Empty generator
#codeexample
In : list(chain(['a', 'b'], range(3), set('xyz')))
Out: ['a', 'b', 0, 1, 2, 'x', 'z', 'y']
Иногда нужно знать, пуст ли генератор (скажем, исчерпан). Чтобы сделать это, вы должны попытаться получить следующий элемент из генератора. Если это работает, и вы хотите вернуть элемент обратно в генератор, что, конечно, невозможно. Вместо этого вы можете приклеить его обратно цепочкой:
def sum_of_odd(gen):
try:
first = next(gen)
except StopIteration:
raise ValueError('Empty generator')
return sum(
x for x in chain([first], gen)
if x % 2 == 1
)
Пример использования:
In : sum_of_odd(x for x in range(1, 6))
Out: 9
In : sum_of_odd(x for x in range(2, 3))
Out: 0
In : sum_of_odd(x for x in range(2, 2))
...
ValueError: Empty generator
👍1
Иногда вы хотите использовать генератор, но вас не волнуют значения, которые он дает. Тем не менее, вам небезразличен какой-либо побочный эффект, это может быть исключение, запись в файл, изменение глобальной переменной и т. д.
Удобный и широко используемый способ сделать это - list (gen ()). Тем не менее, этот код сохраняет все значения в памяти только для того, чтобы сразу же их отбросить. Это может быть нежелательно.
#codeexample
Если вы хотите избежать этого, вы можете использовать deque с ограниченным размером:
from collections import deque
def inversed(nums):
for num in nums:
yield 1 / num
try:
deque(inversed([1, 2, 0]), maxlen=0)
except ZeroDivisionError:
print('E')
Чтобы быть более семантически точным, вам лучше определить свою собственную функцию выпуска:
def exhaust(iterable):
for _ in iterable:
pass
Удобный и широко используемый способ сделать это - list (gen ()). Тем не менее, этот код сохраняет все значения в памяти только для того, чтобы сразу же их отбросить. Это может быть нежелательно.
#codeexample
Если вы хотите избежать этого, вы можете использовать deque с ограниченным размером:
from collections import deque
def inversed(nums):
for num in nums:
yield 1 / num
try:
deque(inversed([1, 2, 0]), maxlen=0)
except ZeroDivisionError:
print('E')
Чтобы быть более семантически точным, вам лучше определить свою собственную функцию выпуска:
def exhaust(iterable):
for _ in iterable:
pass
Пишем функцию, которая принимает в качестве аргумента K list и возвращает все возможные списки из K элементов, где первый элемент из первого списка, второй - из второго и т.д.
#codeexample
assert combinations([1, 2], [3, 4]) == [
[1, 3],
[1, 4],
[2, 3],
[2, 4],
]
#codeexample
assert combinations([1, 2], [3, 4]) == [
[1, 3],
[1, 4],
[2, 3],
[2, 4],
]