В чем разница между перечислением и namedtuple?


Я хотел бы знать, в чем разница между перечислением и namedtuple и когда следует использовать одно над другим.

2 13

2 ответа:

В качестве аналогии (хотя и несовершенной), вы можете думать о enum.Enum и namedtuple в python как о enum и struct В C. другими словами, enums-это способ сглаживания значений, а namedtuple - способ инкапсуляции данных по имени. Эти два типа не являются взаимозаменяемыми, и вы можете использовать enum s в качестве именованных значений в namedtuple.

Я думаю, что этот пример иллюстрирует разницу.
from collections import namedtuple
from enum import Enum

class HairColor(Enum):
    blonde = 1
    brown = 2
    black = 3
    red = 4

Person = namedtuple('Person', ['name','age','hair_color'])
bert = Person('Bert', 5, HairColor.black)

Вы можете получить доступ к именованным "атрибутам" человека так же, как и к обычным объект.

>>> print(bert.name)
Bert
>>> print(bert.age)
5
>>> print(bert.hair_color)
HairColor.black
>>> print(bert.hair_color.value)
3

Вы часто не видите namedtupleтак, потому что та же самая существенная концепция может быть реализована с помощью более широко известного объявления class. Определение class ниже ведет себя почти так же, как определение namedtuple выше.

class Person:
    def __init__(self, name, age, hair_color):
        self.name = name
        self.age = age
        self.hair_color = hair_color
Однако основное различие между объектом namedtuple и объектом class состоит в том, что атрибуты объекта namedtuple не могут быть изменены после его создания.

Namedtuple - это быстрая структура, которая, используя __slots__ вместо __dict__, завершает содержимое, которое вы предоставляете при инициализации (что практически становится доступным только для чтения, хотя метод _replace() существует).

Namedtuple обычно используется, когда вам нужно много (например, сотни, тысячи и даже миллионы) объектов одного типа или Вы читаете и/или записываете запись.
Например, часто приводимый пример-это точка namedtuple, который может использоваться для работы с вершиной полигона с ее компонентами x, y, z.
накладные расходы, вносимые namedtuple над регулярным кортежем, минимальны по сравнению с преимуществом всегда указывать на правильный компонент по имени (.икс, .год, .Зет, ...) вместо по индексу (0, 1, 2, ...).
Чтение кода, такого как A. x, проще, чем A[0]: смысл очевиден даже через несколько месяцев после написания кода и, что еще лучше, для других программистов.

Таким образом, a namedtuple быстр, может использоваться для значимой идентификации содержимого кортежа и, наконец, может сосуществовать с более старым кодом, получающим доступ к содержимому кортежа по индексу.

from collections import namedtuple

Point = namedtuple('Point', 'x y z')  # note the x, y, z fields

origin = Point(0, 0, 0)

A = Point(1, 1, 1)
B = Point(1, 1, 0)
C = Point(1, 0, 0)
D = Point(1, 2, 3)

for p in (origin, A, B, C, D):
    print(p)
    print('x:', p.x, '  y:', p.y, '  z:', p.z)
    print('x:', p[0], '  y:', p[1], '  z:', p[2])
    print()

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

from collections import namedtuple


Point = namedtuple('Point', 'name x y z')  # addition of the field 'name'

origin = Point('O', 0, 0, 0)

A = Point('A', 1, 1, 1)
B = Point('B', 1, 1, 0)
C = Point('C', 1, 0, 0)
D = Point('D', 1, 0, 1)

for p in (origin, A, B, C, D):
    print(p)
    print(p.name)  # more readable than p[0] that is no more the x coordinate
    print('x:', p.x,  '  y:', p.y,  '  z:', p.z)  # unchanged
    print('x:', p[1], '  y:', p[2], '  z:', p[3])  # changed
    print()

Перечисление - это способ связать символические имена с константой значения и классифицировать их как определенный набор. Мы определяем перечисление путем создания класса, производного от перечисление и IntEnum, в зависимости от ценностей, которые мы хотим, чтобы наши константы имели: enum представляет собой универсальный вариант, IntEnum обеспечивает тот факт, что каждая константа будет иметь тип int.

Например, перечисления хороши для определения цветов по имени, определенным целочисленным типам, полу или, опять же, - в более общем смысле - элементам, принадлежащим к определенному типу. набор.

from enum import Enum, IntEnum, unique

class Color_1(Enum):
    red = 'red'
    green = 'green'
    blue = 'blue'

class Color_2(Enum):
    red = (255, 0, 0)
    green = (0, 255, 0)
    blue = (0, 0, 255)

class Color_3(IntEnum):
    red = 0xFF0000
    green = 0xFF00
    blue = 0xFF

class Gender_1(Enum):
    unknown = 'U'
    male = 'M'
    female = 'F'

class Gender_2(Enum):
    unknown = 0.3
    male = 0.5
    female = 0.7

class Shape(Enum):  # Note the different constants types, perfectly legal
    TRIANGLE = 't'
    RECTANGLE = 5
    SQUARE = tuple('square')

class DataType(IntEnum):
    int8 = -8
    int16 = -16
    int32 = -32
    int64 = -64
    int = -2
    negative = -1
    positive = 1
    uint = 2
    uint8 = 8
    uint16 = 16
    uint32 = 32
    uint64 = 64

В питонском развитии - перечислениях элементам может быть присвоено определенное значение-которое может быть либо уникальным, либо нет, в зависимости от ваших предпочтений и спецификации. Декоратор unique используется для обеспечения уникальности значений. По умолчанию можно присвоить одно и то же постоянное значение двум или более различным символьным именам.

class Color_4(IntEnum):
    red = 1
    green = 2
    blue = 3
    RED = 1
    GREEN = 2
    BLUE = 3

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

Например:

Color_4.red == Color_4.RED

Вернет True (тот же класс, то же значение), но следующее:

Shape.SQUARE == tuple('square')

Будет ложным, потому что правый элемент сравнения - кортеж ("квадрат") - не имеет типа Shape, хотя оба они имеют одинаковое значение.

В заключение отметим, что перечисления и безымянные числа - это разные инструменты.

Перечисления были добавлены совсем недавно на Python (поиск PEP435). Если память мне не изменяет правильно, namedtuples были доступны в течение довольно долгого времени, но я все еще новичок в сообществе, поэтому я могу ошибаться. HTH