174 Глава 5 • Общие структуры данных Python
# Получить пробег:
>>> car2[1]
40231.0
# Кортежи неизменяемы:
>>> car2[1] = 12
TypeError:
"'tuple' object does not support item assignment"
# Нет защиты от неверных имен полей
# или отсутствующих/лишних полей:
>>> car3 = (3431.5, 'зеленый', True, 'серебряный')
Написание собственного класса — больше работы,
больше контроля
Классы позволяют определять «шаблоны» многократного использования
для объектов данных, причем эти шаблоны гарантируют, что каждый объ-
ект предоставляет одинаковый набор полей.
Использование обычных классов Python в качестве типов данных
запись
вполне возможно, но это также влечет за собой ручную работу, связанную
с получением удобных функциональных возможностей у других реали-
заций. Например, добавление новых полей в конструктор
__init__
будет
многословным и займет время.
Кроме того, принятое по умолчанию строковое представление объек-
тов-экземпляров, создаваемых на основе собственных классов, не очень
полезно. Чтобы это исправить, вам, вероятно, придется добавить свой
собственный метод
__repr__
1
, который, как правило, довольно многосло-
вен и подлежит обновлению всякий раз, когда вы добавляете новое поле.
Хранящиеся в классах поля могут изменяться, и новые поля могут до-
бавляться свободно, нравится вам это или нет. С помощью декоратора
@property
можно обеспечить себе большее управление и создавать поля
с доступом только для чтения
2
, но это требует написания большего коли-
чества связующего кода.
1
См. раздел «Преобразование строк (каждому классу по __repr__)» главы 4.
2
См. документацию Python «property»:
https://docs .python .org/3/library/functions .html#property
5 .3 . Записи, структуры
и объекты переноса данных 175
Написание собственного класса — отличная возможность, когда в объек-
ты-записи требуется добавить бизнес-логику и
поведение с использовани-
ем методов. Однако это означает, что такие объекты технически больше
не являются простыми объектами данных.
class Car:
def __init__(self, color, mileage, automatic):
self.color = color
self.mileage = mileage
self.automatic =
automatic
>>> car1 = Car('красный', 3812.4, True)
>>> car2 = Car('синий', 40231.0, False)
# Получить пробег:
>>> car2.mileage
40231.0
# Классы изменяемы:
>>> car2.mileage = 12
>>> car2.windshield = 'треснутое'
# Строковое представление не очень полезно
# (приходится добавлять написанный вручную метод __repr__):
>>> car1
collections .namedtuple — удобные объекты данных
Класс
namedtuple
, доступный в Python 2.6+, предоставляет расширение
встроенного типа данных
tuple
1
. Аналогично определению собственного
класса, применение именованного кортежа
namedtuple
позволяет опре-
делять «шаблоны» многократного использования для своих записей,
гарантирующие использование правильных имен полей.
Именованные кортежи неизменяемы, как и обычные кортежи. Это означа-
ет, что вы не можете добавлять новые поля или изменять существующие
поля после того, как экземпляр
namedtuple
был создан.
1
См. раздел «Чем полезны именованные кортежи» главы 4.
176 Глава 5 • Общие структуры данных Python
Помимо этого, именованные кортежи являются, скажем так, именован-
ными кортежами (named tuples). Доступ к каждому хранящемуся в них
объекту можно получить по уникальному идентификатору. Это освобож-
дает от необходимости запоминать целочисленные индексы или идти
обходными методами, например определять индексы целочисленных
констант в качестве мнемокодов.
На внутреннем уровне объекты
namedtuple
реализованы как обычные
классы Python. В том, что касается использования оперативной памяти,
они тоже «лучше» обычных классов и столь же эффективны с точки зре-
ния потребляемой
оперативной памяти, что и обычные кортежи:
>>> from collections
import namedtuple
>>>
from sys import getsizeof
>>> p1 = namedtuple('Point', 'x y z')(1, 2, 3)
>>> p2 = (1, 2, 3)
>>> getsizeof(p1)
72
>>> getsizeof(p2)
72
Именованные кортежи могут довольно просто привести в порядок ис-
ходный код и сделать его более удобочитаемым, обеспечив вашим данным
более совершенную структуру.
По моему опыту, переход от ситуативных типов данных, таких как словари
с фиксированным форматом, к именованным кортежам помогает яснее
выражать свои намерения. Нередко, когда я применяю эту рефакториза-
цию, каким-то невообразимым образом я прихожу к более совершенному
решению проблемы, с которой сталкиваюсь.
Использование именованных кортежей вместо неструктурированных
кортежей и словарей может облегчить жизнь и моим коллегам, потому
что именованные кортежи позволяют раздавать данные в «самодокумен-
тированном» виде (в известной степени).
>>> from collections import namedtuple
>>> Car = namedtuple('Авто' , 'цвет пробег автомат')
5 .3 . Записи, структуры и объекты переноса данных
177
>>> car1 = Car('красный', 3812.4, True)
Do'stlaringiz bilan baham: