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

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


Скачать 1.28 Mb.
НазваниеС. В. Вабищевич инженерпрограммист компании ооо ск хайникс мемори солюшнс Восточная Европа
Дата22.11.2021
Размер1.28 Mb.
Формат файлаpdf
Имя файлаKositsin.pdf
ТипУчебно-методическое пособие
#278468
страница7 из 9
1   2   3   4   5   6   7   8   9
Пример менеджера контекста
>>> class ContextManager(object):
>>>
def __init__(self):
>>>
print(‘__init__()’)
>>>
>>>
def __enter__(self):
>>>
print(‘__enter__()’)
>>>
return ‘some data’
>>>
>>>
def __exit__(self, exc_type, exc_val, exc_tb):
>>>
print(‘__exit__({}, {})’.format(
>>>
exc_type.__name__, exc_val))
>>>
>>> with ContextManager() as c:
>>>
print(‘inside context “%s”’% c)
Менеджер контекста работает следующим образом:
– создается и инициализируется (метод
__init__);
– организуется вход в контекст (метод
__enter__) и возвращается объект контекста (в примере с файлом – объект типа
file);
– выполняются действия внутри контекста (внутри блока
with);
– организуется выход из контекста с возможной обработкой ис- ключений (метод
__exit__).
В примере будет выведено следующее:
__init__()
__enter__() inside context “some data”
__exit__(None, None)
Замечания. Если исключения не произошло, то параметры, передаваемые в функцию
__exit__ – тип, значение исключения и
traceback – имеют значения None.
Менеджер контекста, реализуемый функцией
open, по выходе из контекста просто вызывает метод
close (см. декоратор contextlib.
closing).

90
Менеджеры контекста используются:
– для корректной, более простой и переносимой обработки ис- ключений в некотором блоке кода;
– управления ресурсами.
Декоратор contextlib.contextmanager позволяет создать менеджер контекста из функции- генератора, что значительно упрощает син- таксис.
Менеджер контекста из генератора
>>> @contextlib.contextmanager
>>> def get_context():
>>>
print(‘__enter__()’)
>>>
try:
>>>
yield ‘some data’
>>>
finally:
>>>
print(‘__exit__()’)
>>>
>>> with get_context() as c:
>>>
print(‘inside context “%s”’% c)
__enter__()
inside context “some data”
__exit__()
3.10. РАБОТА С АТРИБУТАМИ
ДИНАМИЧЕСКИЕ АТРИБУТЫ
Python позволяет создавать, изменять и удалять атрибу- ты run- time. За это отвечают следующие магические методы и гло- бальные функции:
– получение атрибута по имени: __
getattr__, __getattribute__ или функция
getattr;
– присваивание атрибута по имени:
__setattr__ или функция
setattr;
– удаление атрибута по имени:
__delattr__ или функция delattr;
– проверка наличия атрибута: функция
hasattr (функция- обертка над
getattr).

91
Напоминание. Некоторые объекты являются readonly, например добавлять или удалять атрибуты
object нельзя.
Замечание. Изменять атрибуты можно (не рекомендуется), мо- дифицируя
__dict__.
ПРИМЕР РАБОТЫ С АТРИБУТАМИ
>>> class A(object):
>>>
def f(self):
>>>
pass
>>>
>>>a = A()
>>>
>>> assert hasattr(a, ‘f’) and hasattr(A, ‘f’) # ‘f’
is a class attribute
>>> assert getattr(a, ‘g’, None) is None # no such
attribute -> default
>>>
>>>setattr(a, ‘x’, 2) # a.x = 2
>>> assert getattr(a, ‘x’) == 2 # assert a.x == 2
>>> assert not hasattr(A, ‘x’) # attribute has been
set for instance only
>>>
>>>delattr(a, ‘x’) # del a.x
>>> assert not (hasattr(a, ‘x’) or hasattr(A, ‘x’))
МАГИЧЕСКИЕ МЕТОДЫ РАБОТЫ С АТРИБУТАМИ
Пример реализации методов работы с атрибутами
>>> class Proxy(object):
>>>
def __init__(self, inner_object):
>>>
self._inner_object = inner_object
>>>
>>>
def __setattr__(self, name, value):
>>>
if name != ‘_inner_object’:
>>>
setattr(self._inner_object, name, value)
>>>
else:
>>>
super(Proxy, self).__setattr__(name, value)
>>>

92
>>>
def __getattribute__(self, name):
>>>
if name == ‘_inner_object’:
>>>
return object.__getattribute__(self, name)
>>>
return getattr(self._inner_object, name)
Пример реализации методов работы с атрибутами
>>>p = Proxy([1]) # p._inner_object = [1]
>>>p.append(2) # p._inner_object = [1, 2]
Обратите внимание на вызовы методов базовых классов:
super(Proxy, self).__setattr__(name, value)
object.__getattribute__(self, name)
Такие вызовы (в случае одинаковых методов, разумеется) экви- валенты как через
super(…), так и через .method_name.
ЗАМЕЧАНИЯ ПО РАБОТЕ С АТРИБУТАМИ
В примере Proxy можно было ограничиться переопределе- нием только метода
__getattr__ (и реализацией конструктора):
>>> class Proxy(object):
>>>

>>>
def __getattr__(self, name):
>>>
return getattr(self._inner_object, name)
Важно! Интерпретатор оптимизирует вызов всех магических ме- тодов: явный вызов x.__
len__ обратится к __getattribute__, неявный
len(x) – нет.
Замечание. Создавать новые методы, присваивая функции объ- екту или классу, некорректно. Присваивать нужно объекты типа types.
MethodType.
3.11. МЕХАНИЗМ СОЗДАНИЯ КЛАССОВ
ПОСЛЕДОВАТЕЛЬНОСТЬ СОЗДАНИЯ КЛАССА
Определение класса приводит к следующим действиям:
– определяется подходящий
метакласс (класс, который созда- ет другие классы);

93
– подготавливается namespace класса;
– выполняется тело класса;
– создается объект класса и присваивается переменной.
>>> class X(object):
>>>
a = 0
>>>
>>># equivalent: type(name, bases, namespace)
>>>X = type(‘X’, (object,), {‘a’: 0})
Метаклассом по умолчанию является
type.
Выполнение тела класса приводит к созданию
словаря всех его атрибутов, который передается в
type. Далее этот словарь доступен через __
dict__ или с помощью built- in функции vars.
Замечание. Изменять, добавлять и удалять атрибуты можно, мо- дифицируя __
dict__. Данный способ менее явный, нежели исполь- зование __
getattr__ и пр.
Важно! Атрибуты классов при наследовании не перезаписыва- ются, а поиск их происходит последовательно в словарях базовых классов.
Вопрос: есть ли разница между реализацией синонима (alias) для функции (функции g и h в примере)?
>>> class X(object):
>>>
def f(self):
>>>
return 0
>>>
>>>
def g(self):
>>>
return self.f()
>>>
>>>
h = f
Обычно реализация синонимов необходима при реализации операторов.
Код в модуле выполняется подобно коду тела класса. Неудиви- тельно, ведь модуль – тоже класс. Значит, в теле класса можно писать любые синтаксически корректные конструкции.
>>> class C(object):
>>>
if sys.version_info.major == 3:
>>>
def f(self):
>>>
return 1

94
>>>
else:
>>>
def g(self):
>>>
return 2
Замечание. В Python 3 порядок объявления атрибутов сохраня- ется (PEP-520)
1
СОЗДАНИЕ ЭКЗЕМПЛЯРА КЛАССА
Создание экземпляра класса заключается в вызове мето- да __
new __ для получения объекта класса и метода __init__ для его инициализации.
>>> class C(object):
>>>
def __new__(cls, name):
>>>
return super().__new__(cls) # make a new
class
>>>
>>>
def __init__(self, name):
>>>
self.name = name
>>>
>>>c = C(‘class’)
Сигнатура метода __
new__ совпадает с сигнатурой __init__.
В методе __
new__ можно возвращать объект другого класса, мо- дифицировать и присваивать атрибуты!
Метод __
init__ не вызывается автоматически, если __new__ воз- вращает объект другого класса.
МЕТАКЛАССЫ
>>> class Meta(type):
>>>
def __new__(mcs, name, bases, attrs, **kwargs):
>>>
# invoked to create class C itself
>>>
return super().__new__(mcs, name, bases, attrs)
>>>
>>>
def __init__(cls, name, bases, attrs,
**kwargs):
1
https://www.python.org/dev/peps/pep-0520

95
>>>
# invoked to init class C itself
>>>
return super().__init__(name, bases, attrs)
>>>
>>>
def __call__(cls):
>>>
# invoked to create an instance of C
>>>
# -> call __new__ and __init__ inside
>>>
# Note: __call__ must share the signature
>>>
# with class’ __new__ and __init__ method
signatures
>>>
return super().__call__()
1 >>> class C(metaclass=Meta):
2 >>> def __new__(cls):
3 >>>
return super().__new__(cls)
4 >>>
5 >>> def __init__(self):
6 >>>
pass
7 >>>
8 >>>c = C()
Строка 1: вызываются методы __
new__ и __init__ метакласса Meta
(создается объект – класс).
Строка 8: вызывается метод __
call__ метакласса Meta, который вызывает методы __
new__ и __init__ класса C.
Методы __
new__ и __init__ метакласса принимают **kwargs – клю- чевые аргументы. Они используются для настройки класса – вызо- ва метода __
prepare__, который возвращает mapping для сохранения атрибутов класса (см. PEP-3115)
1
Примером метакласса в стандартной библиотеке является Enum
2
(с Python 3.4).
Замечание. В Python 3.6 появился метод __init_subclass__
(PEP-487)
3
, позволяющий изменить создание классов наследников
(например, добавить атрибуты).
В классе присутствуют специальный атрибут __
bases__ (кортеж базовых классов) и функция __
subclasses__, возвращающая список подклассов.
1
https
://www.python.org/dev/peps/pep‑3115/
2
https
://docs.python.org/3/library/enum.html
3
https
://www.python.org/dev/peps/pep‑0487

96
АБСТРАКТНЫЕ БАЗОВЫЕ КЛАССЫ
В Python есть возможность создавать условные интерфей- сы и абстрактные классы. Для этого используется метакласс
ABCMeta
(в Python 3.4 – базовый класс
ABC) из модуля abc (PEP-3119)
1
Для объявления абстрактного метода используется декоратор
abstractmethod, абстрактного свой ства – abstractproperty.
В Python иерархия типов введена для чисел – модуль
numbers
PEP-3141 2
, а также коллекций и функционалов – модуль
collections.abc.
ЗАМЕЧАНИЯ О КЛАССАХ
В Python 3.6. метод __
set_name__(self, owner, name) можно переопределить у дескрипторов для получения имени name, под ко- торым дескриптор сохраняется в классе owner.
Замечание. При реализации __getattribute__ в некоторых случа- ях требуется принимать во внимание дескрипторы.
В классах допустимы некоторые атрибуты, характеризующие класс:
– __
slots__ (используется вместо __dict__);
– __
annotations__ (аннотации типов: PEP-318 3
, PEP-481 4
,
PEP-526 5
, PEP-3107 6
);
– __
weakref__ («слабые ссылки»: документация
7
, PEP-205)
8
Замечание. PyCharm IDE имеет возможность подсказывать тип переменных, основываясь на docstrings и коде (https://www.jetbrains.
com/help/pycharm/type- hinting- in- pycharm.html).
3.12. ДЕСКРИПТОРЫ
Свой ства (
property) и декораторы staticmethod и classme-
thod являются дескрипторами – специальными объектами, реализо-
1
https://www.python.org/dev/peps/pep‑3119 2
https://www.python.org/dev/peps/pep‑3141 3
https
://www.python.org/dev/peps/pep‑0318 4
https
://www.python.org/dev/peps/pep‑0481 5
https
://www.python.org/dev/peps/pep‑0526 6
https
://www.python.org/dev/peps/pep‑3107/
7
https
://docs.python.org/3.7/library/weakref.html
8
https
://www.python.org/dev/peps/pep‑0205

97
ванными как атрибуты класса (непосредственного или одного из ро- дителей).
В классах в зависимости от типа дескриптора реализуются ме- тоды:
– __
get__(self, instance, owner) # owner – instance class / type;
– __
set__ (self, instance, value) # self – объект дескриптора;
– __
delete__(self, instance) # instance – объект, в котором вызыва- ется дескриптор.
Стандартное поведение дескрипторов заключается в работе со словарями объекта, класса или базовых классов.
Пример. Вызов
a.x приводит к вызову a.__dict__[‘x’], потом
type(a).__dict__[‘x’] и далее по цепочке наследования.
Пример дескриптора:
>>> class Descriptor(object):
>>>
def __init__(self, label):
>>>
self.label = label
>>>
>>>
def __get__(self, instance, owner):
>>>
return instance.__dict__.get(self.label)
>>>
>>>
def __set__(self, instance, value):
>>>
instance.__dict__[self.label] = value
>>>
>>> class C(object):
>>>
x = Descriptor(‘x’)
>>>
>>>c = C()
>>>c.x = 5
>>> print(c.x)
Методы и свой ства в классе являются дескрипторами. По сути, в каждой функции (неявно) есть метод __
get__:
>>> class Function(object):
>>>
def __get__(self, obj, objtype=None):
>>>
“Simulate func_descr_get() in Objects/
funcobject.c”
>>>
return types.MethodType(self, obj, objtype)
Декораторы
classmethod и staticmethod модифицируют аргу- менты вызова.

Метод – объект- функция, который хранится в словаре атрибутов класса. Доступ же обеспечивается с помощью механизма дескрипто- ров (см. примеры в документации)
1, 2
>>> class D(object):

def f(self, x):

return x

>>> d = D()
>>> D.__dict__[‘f’] # Stored internally as a function

>>> D.f # Get from a class becomes an unbound method

>>> d.f # Get from an instance becomes a bound method
>
ОТВЕТЫ НА ВОПРОСЫ
При создании декораторов- классов для применения
functools.wraps обычно переопределяют метод __new__. Приме- нить напрямую к классу его не удастся. Второй вариант – приме- нить
functools.update_wrapper в методе __init__ ко вновь создан- ному объекту класса.
Что касается синонима и прямого вызова одного метода из друго- го, при наследовании синоним будет хранить ссылку на метод базово- го класса, а прямой вызов будет учитывать наследование.
1
https
://docs.python.org/2/howto/descriptor.html
2
https
://docs.python.org/3.7/howto/descriptor.html

99
Глава
4
ДОПОЛНИТЕЛЬНЫЕ ВОЗМОЖНОСТИ
ЯЗЫКА PYTHON
4.1. МАТЕМАТИЧЕСКАЯ БИБЛИОТЕКА NUMPY
NDARRAY
В библиотеке Numpy реализован класс
ndarray – представ- ление многомерного массива. Он характеризуется данными (data) и информацией о данных:
– тип данных и его размер;
– смещение данных в буфере;
– размерности (shape) и размер в байтах;
– количество элементов для перехода к следующему элементу в измерении (по оси – stride);
– порядок байтов в массиве;
– флаги буфера данных;
– ориентация данных (С-order или Fortran- order).
NDArray соответствует буферу – С-массиву, выровненному по размеру элемента (
itemsize).
К отдельным элементам массива можно обращаться с помощью методов
item/itemset – это быстрее, чем по индексу через __getitem__.
Важно! Все операции с массивом могут быть как с копировани- ем данных, так и без. Некоторые операции возвращают views – новые массивы, которые указывают
на те же данные, но содержат о них иную информацию. При изменении данных в view данные в ориги- нальном массиве меняются.
РАЗМЕРНОСТЬ МАССИВА
Форма массива задается атрибутом
shape – кортеж раз- мерностей.
Изменить размер можно:
– ndarray.reshape() – view, но shape обязан быть compatible с текущим;
– ndarray.resize() – inplace, может понадобиться копирование;
– ndarray.shape – inplace, исключение, если не compatible.

100
Разворачивание массива в 1-D:
– ndarray.ravel – view;
– ndarray.flatten – copy;
– ndarray.flat – итератор по flattened массиву.
Замечание. Допустим 0-D-массив.
ОСИ
Оси – составляющие общей размерности массива.
Важно! Нумерация осей (axes) ведется с нуля и соответствует декартовым координатам. Значение
None в функциях соответству- ет развернутому массиву.
Изменение осей не приводит к копированию данных.
Методы
transpose и swapaxis позволяют изменить порядок сле- дования осей.
Важно! Эти методы могут сделать отображение данных не не- прерывным.
ИНДЕКСАЦИЯ
Numpy поддерживает два вида индексации: простую и «продвинутую».
Простая индексация – один элемент или слайс:
>>> x = numpy.arange(10)
>>> x[1], x[–2], x[3: 7]
В многомерном массиве элементы задаются через запятую:
>>> x = numpy.arange(100).reshape(5, 5, 4)
>>> x[1, 2, 3], x[1, 1:, –1], x[…, 2] == x[:,:, 2]
Замечания. Многоточие «…» – Ellipsis позволяет пропустить не- которые измерения, предполагая, что их нужно взять целиком.
В numpy применяется index broadcasting: если массив по одной из осей имеет длину один, он расширяется до необходимой длины
(например, можно сложить с числом).
ПРОДВИНУТАЯ ИНДЕКСАЦИЯ
Запись x[ind_1, …, ind_n] эквивалентна x[(ind_1, …, ind_n)], но кардинально отличается от x[[ind_1, …, ind_n]].

101
Если в метод __
1   2   3   4   5   6   7   8   9


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