Ваша задача - создать метод, который позволит перебирать части чего-то итерируемого. Каждая часть сама по себе является итеративной, которая повторяет только исходную итерируемую и не хранит никаких данных.
#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],
]
#codeexample
Стандартный модуль json имеет интерфейс командной строки, который может быть полезен для предварительного преобразования JSON одним только Python. Модуль для этого называется json.tool и должен выгдядить так:
$ echo '{"a": [], "b": "c"}' | python -m json.tool
{
"a": [],
"b": "c"
}
Стандартный модуль json имеет интерфейс командной строки, который может быть полезен для предварительного преобразования JSON одним только Python. Модуль для этого называется json.tool и должен выгдядить так:
$ echo '{"a": [], "b": "c"}' | python -m json.tool
{
"a": [],
"b": "c"
}
Когда вы пишете пользовательский __repr__ для какого-либо объекта, вы обычно хотите включить представление его атрибутов. Для этого вы должны выполнить форматирование вызова repr() для объектов, так как он вызывает str() по умолчанию.
#codeexample
Это делается с помощью !r :
class Pair:
def __init__(self, left, right):
self.left = left
self.right = right
def __repr__(self):
class_name = type(self).__name__
return f'{class_name}({self.left!r}, {self.right!r})'
#codeexample
Это делается с помощью !r :
class Pair:
def __init__(self, left, right):
self.left = left
self.right = right
def __repr__(self):
class_name = type(self).__name__
return f'{class_name}({self.left!r}, {self.right!r})'
Если вы хотите перебрать несколько итераций одновременно, вы можете использовать функцию zip (она не имеет ничего общего с форматом файла ZIP):
#codeexample
from datetime import timedelta
names = [
'Eleven. Return and Revert',
'Wilderness',
'The Menagerie Inside',
'Evaporate',
]
years = [
2010,
2013,
2015,
2018,
]
durations = [
timedelta(minutes=57, seconds=38),
timedelta(minutes=48, seconds=5),
timedelta(minutes=46, seconds=34),
timedelta(minutes=43, seconds=25),
]
print('Midas Fall LPs:')
for name, year, duration in zip(
names, years, durations
):
print(f' * {name} ({year}) — {duration}')
Output:
Midas Fall LPs:
* Eleven. Return and Revert (2010) — 0:57:38
* Wilderness (2013) — 0:48:05
* The Menagerie Inside (2015) — 0:46:34
* Evaporate (2018) — 0:43:25
#codeexample
from datetime import timedelta
names = [
'Eleven. Return and Revert',
'Wilderness',
'The Menagerie Inside',
'Evaporate',
]
years = [
2010,
2013,
2015,
2018,
]
durations = [
timedelta(minutes=57, seconds=38),
timedelta(minutes=48, seconds=5),
timedelta(minutes=46, seconds=34),
timedelta(minutes=43, seconds=25),
]
print('Midas Fall LPs:')
for name, year, duration in zip(
names, years, durations
):
print(f' * {name} ({year}) — {duration}')
Output:
Midas Fall LPs:
* Eleven. Return and Revert (2010) — 0:57:38
* Wilderness (2013) — 0:48:05
* The Menagerie Inside (2015) — 0:46:34
* Evaporate (2018) — 0:43:25
#codeexample
Если вы хотите создать словарь из известного набора ключей и некоторого фиксированного значения для всех из них, вы можете использовать словарные выражения:
>>> keys = ['a', 'b', 'c']
>>> {k: True for k in keys}
{'a': True, 'b': True, 'c': True}
Однако, у класса dict есть метод класса fromkeys, разработанный специально для этого случая:
>>> dict.fromkeys(keys, True)
{'a': True, 'b': True, 'c': True}
Вы можете добавлять символы Юникода в строковый литерал не только по его номеру, но и по его имени.
>>> '\N{EM DASH}'
'—'
>>> '\u2014'
'—'
Это также совместимо с f-strings:
>>> width = 800
>>> f'Width \N{EM DASH} {width}'
'Width — 800'
Если вы хотите создать словарь из известного набора ключей и некоторого фиксированного значения для всех из них, вы можете использовать словарные выражения:
>>> keys = ['a', 'b', 'c']
>>> {k: True for k in keys}
{'a': True, 'b': True, 'c': True}
Однако, у класса dict есть метод класса fromkeys, разработанный специально для этого случая:
>>> dict.fromkeys(keys, True)
{'a': True, 'b': True, 'c': True}
Вы можете добавлять символы Юникода в строковый литерал не только по его номеру, но и по его имени.
>>> '\N{EM DASH}'
'—'
>>> '\u2014'
'—'
Это также совместимо с f-strings:
>>> width = 800
>>> f'Width \N{EM DASH} {width}'
'Width — 800'
#codeexample
f-строки позволяют указать ширину для печатаемого значения, а также другие спецификаторы формата:
>>> x = 42
>>> f'{x:5}+{x:15f}'
' 42+ 42.000000'
Они также могут содержать оцененные выражения, которые могут быть полезны, когда ширина неизвестна заранее:
def print_table(matrix):
cols_width = [
max(len(str(row[col])) for row in matrix)
for col in range(len(matrix[0]))
]
for row in matrix:
for i, cell in enumerate(row):
print(
f'{cell:{cols_width[i]}} ',
end=''
)
print()
albums = [
['Eleven. Return and Revert', 2010],
['Wilderness', 2013],
['The Menagerie Inside', 2015],
['Evaporate', 2018],
]
print_table(albums)
На выходе:
Eleven. Return and Revert 2010
Wilderness 2013
The Menagerie Inside 2015
Evaporate 2018
f-строки позволяют указать ширину для печатаемого значения, а также другие спецификаторы формата:
>>> x = 42
>>> f'{x:5}+{x:15f}'
' 42+ 42.000000'
Они также могут содержать оцененные выражения, которые могут быть полезны, когда ширина неизвестна заранее:
def print_table(matrix):
cols_width = [
max(len(str(row[col])) for row in matrix)
for col in range(len(matrix[0]))
]
for row in matrix:
for i, cell in enumerate(row):
print(
f'{cell:{cols_width[i]}} ',
end=''
)
print()
albums = [
['Eleven. Return and Revert', 2010],
['Wilderness', 2013],
['The Menagerie Inside', 2015],
['Evaporate', 2018],
]
print_table(albums)
На выходе:
Eleven. Return and Revert 2010
Wilderness 2013
The Menagerie Inside 2015
Evaporate 2018
__init__ позволяет изменить объект сразу после создания. Если вы хотите контролировать то, что уже создано, используйте __new__:
#codeexample
from typing import Tuple, Dict
from cached_property import cached_property
class Numbers:
_LOADED: Dict[Tuple[int, ...], 'Numbers'] = {}
def __new__(cls, ints: Tuple[int, ...]):
if ints not in cls._LOADED:
obj = super().__new__(cls)
cls._LOADED[ints] = obj
return cls._LOADED[ints]
def __init__(self, ints: Tuple[int, ...]):
self._ints = ints
@cached_property
def biggest(self):
print('calculating...')
return max(self._ints)
print(Numbers((4, 3, 5)).biggest)
print(Numbers((4, 3, 5)).biggest)
print(Numbers((4, 3, 6)).biggest)
#codeexample
from typing import Tuple, Dict
from cached_property import cached_property
class Numbers:
_LOADED: Dict[Tuple[int, ...], 'Numbers'] = {}
def __new__(cls, ints: Tuple[int, ...]):
if ints not in cls._LOADED:
obj = super().__new__(cls)
cls._LOADED[ints] = obj
return cls._LOADED[ints]
def __init__(self, ints: Tuple[int, ...]):
self._ints = ints
@cached_property
def biggest(self):
print('calculating...')
return max(self._ints)
print(Numbers((4, 3, 5)).biggest)
print(Numbers((4, 3, 5)).biggest)
print(Numbers((4, 3, 6)).biggest)
#codeexample
`complex`- это встроенный тип Python для комплексных чисел:
>>> complex(1, 2).real
1.0
>>> abs(complex(3, 4))
5.0
>>> complex(1, 2) == complex(1, -2).conjugate()
True
>>> str(complex(2, -3))
'(2-3j)'
Нет необходимости использовать его напрямую, поскольку в Python есть литералы для комплексных чисел:
>>> (3 + 4j).imag
4.0
>>> not (3 + 4j)
False
>>> (-3 - 4j) + (2 - 2j)
(-1-6j)
`complex`- это встроенный тип Python для комплексных чисел:
>>> complex(1, 2).real
1.0
>>> abs(complex(3, 4))
5.0
>>> complex(1, 2) == complex(1, -2).conjugate()
True
>>> str(complex(2, -3))
'(2-3j)'
Нет необходимости использовать его напрямую, поскольку в Python есть литералы для комплексных чисел:
>>> (3 + 4j).imag
4.0
>>> not (3 + 4j)
False
>>> (-3 - 4j) + (2 - 2j)
(-1-6j)
Вы можете перевести или удалить символы строки (как это делает утилита tr) с помощью метода перевода str:
#codeexample
>>> 'Hello, world!'.translate({
... ord(','): ';',
... ord('o'): '0',
... })
'Hell0; w0rld!'
Единственный аргумент перевода - это словарь, отображающий коды символов на символы (или коды). Обычно такой словарь удобнее создавать статическим методом str.maketrans:
>>> 'Hello, world!'.translate(str.maketrans({
... ',': ';',
... 'o': '0',
... }))
'Hell0; w0rld!'
Or even:
>>> 'Hello, world!'.translate(str.maketrans(
... ',o', ';0'
... ))
'Hell0; w0rld!'
Третий аргумент для удаления параметров:
>>> tr = str.maketrans(',o', ';0', '!')
>>> tr
{44: 59, 111: 48, 33: None}
>>> 'Hello, world!'.translate(tr)
'Hell0; w0rld'
#codeexample
>>> 'Hello, world!'.translate({
... ord(','): ';',
... ord('o'): '0',
... })
'Hell0; w0rld!'
Единственный аргумент перевода - это словарь, отображающий коды символов на символы (или коды). Обычно такой словарь удобнее создавать статическим методом str.maketrans:
>>> 'Hello, world!'.translate(str.maketrans({
... ',': ';',
... 'o': '0',
... }))
'Hell0; w0rld!'
Or even:
>>> 'Hello, world!'.translate(str.maketrans(
... ',o', ';0'
... ))
'Hell0; w0rld!'
Третий аргумент для удаления параметров:
>>> tr = str.maketrans(',o', ';0', '!')
>>> tr
{44: 59, 111: 48, 33: None}
>>> 'Hello, world!'.translate(tr)
'Hell0; w0rld'