Рубрики
Без рубрики

namedtuple – подкласс кортежа с именованными полями

Автор оригинала: Doug Hellmann.

Стандартный tuple использует числовые индексы для доступа к своим членам.

collections_tuple.py

bob  ('Bob', 30, 'male')
print('Representation:', bob)

jane  ('Jane', 29, 'female')
print('\nField by index:', jane[0])

print('\nFields by index:')
for p in [bob, jane]:
    print('{} is a {} year old {}'.format(*p))

Это делает кортежи удобными контейнерами для простого использования.

$ python3 collections_tuple.py

Representation: ('Bob', 30, 'male')

Field by index: Jane

Fields by index:
Bob is a 30 year old male
Jane is a 29 year old female

Напротив, запоминание того, какой индекс следует использовать для каждого значения, может привести к ошибкам, особенно если кортеж имеет много полей и построен далеко от того места, где он используется. namedtuple назначает имена, а также числовой индекс каждому члену.

Определение

Экземпляры namedtuple так же эффективны с точки зрения памяти, как и обычные кортежи, поскольку у них нет словарей для каждого экземпляра. Каждый вид namedtuple представлен своим собственным классом, который создается с помощью фабричной функции namedtuple () . Аргументы – это имя нового класса и строка, содержащая имена элементов.

collections_namedtuple_person.py

import collections

Person  collections.namedtuple('Person', 'name age')

bob  Person(name'Bob', age30)
print('\nRepresentation:', bob)

jane  Person(name'Jane', age29)
print('\nField by name:', jane.name)

print('\nFields by index:')
for p in [bob, jane]:
    print('{} is {} years old'.format(*p))

Как показано в примере, можно получить доступ к полям namedtuple по имени, используя точечную нотацию ( obj.attr ), а также используя позиционные индексы стандартных кортежей.

$ python3 collections_namedtuple_person.py


Representation:,

Field by name: Jane

Fields by index:
Bob is 30 years old
Jane is 29 years old

Как и обычный кортеж , namedtuple неизменяем. Это ограничение позволяет экземплярам tuple иметь согласованное хэш-значение, что позволяет использовать их в качестве ключей в словарях и включать в наборы.

collections_namedtuple_immutable.py

import collections

Person  collections.namedtuple('Person', 'name age')

pat  Person(name'Pat', age12)
print('\nRepresentation:', pat)

pat.age  21

Попытка изменить значение с помощью его именованного атрибута приводит к ошибке AttributeError .

$ python3 collections_namedtuple_immutable.py


Representation:,
Traceback (most recent call last):
  File "collections_namedtuple_immutable.py", line 17, in

    pat.age = 21
AttributeError: can't set attribute

Недействительные имена полей

Имена полей недействительны, если они повторяются или конфликтуют с ключевыми словами Python.

collections_namedtuple_bad_fields.py

import collections

try:
    collections.namedtuple('Person', 'name class age')
except ValueError as err:
    print(err)

try:
    collections.namedtuple('Person', 'name age age')
except ValueError as err:
    print(err)

При анализе имен полей недопустимые значения вызывают исключения ValueError .

$ python3 collections_namedtuple_bad_fields.py

Type names and field names cannot be a keyword: 'class'
Encountered duplicate field name: 'age'

В ситуациях, когда namedtuple создается на основе значений, находящихся вне контроля программы (например, для представления строк, возвращаемых запросом к базе данных, где схема заранее не известна), параметр переименовать должен быть установлен на True , чтобы недопустимые поля были переименованы.

collections_namedtuple_rename.py

import collections

with_class  collections.namedtuple(
    'Person', 'name class age',
    renameTrue)
print(with_class._fields)

two_ages  collections.namedtuple(
    'Person', 'name age age',
    renameTrue)
print(two_ages._fields)

Новые имена для переименованных полей зависят от их индекса в кортеже, поэтому поле с именем class становится _1 , а повторяющееся поле age изменяется. в _2 .

$ python3 collections_namedtuple_rename.py

('name', '_1', 'age')
('name', 'age', '_2')

Особые атрибуты

namedtuple предоставляет несколько полезных атрибутов и методов для работы с подклассами и экземплярами. Все эти встроенные свойства имеют имена с префиксом подчеркивания ( _ ), который по соглашению в большинстве программ Python указывает на частный атрибут. Однако для namedtuple префикс предназначен для защиты имени от столкновения с указанными пользователем именами атрибутов.

Имена полей, переданные в namedtuple для определения нового класса, сохраняются в атрибуте _fields .

collections_namedtuple_fields.py

import collections

Person  collections.namedtuple('Person', 'name age')

bob  Person(name'Bob', age30)
print('Representation:', bob)
print('Fields:', bob._fields)

Хотя аргумент представляет собой одну строку, разделенную пробелами, сохраненное значение представляет собой последовательность отдельных имен.

$ python3 collections_namedtuple_fields.py

Representation:,
Fields: ('name', 'age')

Экземпляры namedtuple можно преобразовать в экземпляры OrderedDict с помощью _asdict () .

collections_namedtuple_asdict.py

import collections

Person  collections.namedtuple('Person', 'name age')

bob  Person(name'Bob', age30)
print('Representation:', bob)
print('As Dictionary:', bob._asdict())

Ключи OrderedDict находятся в том же порядке, что и поля для namedtuple .

$ python3 collections_namedtuple_asdict.py

Representation:,
As Dictionary: OrderedDict([('name', 'Bob'), ('age', 30)])

Метод _replace () создает новый экземпляр, заменяя значения некоторых полей в процессе.

collections_namedtuple_replace.py

import collections

Person  collections.namedtuple('Person', 'name age')

bob  Person(name'Bob', age30)
print('\nBefore:', bob)
bob2  bob._replace(name'Robert')
print('After:', bob2)
print('Same?:', bob is bob2)

Хотя название подразумевает, что он изменяет существующий объект, поскольку экземпляры namedtuple неизменяемы, метод фактически возвращает новый объект.

$ python3 collections_namedtuple_replace.py


Before:,
After:,
Same?: False