- Timestamp:
- Mar 4, 2024, 6:09:26 PM (11 months ago)
- Branches:
- master
- Children:
- 44adf1b
- Parents:
- 647e2ea
- File:
-
- 1 edited
Legend:
- Unmodified
- Added
- Removed
-
doc/theses/jiada_liang_MMath/relatedwork.tex
r647e2ea r9262fe9 2 2 \label{s:RelatedWork} 3 3 4 Enumeration types exist in many popular programming languages, both past and present, \eg Pascal~\cite{Pascal}, Ada~\cite{Ada}, \Csharp~\cite{Csharp}, \CC, Go~\cite{Go}, Java~\cite{Java}, Modula-3~\cite{Modula-3}, Rust~\cite{Rust}, Swift~\cite{Swift}, Python~\cite{Python}, and algebraic data-types in functional programming. 4 In general, an \Newterm{algebraic data type} (ADT) is a composite type, \ie, a type formed by combining other types. 5 Three common classes of algebraic types are \Newterm{array type}, \ie homogeneous types, \Newterm{product type}, \ie heterogeneous tuples and records (structures), and \Newterm{sum type}, \ie tagged product-types (unions). 6 Enumerated types are a special case of product/sum types with non-mutable fields, \ie initialized (constructed) once at the type's declaration, possible restricted to compile-time initialization. 7 Values of algebraic types are access by subscripting, field qualification, or type (pattern) matching. 8 9 Enumeration types exist in many popular programming languages, both past and present, \eg Pascal~\cite{Pascal}, Ada~\cite{Ada}, \Csharp~\cite{Csharp}, Haskell~\cite{Haskell} \CC, Go~\cite{Go}, Java~\cite{Java}, Modula-3~\cite{Modula-3}, Rust~\cite{Rust}, Swift~\cite{Swift}, Python~\cite{Python}. 5 10 Among theses languages, there are a large set of overlapping features, but each language has its own unique extensions and restrictions. 6 7 11 8 12 \section{Pascal} … … 714 718 Via mem::discriminant 715 719 \end{rust} 716 mem::discriminant returns an opaque reference to the discriminant of an enum value which can be compared. This cannot be used to get the value of the discriminant. 717 Casting 720 @mem::discriminant@ returns an opaque reference to the discriminant of an enum value which can be compared. This cannot be used to get the value of the discriminant. 721 722 \subsection{Casting} 718 723 719 724 If an enumeration is unit-only (with no tuple and struct variants), then its discriminant can be directly accessed with a numeric cast; e.g.: … … 1185 1190 1186 1191 \section{Python} 1192 \lstnewenvironment{python}[1][]{\lstset{language=Python,escapechar=\$,moredelim=**[is][\color{red}]{@}{@},}\lstset{#1}}{} 1193 1194 An @Enum@ is a set of symbolic names bound to unique values. 1195 They are similar to global variables, but they offer a more useful @repr()@, grouping, type-safety, and a few other features. 1196 1197 They are most useful when you have a variable that can take one of a limited selection of values. For example, the days of the week: 1198 \begin{python} 1199 >>> from enum import Enum 1200 >>> class Weekday(Enum): 1201 ... MONDAY = 1 1202 ... TUESDAY = 2 1203 ... WEDNESDAY = 3 1204 ... THURSDAY = 4 1205 ... FRIDAY = 5 1206 ... SATURDAY = 6 1207 ... SUNDAY = 7 1208 \end{python} 1209 Or perhaps the RGB primary colors: 1210 \begin{python} 1211 >>> from enum import Enum 1212 >>> class Color(Enum): 1213 ... RED = 1 1214 ... GREEN = 2 1215 ... BLUE = 3 1216 \end{python} 1217 As you can see, creating an @Enum@ is as simple as writing a class that inherits from @Enum@ itself. 1218 1219 Note: Case of Enum Members 1220 1221 Because Enums are used to represent constants, and to help avoid issues with name clashes between mixin-class methods/attributes and enum names, we strongly recommend using @UPPER_CASE@ names for members, and will be using that style in our examples. 1222 1223 Depending on the nature of the enum a member's value may or may not be important, but either way that value can be used to get the corresponding member: 1224 \begin{python} 1225 >>> Weekday(3) 1226 <Weekday.WEDNESDAY: 3> 1227 \end{python} 1228 As you can see, the @repr()@ of a member shows the enum name, the member name, and the value. 1229 The @str()@ of a member shows only the enum name and member name: 1230 \begin{python} 1231 print(Weekday.THURSDAY) 1232 Weekday.THURSDAY 1233 \end{python} 1234 The type of an enumeration member is the enum it belongs to: 1235 \begin{python} 1236 >>> type(Weekday.MONDAY) 1237 <enum 'Weekday'> 1238 isinstance(Weekday.FRIDAY, Weekday) 1239 True 1240 \end{python} 1241 Enum members have an attribute that contains just their name: 1242 \begin{python} 1243 >>> print(Weekday.TUESDAY.name) 1244 TUESDAY 1245 \end{python} 1246 Likewise, they have an attribute for their value: 1247 \begin{python} 1248 >>> Weekday.WEDNESDAY.value 1249 3 1250 \end{python} 1251 Unlike many languages that treat enumerations solely as name/value pairs, Python @Enum@s can have behavior added. 1252 For example, @datetime.date@ has two methods for returning the weekday: @weekday()@ and @isoweekday()@. 1253 The difference is that one of them counts from 0-6 and the other from 1-7. 1254 Rather than keep track of that ourselves we can add a method to the @Weekday@ enum to extract the day from the date instance and return the matching enum member: 1255 \begin{python} 1256 $@$classmethod 1257 def from_date(cls, date): 1258 return cls(date.isoweekday()) 1259 \end{python} 1260 The complete Weekday enum now looks like this: 1261 \begin{python} 1262 >>> class Weekday(Enum): 1263 ... MONDAY = 1 1264 ... TUESDAY = 2 1265 ... WEDNESDAY = 3 1266 ... THURSDAY = 4 1267 ... FRIDAY = 5 1268 ... SATURDAY = 6 1269 ... SUNDAY = 7 1270 ... # 1271 ... $@$classmethod 1272 ... def from_date(cls, date): 1273 ... return cls(date.isoweekday()) 1274 \end{python} 1275 Now we can find out what today is! Observe: 1276 \begin{python} 1277 >>> from datetime import date 1278 >>> Weekday.from_date(date.today()) 1279 <Weekday.TUESDAY: 2> 1280 \end{python} 1281 Of course, if you're reading this on some other day, you'll see that day instead. 1282 1283 This Weekday enum is great if our variable only needs one day, but what if we need several? Maybe we're writing a function to plot chores during a week, and don't want to use a @list@ -- we could use a different type of @Enum@: 1284 \begin{python} 1285 >>> from enum import Flag 1286 >>> class Weekday(Flag): 1287 ... MONDAY = 1 1288 ... TUESDAY = 2 1289 ... WEDNESDAY = 4 1290 ... THURSDAY = 8 1291 ... FRIDAY = 16 1292 ... SATURDAY = 32 1293 ... SUNDAY = 64 1294 \end{python} 1295 We've changed two things: we're inherited from @Flag@, and the values are all powers of 2. 1296 1297 Just like the original @Weekday@ enum above, we can have a single selection: 1298 \begin{python} 1299 >>> first_week_day = Weekday.MONDAY 1300 >>> first_week_day 1301 <Weekday.MONDAY: 1> 1302 \end{python} 1303 But @Flag@ also allows us to combine several members into a single variable: 1304 \begin{python} 1305 >>> weekend = Weekday.SATURDAY | Weekday.SUNDAY 1306 >>> weekend 1307 <Weekday.SATURDAY|SUNDAY: 96> 1308 \end{python} 1309 You can even iterate over a @Flag@ variable: 1310 \begin{python} 1311 >>> for day in weekend: 1312 ... print(day) 1313 Weekday.SATURDAY 1314 Weekday.SUNDAY 1315 \end{python} 1316 Okay, let's get some chores set up: 1317 \begin{python} 1318 >>> chores_for_ethan = { 1319 ... 'feed the cat': Weekday.MONDAY | Weekday.WEDNESDAY | Weekday.FRIDAY, 1320 ... 'do the dishes': Weekday.TUESDAY | Weekday.THURSDAY, 1321 ... 'answer SO questions': Weekday.SATURDAY, 1322 ... } 1323 \end{python} 1324 And a function to display the chores for a given day: 1325 \begin{python} 1326 >>> def show_chores(chores, day): 1327 ... for chore, days in chores.items(): 1328 ... if day in days: 1329 ... print(chore) 1330 >>> show_chores(chores_for_ethan, Weekday.SATURDAY) 1331 answer SO questions 1332 \end{python} 1333 In cases where the actual values of the members do not matter, you can save yourself some work and use @auto()@ for the values: 1334 \begin{python} 1335 >>> from enum import auto 1336 >>> class Weekday(Flag): 1337 ... MONDAY = auto() 1338 ... TUESDAY = auto() 1339 ... WEDNESDAY = auto() 1340 ... THURSDAY = auto() 1341 ... FRIDAY = auto() 1342 ... SATURDAY = auto() 1343 ... SUNDAY = auto() 1344 ... WEEKEND = SATURDAY | SUNDAY 1345 \end{python} 1346 1347 \subsection{Programmatic access to enumeration members and their attributes} 1348 1349 Sometimes it's useful to access members in enumerations programmatically (i.e. situations where @Color.RED@ won't do because the exact color is not known at program-writing time). 1350 @Enum@ allows such access: 1351 \begin{python} 1352 >>> Color(1) 1353 <Color.RED: 1> 1354 >>> Color(3) 1355 <Color.BLUE: 3> 1356 \end{python} 1357 If you want to access enum members by name, use item access: 1358 \begin{python} 1359 Color['RED'] 1360 <Color.RED: 1> 1361 1362 Color['GREEN'] 1363 <Color.GREEN: 2> 1364 \end{python} 1365 If you have an enum member and need its name or value: 1366 \begin{python} 1367 >>> member = Color.RED 1368 >>> member.name 1369 'RED' 1370 >>> member.value 1371 1 1372 \end{python} 1373 1374 \subsection{Duplicating enum members and values} 1375 1376 Having two enum members with the same name is invalid: 1377 \begin{python} 1378 >>> class Shape(Enum): 1379 ... SQUARE = 2 1380 ... SQUARE = 3 1381 ... 1382 Traceback (most recent call last): 1383 ... 1384 TypeError: 'SQUARE' already defined as 2 1385 \end{python} 1386 However, an enum member can have other names associated with it. 1387 Given two entries @A@ and @B@ with the same value (and @A@ defined first), @B@ is an alias for the member @A@. 1388 By-value lookup of the value of @A@ will return the member @A@. 1389 By-name lookup of @A@ will return the member @A@. 1390 By-name lookup of @B@ will also return the member @A@: 1391 \begin{python} 1392 >>> class Shape(Enum): 1393 ... SQUARE = 2 1394 ... DIAMOND = 1 1395 ... CIRCLE = 3 1396 ... ALIAS_FOR_SQUARE = 2 1397 ... 1398 >>> Shape.SQUARE 1399 <Shape.SQUARE: 2> 1400 >>> Shape.ALIAS_FOR_SQUARE 1401 <Shape.SQUARE: 2> 1402 >>> Shape(2) 1403 <Shape.SQUARE: 2> 1404 \end{python} 1405 1406 Note: Attempting to create a member with the same name as an already defined attribute (another member, a method, etc.) or attempting to create an attribute with the same name as a member is not allowed. 1407 1408 \subsection{Ensuring unique enumeration values} 1409 1410 By default, enumerations allow multiple names as aliases for the same value. 1411 When this behavior isn't desired, you can use the @unique()@ decorator: 1412 \begin{python} 1413 >>> from enum import Enum, unique 1414 >>> $@$unique 1415 ... class Mistake(Enum): 1416 ... ONE = 1 1417 ... TWO = 2 1418 ... THREE = 3 1419 ... FOUR = 3 1420 ... 1421 Traceback (most recent call last): 1422 ... 1423 ValueError: duplicate values found in <enum 'Mistake'>: FOUR -> THREE 1424 \end{python} 1425 1426 \subsection{Using automatic values} 1427 1428 If the exact value is unimportant you can use @auto@: 1429 \begin{python} 1430 >>> from enum import Enum, auto 1431 >>> class Color(Enum): 1432 ... RED = auto() 1433 ... BLUE = auto() 1434 ... GREEN = auto() 1435 ... 1436 >>> [member.value for member in Color] 1437 [1, 2, 3] 1438 \end{python} 1439 The values are chosen by \_generate\_next\_value\_(), which can be overridden: 1440 \begin{python} 1441 >>> class AutoName(Enum): 1442 ... $@$staticmethod 1443 ... def _generate_next_value_(name, start, count, last_values): 1444 ... return name 1445 ... 1446 >>> class Ordinal(AutoName): 1447 ... NORTH = auto() 1448 ... SOUTH = auto() 1449 ... EAST = auto() 1450 ... WEST = auto() 1451 ... 1452 >>> [member.value for member in Ordinal] 1453 ['NORTH', 'SOUTH', 'EAST', 'WEST'] 1454 \end{python} 1455 Note The @_generate_next_value_()@ method must be defined before any members. 1456 1457 \subsection{Iteration} 1458 1459 Iterating over the members of an enum does not provide the aliases: 1460 \begin{python} 1461 >>> list(Shape) 1462 [<Shape.SQUARE: 2>, <Shape.DIAMOND: 1>, <Shape.CIRCLE: 3>] 1463 >>> list(Weekday) 1464 [<Weekday.MONDAY: 1>, <Weekday.TUESDAY: 2>, <Weekday.WEDNESDAY: 4>, <Weekday.THURSDAY: 8>, 1465 <Weekday.FRIDAY: 16>, <Weekday.SATURDAY: 32>, <Weekday.SUNDAY: 64>] 1466 \end{python} 1467 Note that the aliases @Shape.ALIAS_FOR_SQUARE@ and @Weekday.WEEKEND@ aren't shown. 1468 1469 The special attribute @__members__@ is a read-only ordered mapping of names to members. 1470 It includes all names defined in the enumeration, including the aliases: 1471 \begin{python} 1472 >>> for name, member in Shape.__members__.items(): 1473 ... name, member 1474 ... 1475 ('SQUARE', <Shape.SQUARE: 2>) 1476 ('DIAMOND', <Shape.DIAMOND: 1>) 1477 ('CIRCLE', <Shape.CIRCLE: 3>) 1478 ('ALIAS_FOR_SQUARE', <Shape.SQUARE: 2>) 1479 \end{python} 1480 The @__members__@ attribute can be used for detailed programmatic access to the enumeration members. 1481 For example, finding all the aliases: 1482 \begin{python} 1483 >>> [name for name, member in Shape.__members__.items() if member.name != name] 1484 ['ALIAS_FOR_SQUARE'] 1485 \end{python} 1486 Note: Aliases for flags include values with multiple flags set, such as 3, and no flags set, i.e. 0. 1487 1488 \subsection{Comparisons} 1489 1490 Enumeration members are compared by identity: 1491 \begin{python} 1492 >>> Color.RED is Color.RED 1493 True 1494 >>> Color.RED is Color.BLUE 1495 False 1496 >>> Color.RED is not Color.BLUE 1497 True 1498 \end{python} 1499 Ordered comparisons between enumeration values are not supported. 1500 Enum members are not integers (but see @IntEnum@ below): 1501 \begin{python} 1502 >>> Color.RED < Color.BLUE 1503 Traceback (most recent call last): 1504 File "<stdin>", line 1, in <module> 1505 TypeError: '<' not supported between instances of 'Color' and 'Color' 1506 \end{python} 1507 Equality comparisons are defined though: 1508 \begin{python} 1509 >>> Color.BLUE == Color.RED 1510 False 1511 >>> Color.BLUE != Color.RED 1512 True 1513 >>> Color.BLUE == Color.BLUE 1514 True 1515 \end{python} 1516 Comparisons against non-enumeration values will always compare not equal (again, @IntEnum@ was explicitly designed to behave differently, see below): 1517 \begin{python} 1518 >>> Color.BLUE == 2 1519 False 1520 \end{python} 1521 1522 Warning: It is possible to reload modules -- if a reloaded module contains enums, they will be recreated, and the new members may not compare identical/equal to the original members. 1523 1524 \subsection{Allowed members and attributes of enumerations} 1525 1526 Most of the examples above use integers for enumeration values. 1527 Using integers is short and handy (and provided by default by the Functional API), but not strictly enforced. 1528 In the vast majority of use-cases, one doesn't care what the actual value of an enumeration is. 1529 But if the value is important, enumerations can have arbitrary values. 1530 1531 Enumerations are Python classes, and can have methods and special methods as usual. If we have this enumeration: 1532 \begin{python} 1533 >>> class Mood(Enum): 1534 ... FUNKY = 1 1535 ... HAPPY = 3 1536 ... 1537 ... def describe(self): 1538 ... # self is the member here 1539 ... return self.name, self.value 1540 ... 1541 ... def __str__(self): 1542 ... return 'my custom str! {0}'.format(self.value) 1543 ... 1544 ... $@$classmethod 1545 ... 1546 ... def favorite_mood(cls): 1547 ... # cls here is the enumeration 1548 ... return cls.HAPPY 1549 ... 1550 \end{python} 1551 Then: 1552 \begin{python} 1553 >>> Mood.favorite_mood() 1554 <Mood.HAPPY: 3> 1555 >>> Mood.HAPPY.describe() 1556 ('HAPPY', 3) 1557 >>> str(Mood.FUNKY) 1558 'my custom str! 1' 1559 \end{python} 1560 The rules for what is allowed are as follows: names that start and end with a single underscore are reserved by enum and cannot be used; 1561 all other attributes defined within an enumeration will become members of this enumeration, with the exception of special methods (@__str__()@, @__add__()@, etc.), descriptors (methods are also descriptors), and variable names listed in @_ignore_@. 1562 1563 Note: if your enumeration defines @__new__()@ and/or @__init__()@, any value(s) given to the enum member will be passed into those methods. 1564 See Planet for an example. 1565 1566 Note: The @__new__()@ method, if defined, is used during creation of the Enum members; 1567 it is then replaced by Enum's @__new__()@ which is used after class creation for lookup of existing members. 1568 See When to use @__new__()@ vs. @__init__()@ for more details. 1569 1570 \subsection{Restricted Enum subclassing} 1571 1572 A new @Enum@ class must have one base enum class, up to one concrete data type, and as many object-based mixin classes as needed. 1573 The order of these base classes is: 1574 \begin{python} 1575 class EnumName([mix-in, ...,] [data-type,] base-enum): 1576 pass 1577 \end{python} 1578 Also, subclassing an enumeration is allowed only if the enumeration does not define any members. 1579 So this is forbidden: 1580 \begin{python} 1581 >>> class MoreColor(Color): 1582 ... PINK = 17 1583 ... 1584 Traceback (most recent call last): 1585 ... 1586 TypeError: <enum 'MoreColor'> cannot extend <enum 'Color'> 1587 \end{python} 1588 But this is allowed: 1589 \begin{python} 1590 >>> class Foo(Enum): 1591 ... def some_behavior(self): 1592 ... pass 1593 ... 1594 >>> class Bar(Foo): 1595 ... HAPPY = 1 1596 ... SAD = 2 1597 ... 1598 \end{python} 1599 Allowing subclassing of enums that define members would lead to a violation of some important invariants of types and instances. 1600 On the other hand, it makes sense to allow sharing some common behavior between a group of enumerations. (See OrderedEnum for an example.) 1601 1602 \subsection{Dataclass support} 1603 1604 When inheriting from a @dataclass@, the @__repr__()@ omits the inherited class' name. 1605 For example: 1606 \begin{python} 1607 >>> from dataclasses import dataclass, field 1608 >>> $@$dataclass 1609 ... class CreatureDataMixin: 1610 ... size: str 1611 ... legs: int 1612 ... tail: bool = field(repr=False, default=True) 1613 ... 1614 >>> class Creature(CreatureDataMixin, Enum): 1615 ... BEETLE = 'small', 6 1616 ... DOG = 'medium', 4 1617 ... 1618 >>> Creature.DOG 1619 <Creature.DOG: size='medium', legs=4> 1620 \end{python} 1621 Use the @dataclass()@ argument repr=False to use the standard @repr()@. 1622 1623 Changed in version 3.12: Only the dataclass fields are shown in the value area, not the dataclass' name. 1624 1625 \subsection{Pickling} 1626 1627 Enumerations can be pickled and unpickled: 1628 \begin{python} 1629 >>> from test.test_enum import Fruit 1630 >>> from pickle import dumps, loads 1631 >>> Fruit.TOMATO is loads(dumps(Fruit.TOMATO)) 1632 True 1633 \end{python} 1634 The usual restrictions for pickling apply: picklable enums must be defined in the top level of a module, since unpickling requires them to be importable from that module. 1635 1636 Note: With pickle protocol version 4 it is possible to easily pickle enums nested in other classes. 1637 1638 It is possible to modify how enum members are pickled/unpickled by defining @__reduce_ex__()@ in the enumeration class. 1639 The default method is by-value, but enums with complicated values may want to use by-name: 1640 \begin{python} 1641 >>> import enum 1642 >>> class MyEnum(enum.Enum): 1643 ... __reduce_ex__ = enum.pickle_by_enum_name 1644 \end{python} 1645 Note: Using by-name for flags is not recommended, as unnamed aliases will not unpickle. 1646 1647 \subsection{Functional API} 1648 1649 The @Enum@ class is callable, providing the following functional API: 1650 \begin{python} 1651 >>> Animal = Enum('Animal', 'ANT BEE CAT DOG') 1652 >>> Animal 1653 <enum 'Animal'> 1654 >>> Animal.ANT 1655 <Animal.ANT: 1> 1656 >>> list(Animal) 1657 [<Animal.ANT: 1>, <Animal.BEE: 2>, <Animal.CAT: 3>, <Animal.DOG: 4>] 1658 \end{python} 1659 The semantics of this API resemble @namedtuple@. 1660 The first argument of the call to @Enum@ is the name of the enumeration. 1661 1662 The second argument is the source of enumeration member names. 1663 It can be a whitespace-separated string of names, a sequence of names, a sequence of 2-tuples with key/value pairs, or a mapping (e.g. dictionary) of names to values. 1664 The last two options enable assigning arbitrary values to enumerations; 1665 the others auto-assign increasing integers starting with 1 (use the @start@ parameter to specify a different starting value). 1666 A new class derived from @Enum@ is returned. 1667 In other words, the above assignment to Animal is equivalent to: 1668 \begin{python} 1669 >>> class Animal(Enum): 1670 ... ANT = 1 1671 ... BEE = 2 1672 ... CAT = 3 1673 ... DOG = 4 1674 ... 1675 \end{python} 1676 The reason for defaulting to 1 as the starting number and not 0 is that 0 is @False@ in a boolean sense, but by default enum members all evaluate to @True@. 1677 1678 Pickling enums created with the functional API can be tricky as frame stack implementation details are used to try and figure out which module the enumeration is being created in (e.g. it will fail if you use a utility function in a separate module, and also may not work on IronPython or Jython). 1679 The solution is to specify the module name explicitly as follows: 1680 \begin{python} 1681 >>> Animal = Enum('Animal', 'ANT BEE CAT DOG', module=__name__) 1682 \end{python} 1683 Warning: If module is not supplied, and @Enum@ cannot determine what it is, the new @Enum@ members will not be unpicklable; to keep errors closer to the source, pickling will be disabled. 1684 1685 The new pickle protocol 4 also, in some circumstances, relies on @__qualname__@ being set to the location where pickle will be able to find the class. 1686 For example, if the class was made available in class SomeData in the global scope: 1687 \begin{python} 1688 >>> Animal = Enum('Animal', 'ANT BEE CAT DOG', qualname='SomeData.Animal') 1689 \end{python} 1690 The complete signature is: 1691 \begin{python} 1692 Enum( 1693 value='NewEnumName', 1694 names=<...>, 1695 *, 1696 module='...', 1697 qualname='...', 1698 type=<mixed-in class>, 1699 start=1, 1700 ) 1701 \end{python} 1702 \begin{itemize} 1703 \item 1704 @value@: What the new enum class will record as its name. 1705 \item 1706 @names@: The enum members. 1707 This can be a whitespace- or comma-separated string (values will start at 1 unless otherwise specified): 1708 \begin{python} 1709 'RED GREEN BLUE' | 'RED,GREEN,BLUE' | 'RED, GREEN, BLUE' 1710 \end{python} 1711 or an iterator of names: 1712 \begin{python} 1713 ['RED', 'GREEN', 'BLUE'] 1714 \end{python} 1715 or an iterator of (name, value) pairs: 1716 \begin{python} 1717 [('CYAN', 4), ('MAGENTA', 5), ('YELLOW', 6)] 1718 \end{python} 1719 or a mapping: 1720 \begin{python} 1721 {'CHARTREUSE': 7, 'SEA_GREEN': 11, 'ROSEMARY': 42} 1722 \end{python} 1723 \item 1724 module: name of module where new enum class can be found. 1725 \item 1726 @qualname@: where in module new enum class can be found. 1727 \item 1728 @type@: type to mix in to new enum class. 1729 \item 1730 @start@: number to start counting at if only names are passed in. 1731 \end{itemize} 1732 Changed in version 3.5: The start parameter was added. 1733 1734 \subsection{Derived Enumerations} 1735 1736 \subsection{IntEnum} 1737 1738 The first variation of @Enum@ that is provided is also a subclass of @int@. 1739 Members of an @IntEnum@ can be compared to integers; 1740 by extension, integer enumerations of different types can also be compared to each other: 1741 \begin{python} 1742 >>> from enum import IntEnum 1743 >>> class Shape(IntEnum): 1744 ... CIRCLE = 1 1745 ... SQUARE = 2 1746 ... 1747 >>> class Request(IntEnum): 1748 ... POST = 1 1749 ... GET = 2 1750 ... 1751 >>> Shape == 1 1752 False 1753 >>> Shape.CIRCLE == 1 1754 True 1755 >>> Shape.CIRCLE == Request.POST 1756 True 1757 \end{python} 1758 However, they still can't be compared to standard @Enum@ enumerations: 1759 \begin{python} 1760 >>> class Shape(IntEnum): 1761 ... CIRCLE = 1 1762 ... SQUARE = 2 1763 ... 1764 >>> class Color(Enum): 1765 ... RED = 1 1766 ... GREEN = 2 1767 ... 1768 >>> Shape.CIRCLE == Color.RED 1769 False 1770 \end{python} 1771 @IntEnum@ values behave like integers in other ways you'd expect: 1772 \begin{python} 1773 >>> int(Shape.CIRCLE) 1774 1 1775 >>> ['a', 'b', 'c'][Shape.CIRCLE] 1776 'b' 1777 >>> [i for i in range(Shape.SQUARE)] 1778 [0, 1] 1779 \end{python} 1780 1781 \subsection{StrEnum} 1782 1783 The second variation of @Enum@ that is provided is also a subclass of @str@. 1784 Members of a @StrEnum@ can be compared to strings; 1785 by extension, string enumerations of different types can also be compared to each other. 1786 1787 New in version 3.11. 1788 1789 \subsection{IntFlag} 1790 1791 The next variation of @Enum@ provided, @IntFlag@, is also based on @int@. 1792 The difference being @IntFlag@ members can be combined using the bitwise operators (@&, |, ^, ~@) and the result is still an @IntFlag@ member, if possible. 1793 Like @IntEnum@, @IntFlag@ members are also integers and can be used wherever an int is used. 1794 1795 Note: Any operation on an IntFlag member besides the bit-wise operations will lose the @IntFlag@ membership. 1796 1797 Bit-wise operations that result in invalid @IntFlag@ values will lose the @IntFlag@ membership. 1798 See @FlagBoundary@ for details. 1799 1800 New in version 3.6. 1801 1802 Changed in version 3.11. 1803 1804 Sample @IntFlag@ class: 1805 \begin{python} 1806 >>> from enum import IntFlag 1807 >>> class Perm(IntFlag): 1808 ... R = 4 1809 ... W = 2 1810 ... X = 1 1811 ... 1812 >>> Perm.R | Perm.W 1813 <Perm.R|W: 6> 1814 >>> Perm.R + Perm.W 1815 6 1816 >>> RW = Perm.R | Perm.W 1817 >>> Perm.R in RW 1818 True 1819 \end{python} 1820 It is also possible to name the combinations: 1821 \begin{python} 1822 >>> class Perm(IntFlag): 1823 ... R = 4 1824 ... W = 2 1825 ... X = 1 1826 ... RWX = 7 1827 ... 1828 >>> Perm.RWX 1829 <Perm.RWX: 7> 1830 >>> ~Perm.RWX 1831 <Perm: 0> 1832 >>> Perm(7) 1833 <Perm.RWX: 7> 1834 \end{python} 1835 Note: Named combinations are considered aliases. Aliases do not show up during iteration, but can be returned from by-value lookups. 1836 1837 Changed in version 3.11. 1838 1839 Another important difference between @IntFlag@ and @Enum@ is that if no flags are set (the value is 0), its boolean evaluation is @False@: 1840 \begin{python} 1841 >>> Perm.R & Perm.X 1842 <Perm: 0> 1843 >>> bool(Perm.R & Perm.X) 1844 False 1845 \end{python} 1846 Because @IntFlag@ members are also subclasses of int they can be combined with them (but may lose @IntFlag@ membership: 1847 \begin{python} 1848 >>> Perm.X | 4 1849 <Perm.R|X: 5> 1850 1851 >>> Perm.X + 8 1852 9 1853 \end{python} 1854 Note: The negation operator, @~@, always returns an @IntFlag@ member with a positive value: 1855 \begin{python} 1856 >>> (~Perm.X).value == (Perm.R|Perm.W).value == 6 1857 True 1858 \end{python} 1859 @IntFlag@ members can also be iterated over: 1860 \begin{python} 1861 >>> list(RW) 1862 [<Perm.R: 4>, <Perm.W: 2>] 1863 \end{python} 1864 New in version 3.11. 1865 1866 \subsection{Flag} 1867 1868 The last variation is @Flag@. 1869 Like @IntFlag@, @Flag@ members can be combined using the bitwise operators (@&, |, ^, ~@). 1870 Unlike @IntFlag@, they cannot be combined with, nor compared against, any other @Flag@ enumeration, nor @int@. 1871 While it is possible to specify the values directly it is recommended to use @auto@ as the value and let @Flag@ select an appropriate value. 1872 1873 New in version 3.6. 1874 1875 Like @IntFlag@, if a combination of @Flag@ members results in no flags being set, the boolean evaluation is @False@: 1876 \begin{python} 1877 >>> from enum import Flag, auto 1878 >>> class Color(Flag): 1879 ... RED = auto() 1880 ... BLUE = auto() 1881 ... GREEN = auto() 1882 ... 1883 >>> Color.RED & Color.GREEN 1884 <Color: 0> 1885 >>> bool(Color.RED & Color.GREEN) 1886 False 1887 \end{python} 1888 Individual flags should have values that are powers of two (1, 2, 4, 8, ...), while combinations of flags will not: 1889 \begin{python} 1890 >>> class Color(Flag): 1891 ... RED = auto() 1892 ... BLUE = auto() 1893 ... GREEN = auto() 1894 ... WHITE = RED | BLUE | GREEN 1895 ... 1896 >>> Color.WHITE 1897 <Color.WHITE: 7> 1898 \end{python} 1899 Giving a name to the ``no flags set'' condition does not change its boolean value: 1900 \begin{python} 1901 >>> class Color(Flag): 1902 ... BLACK = 0 1903 ... RED = auto() 1904 ... BLUE = auto() 1905 ... GREEN = auto() 1906 ... 1907 >>> Color.BLACK 1908 <Color.BLACK: 0> 1909 >>> bool(Color.BLACK) 1910 False 1911 \end{python} 1912 @Flag@ members can also be iterated over: 1913 \begin{python} 1914 >>> purple = Color.RED | Color.BLUE 1915 >>> list(purple) 1916 [<Color.RED: 1>, <Color.BLUE: 2>] 1917 \end{python} 1918 New in version 3.11. 1919 1920 Note: For the majority of new code, @Enum@ and @Flag@ are strongly recommended, since @IntEnum@ and @IntFlag@ break some semantic promises of an enumeration (by being comparable to integers, and thus by transitivity to other unrelated enumerations). 1921 @IntEnum@ and @IntFlag@ should be used only in cases where @Enum@ and @Flag@ will not do; 1922 for example, when integer constants are replaced with enumerations, or for interoperability with other systems. 1923 1924 \subsection{Others} 1925 1926 While @IntEnum@ is part of the enum module, it would be very simple to implement independently: 1927 \begin{python} 1928 class IntEnum(int, Enum): 1929 pass 1930 \end{python} 1931 This demonstrates how similar derived enumerations can be defined; 1932 for example a @FloatEnum@ that mixes in float instead of @int@. 1933 1934 Some rules: 1935 \begin{itemize} 1936 \item 1937 When subclassing @Enum@, mix-in types must appear before @Enum@ itself in the sequence of bases, as in the @IntEnum@ example above. 1938 \item 1939 Mix-in types must be subclassable. 1940 For example, @bool@ and @range@ are not subclassable and will throw an error during Enum creation if used as the mix-in type. 1941 \item 1942 While @Enum@ can have members of any type, once you mix in an additional type, all the members must have values of that type, e.g. @int@ above. 1943 This restriction does not apply to mix-ins which only add methods and don't specify another type. 1944 \item 1945 When another data type is mixed in, the value attribute is not the same as the enum member itself, although it is equivalent and will compare equal. 1946 \item 1947 A data type is a mixin that defines @__new__()@, or a @dataclass@ 1948 \item 1949 \%-style formatting: @%s@ and @%r@ call the @Enum@ class's @__str__()@ and @__repr__()@ respectively; other codes (such as @%i@ or @%h@ for @IntEnum@) treat the enum member as its mixed-in type. 1950 \item 1951 Formatted string literals, @str.format()@, and format() will use the enum's @__str__()@ method. 1952 \end{itemize} 1953 Note: Because @IntEnum@, @IntFlag@, and @StrEnum@ are designed to be drop-in replacements for existing constants, their @__str__()@ method has been reset to their data types' @__str__()@ method. 1954 1955 \subsection{When to use \lstinline{__new__()} vs. \lstinline{__init__()}} 1956 1957 @__new__()@ must be used whenever you want to customize the actual value of the @Enum@ member. 1958 Any other modifications may go in either @__new__()@ or @__init__()@, with @__init__()@ being preferred. 1959 1960 For example, if you want to pass several items to the constructor, but only want one of them to be the value: 1961 \begin{python} 1962 >>> class Coordinate(bytes, Enum): 1963 ... """ 1964 ... Coordinate with binary codes that can be indexed by the int code. 1965 ... """ 1966 ... def __new__(cls, value, label, unit): 1967 ... obj = bytes.__new__(cls, [value]) 1968 ... obj._value_ = value 1969 ... obj.label = label 1970 ... obj.unit = unit 1971 ... return obj 1972 ... PX = (0, 'P.X', 'km') 1973 ... PY = (1, 'P.Y', 'km') 1974 ... VX = (2, 'V.X', 'km/s') 1975 ... VY = (3, 'V.Y', 'km/s') 1976 1977 >>> print(Coordinate['PY']) 1978 Coordinate.PY 1979 1980 >>> print(Coordinate(3)) 1981 Coordinate.VY 1982 \end{python} 1983 Warning: Do not call @super().__new__()@, as the lookup-only @__new__@ is the one that is found; instead, use the data type directly. 1984 1985 \subsection{Finer Points} 1986 1987 Supported @__dunder__@ names 1988 1989 @__members__@ is a read-only ordered mapping of member\_name:member items. It is only available on the class. 1990 1991 @__new__()@, if specified, must create and return the enum members; it is also a very good idea to set the member's @_value_@ appropriately. Once all the members are created it is no longer used. 1992 Supported @_sunder_@ names 1993 \begin{itemize} 1994 \item 1995 @_name_@ -- name of the member 1996 \item 1997 @_value_@ -- value of the member; can be set / modified in @__new__@ 1998 \item 1999 @_missing_@ -- a lookup function used when a value is not found; may be overridden 2000 \item 2001 @_ignore_@ -- a list of names, either as a @list@ or a @str@, that will not be transformed into members, and will be removed from the final class 2002 \item 2003 @_order_@ -- used in Python 2/3 code to ensure member order is consistent (class attribute, removed during class creation) 2004 \item 2005 @_generate_@next@_value_@ -- used by the Functional API and by @auto@ to get an appropriate value for an enum member; may be overridden 2006 \end{itemize} 2007 Note: For standard @Enum@ classes the next value chosen is the last value seen incremented by one. 2008 2009 For @Flag@ classes the next value chosen will be the next highest power-of-two, regardless of the last value seen. 2010 2011 New in version 3.6: @_missing_@, @_order_@, @_generate_@next@_value_@ 2012 2013 New in version 3.7: @_ignore_@ 2014 2015 To help keep Python 2 / Python 3 code in sync an @_order_@ attribute can be provided. 2016 It will be checked against the actual order of the enumeration and raise an error if the two do not match: 2017 \begin{python} 2018 >>> class Color(Enum): 2019 ... _order_ = 'RED GREEN BLUE' 2020 ... RED = 1 2021 ... BLUE = 3 2022 ... GREEN = 2 2023 ... 2024 Traceback (most recent call last): 2025 ... 2026 TypeError: member order does not match _order_: 2027 ['RED', 'BLUE', 'GREEN'] 2028 ['RED', 'GREEN', 'BLUE'] 2029 \end{python} 2030 Note: In Python 2 code the @_order_@ attribute is necessary as definition order is lost before it can be recorded. 2031 2032 \subsection{\lstinline{_Private__names}} 2033 2034 Private names are not converted to enum members, but remain normal attributes. 2035 2036 Changed in version 3.11. 2037 2038 \subsection{\lstinline{Enum} member type} 2039 2040 @Enum@ members are instances of their enum class, and are normally accessed as @EnumClass.member@. 2041 In certain situations, such as writing custom enum behavior, being able to access one member directly from another is useful, and is supported; 2042 however, in order to avoid name clashes between member names and attributes/methods from mixed-in classes, upper-case names are strongly recommended. 2043 2044 Changed in version 3.5. 2045 2046 \subsection{Creating members that are mixed with other data types} 2047 2048 When subclassing other data types, such as @int@ or @str@, with an @Enum@, all values after the = @are@ passed to that data type's constructor. For example: 2049 \begin{python} 2050 >>> class MyEnum(IntEnum): # help(int) -> int(x, base=10) -> integer 2051 ... example = '11', 16 # so x='11' and base=16 2052 ... 2053 MyEnum.example.value # and hex(11) is... 2054 17 2055 \end{python} 2056 2057 \subsection{\lstinline{Boolean} value of \lstinline{Enum} classes and members} 2058 2059 Enum classes that are mixed with non-@Enum@ types (such as @int@, @str@, etc.) are evaluated according to the mixed-in type's rules; 2060 otherwise, all members evaluate as @True@. 2061 To make your own enum's boolean evaluation depend on the member's value add the following to your class: 2062 \begin{python} 2063 def __bool__(self): 2064 return bool(self.value) 2065 \end{python} 2066 Plain @Enum@ classes always evaluate as @True@. 2067 2068 \subsection{\lstinline{Enum} classes with methods} 2069 2070 If you give your enum subclass extra methods, like the Planet class below, those methods will show up in a dir() of the member, but not of the class: 2071 \begin{python} 2072 >>> dir(Planet) 2073 ['EARTH', 'JUPITER', 'MARS', 'MERCURY', 'NEPTUNE', 'SATURN', 'URANUS', 'VENUS', 2074 '__class__', '__doc__', '__members__', '__module__'] 2075 >>> dir(Planet.EARTH) 2076 ['__class__', '__doc__', '__module__', 'mass', 'name', 'radius', 'surface_gravity', 'value'] 2077 \end{python} 2078 2079 \subsection{Combining members of \lstinline{Flag}} 2080 2081 Iterating over a combination of @Flag@ members will only return the members that are comprised of a single bit: 2082 \begin{python} 2083 >>> class Color(Flag): 2084 ... RED = auto() 2085 ... GREEN = auto() 2086 ... BLUE = auto() 2087 ... MAGENTA = RED | BLUE 2088 ... YELLOW = RED | GREEN 2089 ... CYAN = GREEN | BLUE 2090 ... 2091 >>> Color(3) # named combination 2092 <Color.YELLOW: 3> 2093 >>> Color(7) # not named combination 2094 <Color.RED|GREEN|BLUE: 7> 2095 \end{python} 2096 2097 \subsection{\lstinline{Flag} and \lstinline{IntFlag} minutia} 2098 2099 Using the following snippet for our examples: 2100 \begin{python} 2101 >>> class Color(IntFlag): 2102 ... BLACK = 0 2103 ... RED = 1 2104 ... GREEN = 2 2105 ... BLUE = 4 2106 ... PURPLE = RED | BLUE 2107 ... WHITE = RED | GREEN | BLUE 2108 ... 2109 \end{python} 2110 the following are true: 2111 \begin{itemize} 2112 \item 2113 single-bit flags are canonical 2114 \item 2115 multi-bit and zero-bit flags are aliases 2116 \item 2117 only canonical flags are returned during iteration: 2118 \begin{python} 2119 >>> list(Color.WHITE) 2120 [<Color.RED: 1>, <Color.GREEN: 2>, <Color.BLUE: 4>] 2121 \end{python} 2122 negating a flag or flag set returns a new flag/flag set with the corresponding positive integer value: 2123 \begin{python} 2124 >>> Color.BLUE 2125 <Color.BLUE: 4> 2126 2127 >>> ~Color.BLUE 2128 <Color.RED|GREEN: 3> 2129 \end{python} 2130 \item 2131 names of pseudo-flags are constructed from their members' names: 2132 \begin{python} 2133 >>> (Color.RED | Color.GREEN).name 2134 'RED|GREEN' 2135 \end{python} 2136 \item 2137 multi-bit flags, aka aliases, can be returned from operations: 2138 \begin{python} 2139 >>> Color.RED | Color.BLUE 2140 <Color.PURPLE: 5> 2141 2142 >>> Color(7) # or Color(-1) 2143 <Color.WHITE: 7> 2144 2145 >>> Color(0) 2146 <Color.BLACK: 0> 2147 \end{python} 2148 \item 2149 membership / containment checking: zero-valued flags are always considered to be contained: 2150 \begin{python} 2151 >>> Color.BLACK in Color.WHITE 2152 True 2153 \end{python} 2154 otherwise, only if all bits of one flag are in the other flag will True be returned: 2155 \begin{python} 2156 >>> Color.PURPLE in Color.WHITE 2157 True 2158 2159 >>> Color.GREEN in Color.PURPLE 2160 False 2161 \end{python} 2162 \end{itemize} 2163 There is a new boundary mechanism that controls how out-of-range / invalid bits are handled: @STRICT@, @CONFORM@, @EJECT@, and @KEEP@: 2164 \begin{itemize} 2165 \item 2166 @STRICT@ --> raises an exception when presented with invalid values 2167 \item 2168 @CONFORM@ --> discards any invalid bits 2169 \item 2170 @EJECT@ --> lose Flag status and become a normal int with the given value 2171 \item 2172 @KEEP@ --> keep the extra bits 2173 \begin{itemize} 2174 \item 2175 keeps Flag status and extra bits 2176 \item 2177 extra bits do not show up in iteration 2178 \item 2179 extra bits do show up in repr() and str() 2180 \end{itemize} 2181 \end{itemize} 2182 The default for @Flag@ is @STRICT@, the default for @IntFlag@ is @EJECT@, and the default for @_convert_@ is @KEEP@ (see @ssl.Options@ for an example of when @KEEP@ is needed). 2183 2184 \section{How are Enums and Flags different?} 2185 2186 Enums have a custom metaclass that affects many aspects of both derived @Enum@ classes and their instances (members). 2187 2188 \subsection{Enum Classes} 2189 2190 The @EnumType@ metaclass is responsible for providing the @__contains__()@, @__dir__()@, @__iter__()@ and other methods that allow one to do things with an @Enum@ class that fail on a typical class, such as @list(Color)@ or @some_enum_var@ in @Color@. 2191 @EnumType@ is responsible for ensuring that various other methods on the final @Enum@ class are correct (such as @__new__()@, @__getnewargs__()@, @__str__()@ and @__repr__()@). 2192 2193 \subsection{Flag Classes} 2194 2195 Flags have an expanded view of aliasing: to be canonical, the value of a flag needs to be a power-of-two value, and not a duplicate name. 2196 So, in addition to the @Enum@ definition of alias, a flag with no value (a.k.a. 0) or with more than one power-of-two value (e.g. 3) is considered an alias. 2197 2198 \subsection{Enum Members (aka instances)} 2199 2200 The most interesting thing about enum members is that they are singletons. 2201 @EnumType@ creates them all while it is creating the enum class itself, and then puts a custom @__new__()@ in place to ensure that no new ones are ever instantiated by returning only the existing member instances. 2202 2203 \subsection{Flag Members} 2204 2205 Flag members can be iterated over just like the @Flag@ class, and only the canonical members will be returned. 2206 For example: 2207 \begin{python} 2208 >>> list(Color) 2209 [<Color.RED: 1>, <Color.GREEN: 2>, <Color.BLUE: 4>] 2210 \end{python} 2211 (Note that BLACK, PURPLE, and WHITE do not show up.) 2212 2213 Inverting a flag member returns the corresponding positive value, rather than a negative value -- for example: 2214 \begin{python} 2215 >>> ~Color.RED 2216 <Color.GREEN|BLUE: 6> 2217 \end{python} 2218 Flag members have a length corresponding to the number of power-of-two values they contain. For example: 2219 \begin{python} 2220 >>> len(Color.PURPLE) 2221 2 2222 \end{python} 2223 2224 \subsection{Enum Cookbook} 2225 2226 While @Enum@, @IntEnum@, @StrEnum@, @Flag@, and @IntFlag@ are expected to cover the majority of use-cases, they cannot cover them all. Here are recipes for some different types of enumerations that can be used directly, or as examples for creating one's own. 2227 2228 \subsection{Omitting values} 2229 2230 In many use-cases, one doesn't care what the actual value of an enumeration is. There are several ways to define this type of simple enumeration: 2231 \begin{itemize} 2232 \item 2233 use instances of auto for the value 2234 \item 2235 use instances of object as the value 2236 \item 2237 use a descriptive string as the value 2238 \item 2239 use a tuple as the value and a custom @__new__()@ to replace the tuple with an @int@ value 2240 \end{itemize} 2241 Using any of these methods signifies to the user that these values are not important, and also enables one to add, remove, or reorder members without having to renumber the remaining members. 2242 2243 \subsection{Using \lstinline{auto}} 2244 2245 Using @auto@ would look like: 2246 \begin{python} 2247 >>> class Color(Enum): 2248 ... RED = auto() 2249 ... BLUE = auto() 2250 ... GREEN = auto() 2251 ... 2252 >>> Color.GREEN 2253 <Color.GREEN: 3> 2254 \end{python} 2255 2256 \subsection{Using \lstinline{object}} 2257 2258 Using @object@ would look like: 2259 \begin{python} 2260 >>> class Color(Enum): 2261 ... RED = object() 2262 ... GREEN = object() 2263 ... BLUE = object() 2264 ... 2265 >>> Color.GREEN 2266 <Color.GREEN: <object object at 0x...>> 2267 \end{python} 2268 This is also a good example of why you might want to write your own @__repr__()@: 2269 \begin{python} 2270 >>> class Color(Enum): 2271 ... RED = object() 2272 ... GREEN = object() 2273 ... BLUE = object() 2274 ... def __repr__(self): 2275 ... return "<%s.%s>" % (self.__class__.__name__, self._name_) 2276 ... 2277 >>> Color.GREEN 2278 <Color.GREEN> 2279 \end{python} 2280 2281 \subsection{Using a descriptive string} 2282 2283 Using a string as the value would look like: 2284 \begin{python} 2285 >>> class Color(Enum): 2286 ... RED = 'stop' 2287 ... GREEN = 'go' 2288 ... BLUE = 'too fast!' 2289 ... 2290 >>> Color.GREEN 2291 <Color.GREEN: 'go'> 2292 \end{python} 2293 2294 \subsection{Using a custom \lstinline{__new__()}} 2295 2296 Using an auto-numbering @__new__()@ would look like: 2297 \begin{python} 2298 >>> class AutoNumber(Enum): 2299 ... def __new__(cls): 2300 ... value = len(cls.__members__) + 1 2301 ... obj = object.__new__(cls) 2302 ... obj._value_ = value 2303 ... return obj 2304 ... 2305 >>> class Color(AutoNumber): 2306 ... RED = () 2307 ... GREEN = () 2308 ... BLUE = () 2309 ... 2310 >>> Color.GREEN 2311 <Color.GREEN: 2> 2312 \end{python} 2313 To make a more general purpose @AutoNumber@, add @*args@ to the signature: 2314 \begin{python} 2315 >>> class AutoNumber(Enum): 2316 ... def __new__(cls, *args): # this is the only change from above 2317 ... value = len(cls.__members__) + 1 2318 ... obj = object.__new__(cls) 2319 ... obj._value_ = value 2320 ... return obj 2321 \end{python} 2322 Then when you inherit from @AutoNumber@ you can write your own @__init__@ to handle any extra arguments: 2323 \begin{python} 2324 >>> class Swatch(AutoNumber): 2325 ... def __init__(self, pantone='unknown'): 2326 ... self.pantone = pantone 2327 ... AUBURN = '3497' 2328 ... SEA_GREEN = '1246' 2329 ... BLEACHED_CORAL = () # New color, no Pantone code yet! 2330 ... 2331 >>> Swatch.SEA_GREEN 2332 <Swatch.SEA_GREEN: 2> 2333 >>> Swatch.SEA_GREEN.pantone 2334 '1246' 2335 >>> Swatch.BLEACHED_CORAL.pantone 2336 'unknown' 2337 \end{python} 2338 Note: The @__new__()@ method, if defined, is used during creation of the Enum members; 2339 it is then replaced by Enum's @__new__()@ which is used after class creation for lookup of existing members. 2340 2341 Warning: Do not call @super().__new__()@, as the lookup-only @__new__@ is the one that is found; 2342 instead, use the data type directly -- e.g.: 2343 \begin{python} 2344 obj = int.__new__(cls, value) 2345 \end{python} 2346 2347 \subsection{OrderedEnum} 2348 2349 An ordered enumeration that is not based on @IntEnum@ and so maintains the normal @Enum@ invariants (such as not being comparable to other enumerations): 2350 \begin{python} 2351 >>> class OrderedEnum(Enum): 2352 ... def __ge__(self, other): 2353 ... if self.__class__ is other.__class__: 2354 ... return self.value >= other.value 2355 ... return NotImplemented 2356 ... def __gt__(self, other): 2357 ... if self.__class__ is other.__class__: 2358 ... return self.value > other.value 2359 ... return NotImplemented 2360 ... def __le__(self, other): 2361 ... if self.__class__ is other.__class__: 2362 ... return self.value <= other.value 2363 ... return NotImplemented 2364 ... def __lt__(self, other): 2365 ... if self.__class__ is other.__class__: 2366 ... return self.value < other.value 2367 ... return NotImplemented 2368 ... 2369 >>> class Grade(OrderedEnum): 2370 ... A = 5 2371 ... B = 4 2372 ... C = 3 2373 ... D = 2 2374 ... F = 1 2375 >>> Grade.C < Grade.A 2376 True 2377 \end{python} 2378 2379 \subsection{DuplicateFreeEnum} 2380 2381 Raises an error if a duplicate member value is found instead of creating an alias: 2382 \begin{python} 2383 >>> class DuplicateFreeEnum(Enum): 2384 ... def __init__(self, *args): 2385 ... cls = self.__class__ 2386 ... if any(self.value == e.value for e in cls): 2387 ... a = self.name 2388 ... e = cls(self.value).name 2389 ... raise ValueError( 2390 ... "aliases not allowed in DuplicateFreeEnum: %r --> %r" 2391 ... % (a, e)) 2392 >>> class Color(DuplicateFreeEnum): 2393 ... RED = 1 2394 ... GREEN = 2 2395 ... BLUE = 3 2396 ... GRENE = 2 2397 ... 2398 Traceback (most recent call last): 2399 ... 2400 ValueError: aliases not allowed in DuplicateFreeEnum: 'GRENE' --> 'GREEN' 2401 \end{python} 2402 Note: This is a useful example for subclassing Enum to add or change other behaviors as well as disallowing aliases. 2403 If the only desired change is disallowing aliases, the @unique()@ decorator can be used instead. 2404 2405 \subsection{Planet} 2406 2407 If @__new__()@ or @__init__()@ is defined, the value of the enum member will be passed to those methods: 2408 \begin{python} 2409 >>> class Planet(Enum): 2410 ... MERCURY = (3.303e+23, 2.4397e6) 2411 ... VENUS = (4.869e+24, 6.0518e6) 2412 ... EARTH = (5.976e+24, 6.37814e6) 2413 ... MARS = (6.421e+23, 3.3972e6) 2414 ... JUPITER = (1.9e+27, 7.1492e7) 2415 ... SATURN = (5.688e+26, 6.0268e7) 2416 ... URANUS = (8.686e+25, 2.5559e7) 2417 ... NEPTUNE = (1.024e+26, 2.4746e7) 2418 ... def __init__(self, mass, radius): 2419 ... self.mass = mass # in kilograms 2420 ... self.radius = radius # in meters 2421 ... $\@$property 2422 ... def surface_gravity(self): 2423 ... # universal gravitational constant (m3 kg-1 s-2) 2424 ... G = 6.67300E-11 2425 ... return G * self.mass / (self.radius * self.radius) 2426 ... 2427 >>> Planet.EARTH.value 2428 (5.976e+24, 6378140.0) 2429 >>> Planet.EARTH.surface_gravity 2430 9.802652743337129 2431 \end{python} 2432 2433 \subsection{TimePeriod} 2434 2435 An example to show the @_ignore_@ attribute in use: 2436 \begin{python} 2437 >>> from datetime import timedelta 2438 >>> class Period(timedelta, Enum): 2439 ... "different lengths of time" 2440 ... _ignore_ = 'Period i' 2441 ... Period = vars() 2442 ... for i in range(367): 2443 ... Period['day_%d' % i] = i 2444 ... 2445 >>> list(Period)[:2] 2446 [<Period.day_0: datetime.timedelta(0)>, <Period.day_1: datetime.timedelta(days=1)>] 2447 >>> list(Period)[-2:] 2448 [<Period.day_365: datetime.timedelta(days=365)>, <Period.day_366: datetime.timedelta(days=366)>] 2449 \end{python} 2450 2451 \subsection{Subclassing EnumType} 2452 2453 While most enum needs can be met by customizing @Enum@ subclasses, either with class decorators or custom functions, @EnumType@ can be subclassed to provide a different Enum experience. 2454 1187 2455 1188 2456 \section{Algebraic Data Type}
Note: See TracChangeset
for help on using the changeset viewer.