Главная страница

С. В. Вабищевич инженерпрограммист компании ооо ск хайникс мемори солюшнс Восточная Европа


Скачать 1.28 Mb.
НазваниеС. В. Вабищевич инженерпрограммист компании ооо ск хайникс мемори солюшнс Восточная Европа
Дата22.11.2021
Размер1.28 Mb.
Формат файлаpdf
Имя файлаKositsin.pdf
ТипУчебно-методическое пособие
#278468
страница6 из 9
1   2   3   4   5   6   7   8   9
assert – statement, позволяющий проверить на истинность некоторое выражение:
>>> def calculate_binomial_mean(n, p):
>>>
assert n > 0, ‘number of experiments must be
positive’
>>>
assert 0 <= p <= 1, ‘probability must be in [0; 1]’
>>>
return n * p
Важно! Скобки в assert имеют смысл проверки кортежа на пу- стоту.
1
https
://pypi.python.org/pypi/line_profiler
2
https
://pypi.python.org/pypi/memory_profiler/
3
https
://pypi.python.org/pypi/snakeviz
4
https
://habrahabr.ru/company/mailru/blog/202832

78
ЮНИТ- ТЕСТЫ
Общая идея юнит- тестов:
– разбить код на независимые части (юниты);
– тестировать каждую часть отдельно.
Преимущества:
нужно меньше тестов;
– проще отлаживать.
Недостатки:
– нужны тесты, проверяющие взаимодействие юнитов.
БИБЛИОТЕКА DOCTEST
Библиотека
doctest (Python 2.x)
1
, (Python 3.x)
2
позволяет расположить тест непосредственно в документации, чтобы и пока- зать, и проверить, как работает функция.
Недостаток подхода в его сложности: проверка некорректных входных данных или результатов сложных типов трудоемка.
>>> def factorial(n):
>>>
“””Return the factorial of n, an exact integer >= 0.


>>> factorial(5)

120

“””
>>>
pass # implementation is here
>>>
>>> if __name__ == ‘__main__’:
>>>
import doctest
>>>
doctest.testmod()
БИБЛИОТЕКА UNITTEST
Тесты можно писать, используя библиотеку
unittest
(Python 2.x)
3
, (Python 3.x)
4
, что позволяет, в частности, группиро-
1
https
://docs.python.org/2.7/library/doctest.html
2
https
://docs.python.org/3.7/library/doctest.html
3
https
://docs.python.org/2.7/library/unittest.html
4
https
://docs.python.org/3.7/library/unittest.html

79
вать тесты в test cases, а также использовать множество удобных проверок.
>>> import unittest
>>>
>>> class TestFactorial(unittest.TestCase):
>>>
def test_simple(self):
>>>
self.assertEqual(factorial(5), 120)
>>>
>>> if __name__ == ‘__main__’:
>>>
unittest.main()
Возможности unittest:
– проверка различных типов и видов возвращаемых значе- ний:
assertTrue, assertIsNone, assertAlmostEqual, assertRaisesRegexp и др.;
– возможность подготовить тестирование: методы
setup
(
setUpClass) и teardown (tearDownClass) – вызываются перед и по- сле каждого запуска теста (класса test case), создавая и удаляя ис- пользуемые объекты;
– возможность пропустить тест по некоторому условию (см.
unittest.SkipTest).
Также есть библиотека
pytest, в которой все проверки можно проводить с помощью
assert statement’ов, и библиотека nose, объе- диняющая все виды тестов.
ПОДМЕНА ОБЪЕКТОВ
При помощи библиотеки
mock
1
(в стандартной библио- теке с Python 3.3, см. примеры)
2
можно подменить любой объект, будь то поток ввода- вывода
stdout, некоторый модуль, класс, атри- бут, свой ство, метод.
>>> from io import StringIO
>>>
>>> class InterfaceTestCase(unittest.TestCase):
>>>
def setUp(self):
>>>
self._stdout_mock = self._setup_stdout_mock()
1
https
://docs.python.org/3.7/library/unittest.mock.html
2
https
://docs.python.org/3.7/library/unittest.mock‑ examples.html

80
>>>
>>>
def _setup_stdout_mock(self):
>>> patcher = mock.patch(‘sys.stdout’, new=StringIO())
>>>
patcher.start()
>>>
self.addCleanup(patcher.stop)
>>>
return patcher.new
3.7. ДЕКОРАТОРЫ
Декораторы (PEP-318)
1
:
– выполняют некоторое дополнительное действие при вызове или создании функции;
– модифицируют функцию после создания;
– могут принимать аргументы;
– упрощают написание кода.
Рассмотрим пример декоратора – функции, которая при вызо- ве декорированной функции проверяет, возвращенное ей значение имеет тип float. Функцию- декоратор назовем check_return_type_float.
>>> @check_return_type_float
>>> def g():
>>> return ‘not a float’
Эквивалентной записью будет следующая:
>>> def g():
>>> return ‘not a float’
>>>
>>> g = check_return_type_float(g)
Декоратор реализован как функция, которая возвращает другую функцию – wrapper:
>>> def check_return_type_float(f):
>>>
def wrapper(*args, **kwargs):
>>> result = f(*args, **kwargs)
>>>
assert isinstance(result, float)
>>>
return result
>>>
return wrapper
1
https
://www.python.org/dev/peps/pep‑0318

81
Поскольку декоратор возвращает другую функцию, в примере
check_return_type_float у переменной
f будет имя ‘wrapper’.
Для того чтобы метаданные (имя, документация) были кор- ректными, внутренней функции (wrapper’у) добавляют декоратор
functools.wraps:
>>> def check_return_type_float(f):
>>>
@functools.wraps(f)
>>>
def wrapper(*args, **kwargs):
>>>
# some code
>>>
return wrapper
Замечание. В Python 3 декорировать можно не только функции, но и классы (PEP-3129)
1
РЕАЛИЗАЦИЯ ДЕКОРАТОРА С ПОМОЩЬЮ КЛАССА
>>> class FloatTypeChecker(object):
>>>
def __init__(self, f):
>>> self.f = f
>>>
>>>
def __call__(self, *args, **kwargs):
>>> result = self.f(*args, **kwargs)
>>>
assert isinstance(result, float)
>>>
return result
>>>
>>> check_return_type_float = FloatTypeChecker
Вопрос: Как применить здесь
functools.wraps?
Замечание. Добавлять данный декоратор желательно везде. Это и хороший стиль кода, и так остается возможность узнать имя вы- званной функции run- time.
ДЕКОРАТОРЫ С ПАРАМЕТРАМИ
Для создания более общего декоратора логично добавить ему возможность принимать параметры.
>>> def check_return_type(type_):
>>>
def wrapper(f):
1
https
://www.python.org/dev/peps/pep‑3129

82
>>>
@functools.wraps(f)
>>>
def wrapped(*args, **kwargs):
>>> result = f(*args, **kwargs)
>>>
assert isinstance(result, type_)
>>>
return result
>>>
return wrapped
>>> return wrapper
>>>
>>> @check_return_type(float)
>>> def g():
>>> return ‘not a float’
НЕСКОЛЬКО ДЕКОРАТОРОВ
Эквивалентной записью будет следующая:
>>> def g():
>>> return ‘not a float’
>>>
>>> g = check_return_type(float)(g)
Допустимо применять несколько декораторов – один над другим.
>>> @decorator2
>>> @decorator1
>>> def f():
>>> pass
Эквивалентная запись применения декораторов к функции
f имеет вид:
>>> f = decorator2(decorator1(f))
ПАРАМЕТРИЗОВАННЫЙ ДЕКОРАТОР- КЛАСС
Декораторы с параметрами можно реализовать как класс.
В таком случае параметры будут сохраняться в методе
__init__, а де- корированную функцию следует возвращать в
__call__.
>>> class FloatTypeChecker(object):
>>>
def __init__(self, result_type):
>>> self._result_type = result_type

83
>>>
>>> def __call__(self, f):
>>>
@functools.wraps(f)
>>>
def wrapper(*args, **kwargs):
>>> result = f(*args, **kwargs)
>>>
assert isinstance(result, self.
_result_type)
>>>
return result
>>>
return wrapper
СВОЙ СТВА КАК ДЕКОРАТОРЫ
Рассмотрим пример реализации свой ства:
>>> class Animal(object):
>>>
def __init__(self, age=0):
>>>
self._age = age
>>>
>>>
@property
>>>
def age(self):
>>>
“””age of animal”””
>>>
return self._age
>>>
>>>
@age.setter
>>>
def age(self, age):
>>>
assert age >= self._age
>>>
self._age = age
Свой ства – это дескрипторы, которые можно создать, декори- руя методы с помощью
property (docstring- свой ства получаются из getter’а):
– getter – @property
– setter – @.setter
– deleter – @.delete
Полный синтаксис декоратора- дескриптора
property имеет вид:
age = property(fget, fset, fdelete, doc)
Замечание. Создать write- only- свой ство можно, только явно вы- звав
property с параметром fget, равным None.

84 3.8. ИТЕРАТОРЫ И ГЕНЕРАТОРЫ
SEQUENCE AND ITERABLE
Последовательность (sequence) – упорядоченный индекси-
руемый набор объектов, например
list, tuple и str.
У этих объектов переопределены «магические методы»
__len__
(длина последовательности) и
__getitem__ (отвечает за индексацию).
Итерируемое (iterable) – упорядоченный набор объектов, элемен- ты которого можно получать по одному.
У таких объектов реализован метод
__iter__ – возвращает итера- тор, который позволяет обойти итерируемый объект.
Итератор (iterator) представляет собой «поток данных» – он по- зволяет обойти все элементы итерируемого объекта, возвращая их в некоторой последовательности.
В итераторе переопределен метод
__next__ (next в Python 2), вы- зов которого либо возвращает следующий объект, либо бросает ис- ключение
StopIteration, если все объекты закончились.
Для явного получения итератора и взятия следующего элемента используются built- in- методы
iter и next.
Следующие примеры равносильны:
>>> for item in sequence:
>>>
action(item)
>>> def for_sequence(sequence, action): # “for” for
sequence
>>>
i, length = 0, len(sequence)
>>>
while i < length:
>>>
item = sequence[i]
>>>
action(item)
>>>
i += 1
>>> def for_iterable(iterable, action): # “for” for
iterator
>>>
iterator = iter(iterable)
>>>
try:
>>>
while True:
>>>
item = next(iterator)
>>>
action(item)
>>>
except StopIteration:
>>>
pass

85
ИТЕРАТОРЫ
Итераторы представляют собой классы, содержащие ин- формацию о текущем состоянии итерирования по объекту (напри- мер, индекс).
После обхода всех элементов итератор «истощается» (exhausted), бросая исключение
StopIteration при каждом следующем вызове
__next__.
Замечания. Функция next имеет второй параметр – значение по умолчанию, которое будет возвращено, когда итератор исчерпается.
У функции
iter также есть второй аргумент – значение, до полу-
чения которого будет продолжаться итерирование.
Пример реализации итератора:
>>> class RangeIterator(collections.Iterator):
>>>
def __init__(self, start, stop=None, step=1):
>>>
self._start = start if stop is not None
else 0
>>>
self._stop = stop if stop is not None else
start
>>>
self._step = step # positive only
>>>
self._current = self._start
>>>
>>>
def __next__(self):
>>>
if self._current >= self._stop:
>>>
raise StopIteration()
>>>
>>>
result = self._current
>>>
self._current += self._step
>>>
return result
Поскольку итераторы хранят информацию о состоянии, их мож- но прервать и впоследствии продолжить итерироваться.
Вопрос: что выведет следующий код?
>>> odd_indices_iterator = RangeIterator(1, 10, 2)
>>>
>>> for idx in odd_indices_iterator:
>>>
if idx > 5:
>>>
break
>>>
print(idx)
>>>
>>> for idx in odd_indices_iterator:
>>>
print(idx)

86
ИТЕРИРУЕМЫЕ И ИСТОЩАЕМЫЕ
Последовательности итерируемы и неистощаемы (можно много раз итерироваться по ним).
Итерируемые объекты (не последовательности) могут как не истощаться (
range в Python 3.x или xrange в Python 2.x), так и истощаться (генераторы).
Итераторы итерируемы (возвращают сами себя) и истощаемы
(можно только один раз обойти).
Замечания. Часто в классах не реализуют отдельный класс- итератор. В таком случае метод
__iter__ возвращает генератор.
В Python 2
range возвращает список, а xrange – генератор.
Пример итерируемого объекта
>>> class SomeSequence(collections.Iterable):
>>> def __init__(self, *items):
>>>
self._items = items
>>>
>>> def __iter__(self):
>>>
for item in self._items:
>>>
yield item
>>>
>>> def __iter__(self):
>>>
yield from self._items # только в Python
3.3+
>>>
>>> def __iter__(self): # простой и менее гибкий
вариант
>>>
return iter(self._items)
Напоминание. В модуле collections есть и другие базовые клас- сы, например
Sequence. Эти классы реализуют множество полезных методов, требуя переопределить лишь несколько.
ГЕНЕРАТОР
Генератор – итератор, с которым можно взаимодейство- вать (PEP-255)
1 1 https://www.python.org/dev/peps/pep-0255

87
Каждый следующий объект возвращается с помощью выражения
yield. Это выражение приостанавливает работу генератора и переда- ет значение в вызывающую функцию. При повторном вызове испол- нение продолжается с текущей позиции либо до следующего
yield, либо до конца функции.
Генераторы удобно использовать, когда вся последовательность сразу не нужна, а следует лишь по ней итерироваться.
>>> assert all(x % 2 for x in range(1, 10, 2))
Замечание. Выражения- генераторы имеют вид comprehensions с круглыми скобками. При передаче в функцию дополнительные кру- глые скобки не нужны.
ЗАМЕЧАНИЯ ПО ГЕНЕРАТОРАМ
Конструкция
yield from делегирует, по сути, исполнение некоторому другому итератору (c Python 3.3, PEP-380)
1
В Python 3 появилась возможность у генераторов (например,
range) узнать длину генерируемой ими последовательности (метод
__len__) и проверить, генерируют ли они определенный элемент (ме- тод
__contains__).
Также в Python 3 есть специальный класс –
collections.
ChainMap, который представляет собой обертку над нескольки- ми mapping’ами.
Генераторы могут использоваться как coroutines: выражение yield не только отдает значение из функции, но может принимать и некоторые значения, исключения или завершать выполнение ге- нератора. Подробнее о coroutines, а также их асинхронных верси- ях написано здесь: документация
2
, PEP-342 3
, PEP-479 4
, PEP-492 5
,
PEP-525 6
, PEP-530 7
, PEP-3148 8
, PEP-3156 9
1 https://www.python.org/dev/peps/pep-0380 2
https
://docs.python.org/3.7/library/asyncio.html
3
https
://www.python.org/dev/peps/pep‑0342 4
https
://www.python.org/dev/peps/pep‑0479 5
https
://www.python.org/dev/peps/pep‑0492 6
https
://www.python.org/dev/peps/pep‑0525 7
https
://www.python.org/dev/peps/pep‑0530 8
https
://www.python.org/dev/peps/pep‑3148 9
https
://www.python.org/dev/peps/pep‑3156

88
ДОПОЛНИТЕЛЬНЫЕ СПОСОБЫ ИТЕРИРОВАНИЯ
В стандартной библиотеке есть модуль
itertools, в кото- ром реализовано множество итераторов:

cycle – зацикливает некоторый iterable;

count – бесконечный счетчик с заданным начальным значе- нием и шагом;

repeat – возвращает некоторое значение заданное число раз.
Есть и комбинаторные итераторы:

product– итератор по декартову произведению последова- тельностей (по сути, генерирует кортежи, если бы был реализован вложенный
for);

combinations– итератор по упорядоченным сочетаниям эле- ментов;

permutations– итератор по перестановкам переданных эле- ментов.
Дополнительные способы итерирования:

chain – итерируется последовательно по нескольким iterable;

zip_longest – аналог zip, только прекращает итерироваться, когда исчерпывается не первый, а последний итератор;

takewhile/dropwhile/filterfalse/compress – отбирает элемен- ты последовательности в соответствии с предикатом;

islice – итераторный аналог slice (не создает списка элементов);

groupby – группирует последовательные элементы;

starmap – аналог map, только распаковывает аргумент при пе- редаче;

tee – создает n копий итератора.
Замечания. В Python 2 доступны ifilter и izip – итераторные ана- логи
filter и zip.
В Python 3.2 появилась функция
accumulate, которая возвраща- ет итератор по кумулятивному массиву.
3.9. МЕНЕДЖЕРЫ КОНТЕКСТА
В процессе работы с файлами важно корректно работать с исключениями: файл необходимо закрыть в любом случае.
Данный синтаксис позволяет закрыть файл по выходе из бло- ка
with:
>>> with open(file_name) as f:
>>>
# some actions

89
Функция
open возвращает специальный объект – context manager.
Менеджер контекста последовательно инициализирует контекст, вхо-
дит в него и корректно обрабатывает выход.
1   2   3   4   5   6   7   8   9


написать администратору сайта