Некоторые модули могут содержать такие загадочные конструкции:
try:
cache
except NameError:
cache = {}
Похоже, нет смысла делать что-то подобное. Кэш определенно вызывает NameError в начале модуля, так как он не был назначен ранее.
#codeexample
Однако это не тот случай, если модуль перезагружается. Когда это происходит, словарь, содержащий все атрибуты модуля, используется повторно, давая модулю возможность повторно использовать атрибуты своего предыдущего воплощения. Если модуль предназначен для перезагрузки, он может полагаться на эту функцию. Например, приведенный выше код помогает сохранить часть кеша неповрежденной при перезагрузке.
try:
cache
except NameError:
cache = {}
Похоже, нет смысла делать что-то подобное. Кэш определенно вызывает NameError в начале модуля, так как он не был назначен ранее.
#codeexample
Однако это не тот случай, если модуль перезагружается. Когда это происходит, словарь, содержащий все атрибуты модуля, используется повторно, давая модулю возможность повторно использовать атрибуты своего предыдущего воплощения. Если модуль предназначен для перезагрузки, он может полагаться на эту функцию. Например, приведенный выше код помогает сохранить часть кеша неповрежденной при перезагрузке.
Ваша задача - создать метод, который позволит перебирать части чего-то итерируемого. Каждая часть сама по себе является итеративной, которая повторяет только исходную итерируемую и не хранит никаких данных.
#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
Иногда вы хотите использовать генератор, но вас не волнуют значения, которые он дает. Тем не менее, вам небезразличен какой-либо побочный эффект, это может быть исключение, запись в файл, изменение глобальной переменной и т. д.
Удобный и широко используемый способ сделать это - 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'