枚举和命名元组有啥区别?
Posted
技术标签:
【中文标题】枚举和命名元组有啥区别?【英文标题】:What's the difference between enum and namedtuple?枚举和命名元组有什么区别? 【发布时间】:2017-03-30 16:30:33 【问题描述】:我想知道 enum 和 namedtuple 之间有什么区别,什么时候应该使用一个而不是另一个。
【问题讨论】:
Op 在询问之前应该先搜索一下。看看这些主题:***.com/questions/2970608/… 和 ***.com/questions/36932/… 【参考方案1】:作为一个类比(尽管不完美),您可以将 python 中的 enum.Enum
和 namedtuple
视为 C 中的 enum
和 struct
。换句话说,enum
s 是一种别名方式值,而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
s,因为同样的基本概念可以通过使用更广为人知的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
s 作为枚举值...class People(enum.Enum): john = Person('John', 21, HairColor.blonde)
看来namedtuple
和Java的enum
很像,不是吗?
那么enum
s 对摆脱幻数有用吗?我很难看到好的用例。在PEP 435 中,它说:“这可以让我们用友好的字符串表示形式的枚举替换标准库中的许多整数常量,而不会放弃向后兼容性。”
@Alex,据我所知,enum
s 对于避免歧义和提高性能很有用(存储int
s 而不是更大的str
s)。我可以用制表符完成可用的枚举值,而不是猜测字符串版本是什么(是骆驼吗?全大写?小写?-> 使用枚举来避免这种情况)。当你在做大数据表时,你可以存储更小的整数,而不是存储 10^8 个字符串 :)【参考方案2】:
namedtuple 是一个 fast 结构,它使用 __slots__ 而不是 __dict__,最终确定您提供的内容在初始化时(实际上变为只读,尽管存在 _replace() 方法)。
当您需要许多(例如数百、数千甚至数百万)相同类型的对象或者您正在读取和/或写入记录时,通常会使用命名元组。
例如,一个经常被引用的例子是一个 Point namedtuple,它可能用于处理一个多边形顶点及其x, y, z
组件。与常规元组相比,namedtuple 引入的开销是最小的。总是按名称指向正确组件的好处 (.x, .y, .z, ...) 而不是按索引 (0, 1, 2, ...) .
阅读诸如 A.x 之类的代码比 A[0] 更容易:意思很明显,即使在您编写代码几个月后,对其他程序员也是如此。
因此,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()
枚举是一种将符号名称耦合到常量值并将它们分类为特定集合的方法。我们通过创建从 Enum 或 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
在 pythonic 开发中 - 枚举元素可能具有指定的特定值 - 可以是唯一的也可以不是唯一的,这取决于您的偏好和规范。 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')
将为 False - 因为比较的右侧元素 - tuple('square') - 不是 Shape 类型,尽管它们具有相同的值。
总而言之,枚举和命名元组是不同的工具。
枚举最近才添加到 Python 中(搜索 PEP435)。如果我没记错的话,namedtuples 已经有很长一段时间了,但我仍然是社区新手,所以我可能错了。 高温
【讨论】:
和枚举对比? @Billy 抱歉,但你在我写我刚刚添加的第二部分时来到这里。 通过使用IntEnum
测试上述内容,我注意到以下比较Color_4.red == 1
的结果为True
。但是,当执行1 in Color_4
时,它会导致False
(只有在执行Color_4.red in Color_4
时才会导致True
)以上是关于枚举和命名元组有啥区别?的主要内容,如果未能解决你的问题,请参考以下文章