AttributeError: "can't set attribute"
На внутреннем уровне объекты
namedtuple
реализованы как обычные
классы Python. В том, что касается использования оперативной памяти,
они тоже «лучше» обычных классов и так же эффективны с точки зрения
потребления оперативной памяти, как и обычные кортежи.
В Python именованные кортежи неплохо рассматривать как эффективную
с точки зрения потребления оперативной памяти краткую форму для
определения неизменяющегося класса вручную.
4 .6 . Чем полезны именованные кортежи 133
Создание производных от Namedtuple подклассов
Поскольку объекты
namedtuple
строятся поверх обычных классов Python,
вы даже можете добавлять в них методы. Например, подобно любому
другому классу, вы можете расширить класс
namedtuple
и таким образом
добавить в него методы и новые свойства. Приведем пример:
Car = namedtuple('Авто', 'цвет пробег')
class MyCarWithMethods(Car):
def hexcolor(self):
if self.цвет == 'красный':
return '#ff0000'
else:
return '#000000'
Теперь можно создавать объекты
MyCarWithMethods
и, следовательно, вы-
зывать их метод
hexcolor()
:
>>> c = MyCarWithMethods('красный', 1234)
>>> c.hexcolor()
'#ff0000'
Вместе с тем выглядеть это может слегка неуклюжим. По-видимому,
такая возможность пригодится, если вам нужен класс с неизменяемыми
свойствами, но здесь легко и в ногу себе выстрелить.
Например, в добавлении нового неизменяемого поля (immutable field)
есть свои сложности из-за внутренней структуры именованных кортежей.
Самый легкий способ создать иерархии именованных кортежей — ис-
пользовать свойства
_fields
базового кортежа:
>>> Car = namedtuple('Авто', 'цвет пробег')
>>> ElectricCar = namedtuple(
... 'ЭлектрическоеАвто', Car._fields + ('заряд',))
Это дает желаемый результат:
>>> ElectricCar('красный', 1234, 45.0)
ЭлектрическоеАвто(цвет='красный', пробег=1234, заряд=45.0)
134 Глава 4 • Классы и ООП
Встроенные вспомогательные методы
Помимо свойства
_fields
, каждый экземпляр именованного кортежа так-
же предлагает еще несколько вспомогательных методов, которые могли
бы быть вам полезны. Все их имена начинаются с одинарного символа
подчеркивания (
_
), который обычно сигнализирует о том, что метод или
свойство являются «приватными» и не являются частью стабильного
публичного интерфейса класса или модуля.
Правда, в случае с именованными кортежами согласованное правило
именования с символом подчеркивания несет в себе другой смысл. Эти
вспомогательные методы и свойства являются составной частью публич-
ного интерфейса класса
namedtuple
. Вспомогательные методы получают
такие имена, чтобы избежать конфликтов имен с определяемыми пользо-
вателями полями кортежей. Так что можете спокойно ими пользоваться,
если они вам нужны!
Хочу показать вам несколько сценариев, где могли бы пригодиться вспо-
могательные методы именованного кортежа. Давайте начнем со вспомо-
гательного метода
_asdict()
. Он возвращает содержимое именованного
кортежа в виде словаря:
>>> my_car._asdict()
OrderedDict([('цвет', 'красный'), ('пробег', 3812.4)])
Этот метод очень полезен для предотвращения опечаток в именах полей
во время генерирования результата в формате JSON, например:
>>> json.dumps(my_car._asdict(), ensure_ascii=False)
# False для кириллицы
'{"цвет": "красный", "пробег": 3812.4}'
Метод
_replace()
— это еще один полезный вспомогательный метод. Он
создает (мелкую) копию кортежа и позволяет вам выборочно заменять
некоторые его поля:
>>> my_car._replace(цвет='синий')
Авто(цвет='синий', пробег=3812.4)
Наконец, метод класса
_make()
может использоваться для создания новых
экземпляров класса
namedtuple
из (итерируемой) последовательности:
4 .6 . Чем полезны именованные кортежи 135
>>> Car._make(['красный', 999])
Авто(color='красный', пробег=999)
Когда использовать именованные кортежи
Именованные кортежи могут оказаться простым средством для приве-
дения в порядок исходного кода, и они могут сделать код более удобо-
читаемым, обеспечив вашим данным наиболее совершенную структуру.
Например, на моем опыте переход от ситуативных типов данных, таких
как словари с фиксированным форматом, к именованным кортежам по-
могает яснее выражать свои замыслы. Нередко, когда я предпринимаю эту
рефакторизацию, я каким-то невообразимым образом прихожу к более
совершенному решению проблемы, с которой я сталкиваюсь.
Использование именованных кортежей поверх неструктурированных,
а также использование словарей может облегчить жизнь моих коллег,
потому что именованные кортежи позволяют раздавать данные в «само-
документированном» виде (в известной степени).
С другой стороны, я стараюсь не использовать именованные кортежи
ради них самих, если они не помогают мне писать «более чистый» и бо-
лее удобный в сопровождении исходный код. Как и в отношении многих
других методов, приводимых в настоящей книге, иногда может оказаться
слишком много хорошего (что, как известно, тоже плохо).
Тем не менее если именованные кортежи использовать с осторожностью,
они, несомненно, могут сделать ваш программный код Python лучше
и выразительнее.
Ключевые выводы
В языке Python
collection.namedtuple
является эффективной с точки
зрения потребляемой оперативной памяти краткой формой для опре-
деления неизменяющегося класса вручную.
Именованные кортежи помогут почистить ваш исходный код, обес-
печив вашим данным более доступную для понимания структуру.
136 Глава 4 • Классы и ООП
Именованные кортежи обеспечивают несколько полезных вспомо-
гательных методов, которые начинаются с одинарного символа под-
черкивания, но при этом являются составной частью публичного
интерфейса. Вполне нормально их использовать.
4 .7 . Переменные класса против переменных
экземпляра: подводные камни
Помимо проведения различия между методами класса и методами экзем-
пляра, объектная модель Python также проводит различие между пере-
менными класса и переменными экземпляра.
Это различие имеет большое значение. Мне, как начинающему раз-
работчику на Python, оно также доставляло немало хлопот. В течение
длительного времени я не мог найти время, чтобы разобраться в этих по-
нятиях с самых азов. И поэтому мои первые эксперименты с ООП были
пронизаны удивительными линиями поведения и странными ошибками.
В этом разделе мы устраним путаницу относительно этой темы при по-
мощи нескольких практических примеров.
Как я уже сказал, в объектах Python объявляются два вида атрибутов
данных: переменные класса (class variables) и переменные экземпляра
(instance variables).
Do'stlaringiz bilan baham: |