Changeset c033405 for doc/theses
- Timestamp:
- Jun 12, 2024, 9:19:14 AM (6 months ago)
- Branches:
- master
- Children:
- bc17be98
- Parents:
- 736a38d
- File:
-
- 1 edited
Legend:
- Unmodified
- Added
- Removed
-
doc/theses/jiada_liang_MMath/relatedwork.tex
r736a38d rc033405 43 43 \begin{pascal} 44 44 Type @{$\color{red}\$$PACKENUM 1}@ SmallEnum = ( one, two, three ); 45 45 @{$\color{red}\$$PACKENUM 4}@ LargeEnum = ( BigOne, BigTwo, BigThree ); 46 46 Var S : SmallEnum; { 1 byte } 47 47 L : LargeEnum; { 4 bytes} … … 406 406 Mon 0, Tue 1, Wed 2, Thu 10, Fri 11, Sat 12, Sun 13, 407 407 \end{csharp} 408 Hence, enumerating is not supplied directly by the enumeration, but indirectly through another enumerable type, array. 408 409 409 410 An enumeration type cannot declare an array dimension but an enumerator can be used as a subscript. … … 535 536 \begin{Java} 536 537 public boolean isWeekday() { return !ordinal()! <= Fri.ordinal(); } 537 public boolean isWeekend() { return Fri.ordinal() <!ordinal()!; }538 public boolean isWeekend() { return Sat.ordinal() <= !ordinal()!; } 538 539 \end{Java} 539 540 Notice the unqualified calls to @ordinal@ in the members implying a \lstinline[language=Java]{this} to some implicit implementation variable, likely an @int@. … … 590 591 0 1 Mon, 1 2 Tue, 2 3 Wed, 3 4 Thu, 4 5 Fri, 5 6 Sat, 6 7 Sun, 591 592 \end{Java} 593 Like \Csharp, enumerating is supplied indirectly through another enumerable type, not via the enumeration. 592 594 593 595 An enumeration type cannot declare an array dimension nor can an enumerator be used as a subscript. … … 602 604 % https://doc.rust-lang.org/reference/items/enumerations.html 603 605 604 Rust @enum@ provides two largely independent mechanisms : an ADT and an enumeration.606 Rust @enum@ provides two largely independent mechanisms from a single language feature: an ADT and an enumeration. 605 607 When @enum@ is an ADT, pattern matching is used to discriminate among the variant types. 606 608 \begin{cquote} 607 \sf\setlength{\tabcolsep}{20pt} 608 \begin{tabular}{@{}ll@{}} 609 \begin{tabular}{@{}l@{\hspace{30pt}}ll@{}} 609 610 \begin{rust} 610 611 struct S { 611 612 i : isize, j : isize 612 613 } 614 let mut s = S{ i : 3, j : 4 }; 613 615 enum @ADT@ { 614 I( isize), // int615 F( f64), // float616 S( S), // struct616 I( isize ), $\C[1in]{// int}$ 617 F( f64 ), $\C{// float}$ 618 S( S ), $\C{// struct}\CRT$ 617 619 } 618 620 \end{rust} 619 621 & 620 622 \begin{rust} 621 let mut s = S{ i : 3, j : 4 };622 623 let mut adt : ADT; 623 adt = ADT::I(3); adt = ADT::F(3.5); adt = ADT::S(s); // init examples 624 adt = ADT::I(3); println!( "{:?}", adt ); 625 adt = ADT::F(3.5); println!( "{:?}", adt ); 626 adt = ADT::S(s); println!( "{:?}", adt ); 624 627 @match@ adt { 625 ADT::I( i) => println!( "{:?}", i ),626 ADT::F( f) => println!( "{:?}", f ),627 ADT::S( s) => println!( "{:?} {:?}", s.i, s.j ),628 ADT::I( i ) => println!( "{:}", i ), 629 ADT::F( f ) => println!( "{:}", f ), 630 ADT::S( s ) => println!( "{:} {:}", s.i, s.j ), 628 631 } 629 632 \end{rust} 630 \end{tabular} 631 \end{cquote} 632 When the variant types are the unit type, the ADT is still not an enumeration because there is no enumerating \see{\VRef{s:AlgebraicDataType}}. 633 & 634 \begin{rust} 635 I(3) 636 F(3.5) 637 S(S { i: 3, j: 4 }) 638 3 4 639 640 641 642 643 644 \end{rust} 645 \end{tabular} 646 \end{cquote} 647 Even when the variant types are the unit type, the ADT is still not an enumeration because there is no enumerating \see{\VRef{s:AlgebraicDataType}}. 633 648 \begin{rust} 634 649 enum Week { Mon, Tues, Wed, Thu, Fri, Sat, Sun@,@ } // terminating comma … … 643 658 However, Rust allows direct setting of the ADT constructor, which means it is actually a tag. 644 659 \begin{cquote} 645 \s f\setlength{\tabcolsep}{15pt}660 \setlength{\tabcolsep}{15pt} 646 661 \begin{tabular}{@{}ll@{}} 647 662 \begin{rust} … … 696 711 \end{tabular} 697 712 \end{cquote} 698 However, there is no mechanism to iterate through an enumeration without ancasting to integral and positions versus values is not handled.713 However, there is no mechanism to iterate through an enumeration without casting to integral and positions versus values is not handled. 699 714 \begin{c++} 700 715 for d in Week::Mon as isize ..= Week::Sun as isize { … … 711 726 % https://www.programiz.com/swift/online-compiler 712 727 713 A Swift enumeration provides a heterogenous set of enumerators, like a tagged @union@, where the field name is the enumerator and its list of type parameters form its type. 728 Like Rust, Swift @enum@ provides two largely independent mechanisms from a single language feature: an ADT and an enumeration. 729 When @enum@ is an ADT, pattern matching is used to discriminate among the variant types. 730 \begin{cquote} 731 \setlength{\tabcolsep}{20pt} 732 \begin{tabular}{@{}l@{\hspace{55pt}}ll@{}} 714 733 \begin{swift} 715 enum Many { 716 case Mon, Tue, Wed, Thu, Fri, Sat, Sun // basic enumerator 717 case code( String ) // string enumerator 718 case tuple( Int, Int, Int ) // tuple enumerator 734 struct S { 735 var i : Int, j : Int 736 } 737 var s = S( i : 3, j : 5 ) 738 @enum@ ADT { 739 case I(Int) $\C[1.125in]{// int}$ 740 case F(Float) $\C{// float}$ 741 case S(S) $\C{// struct}\CRT$ 742 } 743 \end{swift} 744 & 745 \begin{swift} 746 var adt : ADT 747 adt = .I( 3 ); print( adt ) 748 adt = .F( 3.5 ); print( adt ) 749 adt = .S( s ); print( adt ) 750 @switch@ adt { // pattern matching 751 case .I(let i): print( i ) 752 case .F(let f): print( f ) 753 case .S(let s): print( s.i, s.j ) 754 } 755 \end{swift} 756 & 757 \begin{swift} 758 I(3) 759 F(3.5) 760 S(S(i: 3, j: 5)) 761 3 5 762 763 764 765 766 767 \end{swift} 768 \end{tabular} 769 \end{cquote} 770 (Note, after an @adt@'s type is know, the enumerator is inferred without qualification, \eg @.I(3)@.) 771 772 An enumeration is created when \emph{all} the enumerators are unit-type. 773 \begin{swift} 774 enum Week { 775 case Mon, Tue, Wed, Thu, Fri, Sat, Sun // unit-type 719 776 }; 720 var day = Many.Sat; // qualification to resolve type 721 print( day ); 722 day = .Wed // no qualification after type resolved 723 print( day ); 724 day = .code( "ABC" ); 725 print( day ); 726 day = .tuple( 1, 2, 3 ); 727 print( day ); 728 729 Sat 730 Wed 731 code("ABC") 732 tuple(1, 2, 3) 777 var week : Week = Week.Mon; 733 778 \end{swift} 734 735 736 An enumeration defines a common type for a group of related values and enables you to work with those values in a type-safe way within your code. 737 738 If you are familiar with C, you will know that C enumerations assign related names to a set of integer values. 739 Enumerations in Swift are much more flexible, and don't have to provide a value for each case of the enumeration. 740 If a value (known as a raw value) is provided for each enumeration case, the value can be a string, a character, or a value of any integer or floating-point type. 741 742 Alternatively, enumeration cases can specify associated values of any type to be stored along with each different case value, much as unions or variants do in other languages. 743 You can define a common set of related cases as part of one enumeration, each of which has a different set of values of appropriate types associated with it. 744 745 Enumerations in Swift are first-class types in their own right. 746 They adopt many features traditionally supported only by classes, such as computed properties to provide additional information about the enumeration's current value, and instance methods to provide functionality related to the values the enumeration represents. 747 Enumerations can also define initializers to provide an initial case value; 748 can be extended to expand their functionality beyond their original implementation; and can conform to protocols to provide standard functionality. 749 750 For more about these capabilities, see Properties, Methods, Initialization, Extensions, and Protocols. 751 752 \paragraph{Enumeration Syntax} 753 754 755 Note: 756 Swift enumeration cases don't have an integer value set by default, unlike languages like C and Objective-C. 757 In the CompassPoint example above, @north@, @south@, @east@ and @west@ don't implicitly equal 0, 1, 2 and 3. 758 Instead, the different enumeration cases are values in their own right, with an explicitly defined type of CompassPoint. 759 760 Multiple cases can appear on a single line, separated by commas: 779 As well, it is possible to type \emph{all} the enumerators with a common type, and set different values for each enumerator; 780 for integral types, there is auto-incrementing. 781 \begin{cquote} 782 \setlength{\tabcolsep}{15pt} 783 \begin{tabular}{@{}lll@{}} 761 784 \begin{swift} 762 enum Planet { 763 case mercury, venus, earth, mars, jupiter, saturn, uranus, neptune 764 } 785 enum WeekInt: @Int@ { 786 case Mon, Tue, Wed, Thu = 10, Fri, 787 Sat = 4, Sun // auto-incrementing 788 }; 765 789 \end{swift} 766 Each enumeration definition defines a new type. 767 Like other types in Swift, their names (such as @CompassPoint@ and @Planet@) start with a capital letter. 768 Give enumeration types singular rather than plural names, so that they read as self-evident: 790 & 769 791 \begin{swift} 770 var directionToHead = CompassPoint.west 792 enum WeekStr: @String@ { 793 case Mon = "MON", Tue, Wed, Thu, Fri, 794 Sat = "SAT", Sun 795 }; 771 796 \end{swift} 772 The type of @directionToHead@ is inferred when it's initialized with one of the possible values of @CompassPoint@. 773 Once @directionToHead@ is declared as a @CompassPoint@, you can set it to a different @CompassPoint@ value using a shorter dot syntax: 797 \end{tabular} 798 \end{cquote} 799 An enumeration only supports equality comparison between enumerator values, unless it inherits from @Comparable@, adding relational operators @<@, @<=@, @>@, and @>=@. 800 801 An enumeration can have methods. 774 802 \begin{swift} 775 directionToHead = .east 803 enum Week: Comparable { 804 case Mon, Tue, Wed, Thu, Fri, Sat, Sun // unit-type 805 func @isWeekday() -> Bool@ { return self <= .Fri } // method 806 func @isWeekend() -> Bool@ { return .Sat <= self } // method 807 }; 776 808 \end{swift} 777 The type of @directionToHead@ is already known, and so you can drop the type when setting its value. 778 This makes for highly readable code when working with explicitly typed enumeration values. 779 780 \paragraph{Matching Enumeration Values with a Switch Statement} 781 782 You can match individual enumeration values with a switch statement: 809 An enumeration can be used in the @if@ and @switch@ statements, where @switch@ must be exhaustive or have a @default@. 810 \begin{cquote} 811 \setlength{\tabcolsep}{15pt} 812 \begin{tabular}{@{}ll@{}} 783 813 \begin{swift} 784 directionToHead = .south 785 switch directionToHead { 786 case .north: 787 print("Lots of planets have a north") 788 case .south: 789 print("Watch out for penguins") 790 case .east: 791 print("Where the sun rises") 792 case .west: 793 print("Where the skies are blue") 794 } 795 // Prints "Watch out for penguins" 814 if @week <= .Fri@ { 815 print( "weekday" ); 816 } 817 818 796 819 \end{swift} 797 You can read this code as: 798 \begin{quote} 799 "Consider the value of directionToHead. 800 In the case where it equals @.north@, print "Lots of planets have a north". 801 In the case where it equals @.south@, print "Watch out for penguins"." 802 803 ...and so on. 804 \end{quote} 805 As described in Control Flow, a switch statement must be exhaustive when considering an enumeration's cases. 806 If the case for @.west@ is omitted, this code doesn't compile, because it doesn't consider the complete list of @CompassPoint@ cases. 807 Requiring exhaustiveness ensures that enumeration cases aren't accidentally omitted. 808 809 When it isn't appropriate to provide a case for every enumeration case, you can provide a default case to cover any cases that aren't addressed explicitly: 820 & 810 821 \begin{swift} 811 let somePlanet = Planet.earth 812 switch somePlanet { 813 case .earth: 814 print("Mostly harmless") 815 default: 816 print("Not a safe place for humans") 817 } 818 // Prints "Mostly harmless" 822 switch @week@ { 823 case .Mon: print( "Mon" ) 824 ... 825 case .Sun: print( "Sun" ) 826 } 819 827 \end{swift} 820 821 \paragraph{Iterating over Enumeration Cases} 822 823 For some enumerations, it's useful to have a collection of all of that enumeration's cases. 824 You enable this by writing @CaseIterable@ after the enumeration's name. 825 Swift exposes a collection of all the cases as an allCases property of the enumeration type. 826 Here's an example: 828 \end{tabular} 829 \end{cquote} 830 831 Enumerating is accomplished by inheriting from @CaseIterable@ without any associated values. 827 832 \begin{swift} 828 enum Beverage: CaseIterable { 829 case coffee, tea, juice 830 } 831 let numberOfChoices = Beverage.allCases.count 832 print("\(numberOfChoices) beverages available") 833 // Prints "3 beverages available" 833 enum Week: Comparable, @CaseIterable@ { 834 case Mon, Tue, Wed, Thu, Fri, Sat, Sun // unit-type 835 }; 836 var weeki : Week = Week.Mon; 837 if weeki <= .Fri { 838 print( "weekday" ); 839 } 840 for day in Week@.allCases@ { 841 print( day, terminator:" " ) 842 } 843 weekday 844 Mon Tue Wed Thu Fri Sat Sun 834 845 \end{swift} 835 In the example above, you write @Beverage.allCases@ to access a collection that contains all of the cases of the @Beverage@ enumeration. 836 You can use @allCases@ like any other collection -- the collection's elements are instances of the enumeration type, so in this case they're Beverage values. 837 The example above counts how many cases there are, and the example below uses a for-in loop to iterate over all the cases. 846 The @enum.allCases@ property returns a collection of all the cases for looping over an enumeration type or variable (expensive operation). 847 848 A typed enumeration is accomplished by inheriting from any Swift type, and accessing the underlying enumerator value is done with attribute @rawValue@. 849 Type @Int@ has auto-incrementing from previous enumerator; 850 type @String@ has auto-incrementing of the enumerator label. 851 \begin{cquote} 852 \setlength{\tabcolsep}{15pt} 853 \begin{tabular}{@{}lll@{}} 838 854 \begin{swift} 839 for beverage in Beverage.allCases { 840 print(beverage) 841 } 842 // coffee 843 // tea 844 // juice 855 enum WeekInt: @Int@, CaseIterable { 856 case Mon, Tue, Wed, Thu = 10, Fri, 857 Sat = 4, Sun // auto-incrementing 858 }; 859 for day in WeekInt.allCases { 860 print( day@.rawValue@, terminator:" " ) 861 } 862 0 1 2 10 11 4 5 845 863 \end{swift} 846 The syntax used in the examples above marks the enumeration as conforming to the @CaseIterable@ protocol. 847 For information about protocols, see Protocols. 848 849 \paragraph{Associated Values} 850 The examples in the previous section show how the cases of an enumeration are a defined (and typed) value in their own right. 851 You can set a constant or variable to Planet.earth, and check for this value later. 852 However, it's sometimes useful to be able to store values of other types alongside these case values. 853 This additional information is called an associated value, and it varies each time you use that case as a value in your code. 854 855 You can define Swift enumerations to store associated values of any given type, and the value types can be different for each case of the enumeration if needed. 856 Enumerations similar to these are known as discriminated unions, tagged unions, or variants in other programming languages. 857 858 For example, suppose an inventory tracking system needs to track products by two different types of barcode. 859 Some products are labeled with 1D barcodes in UPC format, which uses the numbers 0 to 9. 860 Each barcode has a number system digit, followed by five manufacturer code digits and five product code digits. 861 These are followed by a check digit to verify that the code has been scanned correctly: 862 863 Other products are labeled with 2D barcodes in QR code format, which can use any ISO 8859-1 character and can encode a string up to 2,953 characters long: 864 865 It's convenient for an inventory tracking system to store UPC barcodes as a tuple of four integers, and QR code barcodes as a string of any length. 866 867 In Swift, an enumeration to define product barcodes of either type might look like this: 864 & 868 865 \begin{swift} 869 enum Barcode { 870 case upc(Int, Int, Int, Int) 871 case qrCode(String) 872 } 866 enum WeekStr: @String@, CaseIterable { 867 case Mon = "MON", Tue, Wed, Thu, Fri, 868 Sat = "SAT", Sun 869 }; 870 for day in WeekStr.allCases { 871 print( day@.rawValue@, terminator:" " ) 872 } 873 MON Tue Wed Thu Fri SAT Sun 873 874 \end{swift} 874 This can be read as: 875 \begin{quote} 876 "Define an enumeration type called Barcode, which can take either a value of upc with an associated value of type @(Int, Int, Int, Int)@, or a value of @qrCode@ with an associated value of type @String@." 877 \end{quote} 878 This definition doesn't provide any actual @Int@ or @String@ values -- it just defines the type of associated values that Barcode constants and variables can store when they're equal to @Barcode.upc@ or @Barcode.qrCode@. 879 880 You can then create new barcodes using either type: 875 \end{tabular} 876 \end{cquote} 877 878 There is a bidirectional conversion from typed enumerator to @rawValue@ and vise versa. 881 879 \begin{swift} 882 var productBarcode = Barcode.upc(8, 85909, 51226, 3) 880 var weekInt : WeekInt = WeekInt.Mon; 881 if let opt = WeekInt( rawValue: 0 ) { // test optional return value 882 print( weekInt.rawValue, opt ) // 0 Mon 883 } else { 884 print( "invalid weekday lookup" ) 885 } 883 886 \end{swift} 884 This example creates a new variable called @productBarcode@ and assigns it a value of @Barcode.upc@ with an associated tuple value of @(8, 85909, 51226, 3)@. 885 886 You can assign the same product a different type of barcode: 887 \begin{swift} 888 productBarcode = .qrCode("ABCDEFGHIJKLMNOP") 889 \end{swift} 890 At this point, the original @Barcode.upc@ and its integer values are replaced by the new @Barcode.qrCode@ and its string value. 891 Constants and variables of type Barcode can store either a @.upc@ or a @.qrCode@ (together with their associated values), but they can store only one of them at any given time. 892 893 You can check the different barcode types using a switch statement, similar to the example in Matching Enumeration Values with a Switch Statement. 894 This time, however, the associated values are extracted as part of the switch statement. 895 You extract each associated value as a constant (with the let prefix) or a variable (with the var prefix) for use within the switch case's body: 896 \begin{swift} 897 switch productBarcode { 898 case .upc(let numberSystem, let manufacturer, let product, let check): 899 print("UPC: \(numberSystem), \(manufacturer), \(product), \(check).") 900 case .qrCode(let productCode): 901 print("QR code: \(productCode).") 902 } 903 // Prints "QR code: ABCDEFGHIJKLMNOP." 904 \end{swift} 905 If all of the associated values for an enumeration case are extracted as constants, or if all are extracted as variables, you can place a single let or var annotation before the case name, for brevity: 906 \begin{swift} 907 switch productBarcode { 908 case let .upc(numberSystem, manufacturer, product, check): 909 print("UPC : \(numberSystem), \(manufacturer), \(product), \(check).") 910 case let .qrCode(productCode): 911 print("QR code: \(productCode).") 912 } 913 // Prints "QR code: ABCDEFGHIJKLMNOP." 914 \end{swift} 915 916 \paragraph{Raw Values} 917 918 The barcode example in Associated Values shows how cases of an enumeration can declare that they store associated values of different types. 919 As an alternative to associated values, enumeration cases can come prepopulated with default values (called raw values), which are all of the same type. 920 921 Here's an example that stores raw ASCII values alongside named enumeration cases: 922 \begin{swift} 923 enum ASCIIControlCharacter: Character { 924 case tab = "\t" 925 case lineFeed = "\n" 926 case carriageReturn = "\r" 927 } 928 \end{swift} 929 Here, the raw values for an enumeration called ASCIIControlCharacter are defined to be of type Character, and are set to some of the more common ASCII control characters. 930 Character values are described in Strings and Characters. 931 932 Raw values can be strings, characters, or any of the integer or floating-point number types. 933 Each raw value must be unique within its enumeration declaration. 934 935 Note 936 937 Raw values are not the same as associated values. 938 Raw values are set to prepopulated values when you first define the enumeration in your code, like the three ASCII codes above. 939 The raw value for a particular enumeration case is always the same. 940 Associated values are set when you create a new constant or variable based on one of the enumeration's cases, and can be different each time you do so. 941 Implicitly Assigned Raw Values 942 943 When you're working with enumerations that store integer or string raw values, you don't have to explicitly assign a raw value for each case. 944 When you don't, Swift automatically assigns the values for you. 945 946 For example, when integers are used for raw values, the implicit value for each case is one more than the previous case. 947 If the first case doesn't have a value set, its value is 0. 948 949 The enumeration below is a refinement of the earlier Planet enumeration, with integer raw values to represent each planet's order from the sun: 950 951 \begin{swift} 952 enum Planet: Int { 953 case mercury = 1, venus, earth, mars, jupiter, saturn, uranus, neptune 954 } 955 \end{swift} 956 In the example above, Planet.mercury has an explicit raw value of 1, Planet.venus has an implicit raw value of 2, and so on. 957 958 When strings are used for raw values, the implicit value for each case is the text of that case's name. 959 960 The enumeration below is a refinement of the earlier CompassPoint enumeration, with string raw values to represent each direction's name: 961 \begin{swift} 962 enum CompassPoint: String { 963 case north, south, east, west 964 } 965 \end{swift} 966 In the example above, CompassPoint.south has an implicit raw value of "south", and so on. 967 968 You access the raw value of an enumeration case with its rawValue property: 969 \begin{swift} 970 let earthsOrder = Planet.earth.rawValue 971 // earthsOrder is 3 972 973 let sunsetDirection = CompassPoint.west.rawValue 974 // sunsetDirection is "west" 975 \end{swift} 976 977 \paragraph{Initializing from a Raw Value} 978 979 If you define an enumeration with a raw-value type, the enumeration automatically receives an initializer that takes a value of the raw value's type (as a parameter called rawValue) and returns either an enumeration case or nil. 980 You can use this initializer to try to create a new instance of the enumeration. 981 982 This example identifies Uranus from its raw value of 7: 983 \begin{swift} 984 let possiblePlanet = Planet(rawValue: 7) 985 // possiblePlanet is of type Planet? and equals Planet.uranus 986 \end{swift} 987 Not all possible Int values will find a matching planet, however. 988 Because of this, the raw value initializer always returns an optional enumeration case. 989 In the example above, possiblePlanet is of type Planet?, or "optional Planet." 990 Note 991 992 The raw value initializer is a failable initializer, because not every raw value will return an enumeration case. 993 For more information, see Failable Initializers. 994 995 If you try to find a planet with a position of 11, the optional Planet value returned by the raw value initializer will be nil: 996 \begin{swift} 997 let positionToFind = 11 998 if let somePlanet = Planet(rawValue: positionToFind) { 999 switch somePlanet { 1000 case .earth: 1001 print("Mostly harmless") 1002 default: 1003 print("Not a safe place for humans") 1004 } 1005 } else { 1006 print("There isn't a planet at position \(positionToFind)") 1007 } 1008 // Prints "There isn't a planet at position 11" 1009 \end{swift} 1010 This example uses optional binding to try to access a planet with a raw value of 11. 1011 The statement if let somePlanet = Planet(rawValue: 11) creates an optional Planet, and sets somePlanet to the value of that optional Planet if it can be retrieved. 1012 In this case, it isn't possible to retrieve a planet with a position of 11, and so the else branch is executed instead. 1013 1014 \paragraph{Recursive Enumerations} 1015 1016 A recursive enumeration is an enumeration that has another instance of the enumeration as the associated value for one or more of the enumeration cases. 1017 You indicate that an enumeration case is recursive by writing indirect before it, which tells the compiler to insert the necessary layer of indirection. 1018 1019 For example, here is an enumeration that stores simple arithmetic expressions: 1020 \begin{swift} 1021 enum ArithmeticExpression { 1022 case number(Int) 1023 indirect case addition(ArithmeticExpression, ArithmeticExpression) 1024 indirect case multiplication(ArithmeticExpression, ArithmeticExpression) 1025 } 1026 \end{swift} 1027 You can also write indirect before the beginning of the enumeration to enable indirection for all of the enumeration's cases that have an associated value: 1028 \begin{swift} 1029 indirect enum ArithmeticExpression { 1030 case number(Int) 1031 case addition(ArithmeticExpression, ArithmeticExpression) 1032 case multiplication(ArithmeticExpression, ArithmeticExpression) 1033 } 1034 \end{swift} 1035 This enumeration can store three kinds of arithmetic expressions: a plain number, the addition of two expressions, and the multiplication of two expressions. 1036 The addition and multiplication cases have associated values that are also arithmetic expressions -- these associated values make it possible to nest expressions. 1037 For example, the expression (5 + 4) * 2 has a number on the right-hand side of the multiplication and another expression on the left-hand side of the multiplication. 1038 Because the data is nested, the enumeration used to store the data also needs to support nesting -- this means the enumeration needs to be recursive. 1039 The code below shows the ArithmeticExpression recursive enumeration being created for (5 + 4) * 2: 1040 \begin{swift} 1041 let five = ArithmeticExpression.number(5) 1042 let four = ArithmeticExpression.number(4) 1043 let sum = ArithmeticExpression.addition(five, four) 1044 let product = ArithmeticExpression.multiplication(sum, ArithmeticExpression.number(2)) 1045 \end{swift} 1046 A recursive function is a straightforward way to work with data that has a recursive structure. 1047 For example, here's a function that evaluates an arithmetic expression: 1048 \begin{swift} 1049 func evaluate(_ expression: ArithmeticExpression) -> Int { 1050 switch expression { 1051 case let .number(value): 1052 return value 1053 case let .addition(left, right): 1054 return evaluate(left) + evaluate(right) 1055 case let .multiplication(left, right): 1056 return evaluate(left) * evaluate(right) 1057 } 1058 } 1059 1060 print(evaluate(product)) 1061 // Prints "18" 1062 \end{swift} 1063 This function evaluates a plain number by simply returning the associated value. 1064 It evaluates an addition or multiplication by evaluating the expression on the left-hand side, evaluating the expression on the right-hand side, and then adding them or multiplying them. 887 Conversion from @rawValue@ to enumerator may fail (bad lookup), so the result is an optional value. 1065 888 1066 889 … … 1068 891 % https://docs.python.org/3/howto/enum.html 1069 892 1070 Python is a dynamically-typed reflexive programming language with multiple versions, and hence, it is possible to extend existing or build new language features within the language. 893 Python is a dynamically-typed reflexive programming language with multiple incompatible versions. 894 The generality of the language makes it is possible to extend existing or build new language features. 1071 895 As a result, discussing Python enumerations is a moving target, because if a features does not exist, it can often be created with varying levels of complexity within the language. 1072 Nevertheless, the following is a discuss of the core enumeration features that come with Python 3.13. 1073 1074 A Python enumeration type is a set of ordered scoped identifiers (enumerators) bound to \emph{unique} values. 1075 An enumeration is not a basic type; 1076 it is a @class@ inheriting from the @Enum@ class, where the enumerators must be explicitly initialized, \eg: 1077 \begin{python} 1078 class Week(@Enum@): Mon = 1; Tue = 2; Wed = 3; Thu = 4; Fri = 5; Sat = 6; Sun = 7 896 Therefore, the following discussion is (mostly) restricted to the core enumeration features in Python 3.13. 897 898 A Python enumeration is not a basic type; 899 it is a @class@ inheriting from the @Enum@ class. 900 The @Enum@ class presents a set of scoped enumerators, where each enumerator is a pair object with a \emph{constant} string name and arbitrary value. 901 Hence, an enumeration instance is a fixed type (enumeration pair), and its value is the type of one of the enumerator pairs. 902 903 The enumerator value fields must be explicitly initialized and be \emph{unique}. 904 \begin{python} 905 class Week(!Enum!): Mon = 1; Tue = 2; Wed = 3; Thu = 4; Fri = 5; Sat = 6; Sun = 7 1079 906 \end{python} 1080 907 and/or explicitly auto initialized, \eg: 1081 908 \begin{python} 1082 class Week(Enum): Mon = 1; Tue = 2; Wed = 3; Thu = 10; Fri = @auto()@; Sat = 4; Sun = @auto()@ 1083 \end{python} 1084 where @auto@ increments by 1 from the previous enumerator value \see{Golang \lstinline[language=Go]{iota}, \VRef{s:Golang}}. 1085 Object initialization and assignment are restricted to the enumerators of this type. 1086 An enumerator initialized with same value is an alias and invisible at the enumeration level, \ie the alias it substituted for its aliasee. 1087 \begin{python} 1088 class Week(Enum): Mon = 1; Tue = 2; Wed = 3; Thu = 10; Fri = @10@; Sat = @10@; Sun = @10@ 909 class Week(Enum): Mon = 1; Tue = 2; Wed = 3; Thu = 10; Fri = !auto()!; Sat = 4; Sun = !auto()! 910 Mon : 1 Tue : 2 Wed : 3 Thu : 10 Fri : !11! Sat : 4 Sun : !12! 911 \end{python} 912 where @auto@ increments by 1 from the previous @auto@ value \see{Golang \lstinline[language=Go]{iota}, \VRef{s:Golang}}. 913 @auto@ is controlled by member @_generate_next_value_()@, which can be overridden: 914 \begin{python} 915 @staticmethod 916 def _generate_next_value_( name, start, count, last_values ): 917 return name 918 \end{python} 919 920 There is no direct concept of restricting the enumerators in an enumeration \emph{instance} because the dynamic typing changes the type. 921 \begin{python} 922 class RGB(Enum): Red = 1; Green = 2; Blue = 3 923 day : Week = Week.Tue; $\C{\# type is Week}$ 924 !day = RGB.Red! $\C{\# type is RGB}$ 925 !day : Week = RGB.Red! $\C{\# type is RGB}$ 926 \end{python} 927 The enumerators are constants and cannot be reassigned. 928 Hence, while enumerators can be different types, 929 \begin{python} 930 class Diff(Enum): Int = 1; Float = 3.5; Str = "ABC" 931 \end{python} 932 it is not an ADT because the enumerator names are not constructors. 933 934 An enumerator initialized with the same value is an alias and invisible at the enumeration level, \ie the alias is substituted for its aliasee. 935 \begin{python} 936 class WeekD(Enum): Mon = 1; Tue = 2; Wed = 3; Thu = !10!; Fri = !10!; Sat = !10!; Sun = !10! 1089 937 \end{python} 1090 938 Here, the enumeration has only 4 enumerators and 3 aliases. 1091 939 An alias is only visible by dropping down to the @class@ level and asking for class members. 1092 @Enum@ only supports equality comparison between enumerator values; 1093 the extended class @OrderedEnum@ adds relational operators @<@, @<=@, @>@, and @>=@. 1094 1095 There are bidirectional enumeration pseudo-functions for label and value, but there is no concept of access using ordering (position). 940 Aliasing is prevented using the @unique@ decorator. 941 \begin{python} 942 !@unique! 943 class DupVal(Enum): One = 1; Two = 2; Three = !3!; Four = !3! 944 ValueError: duplicate values found in <enum 'DupVal'>: Four -> Three 945 \end{python} 946 947 \begin{lrbox}{\myboxA} 948 \begin{python} 949 def by_position(enum_type, position): 950 for index, value in enumerate(enum_type): 951 if position == index: return value 952 raise Exception("by_position out of range") 953 \end{python} 954 \end{lrbox} 955 There are bidirectional enumeration pseudo-functions for label and value, but there is no concept of access using ordering (position).\footnote{ 956 There is an $O(N)$ mechanism to access an enumerator's value by position. \newline \usebox\myboxA} 1096 957 \begin{cquote} 1097 958 \setlength{\tabcolsep}{15pt} 1098 959 \begin{tabular}{@{}ll@{}} 1099 960 \begin{python} 1100 Week.Thu.value == 10; 1101 Week.Thu.name == 'Thu'; 1102 \end{python} 1103 & 1104 \begin{python} 1105 Week( 10 ) == Thu 1106 Week['Thu'].value = 10 1107 \end{python} 1108 \end{tabular} 1109 \end{cquote} 961 Week.Thu.value == 4; 962 Week.Thu.name == "Thu"; 963 \end{python} 964 & 965 \begin{python} 966 Week( 4 ) == Week.Thu 967 Week["Thu"].value == 4 968 \end{python} 969 \end{tabular} 970 \end{cquote} 971 @Enum@ only supports equality comparison between enumerator values. 972 There are multiple library extensions to @Enum@, \eg @OrderedEnum@ recipe class, adding relational operators @<@, @<=@, @>@, and @>=@. 1110 973 1111 974 An enumeration \lstinline[language=python]{class} can have methods. 1112 975 \begin{python} 1113 class Week( Enum):976 class Week(!OrderedEnum!): 1114 977 Mon = 1; Tue = 2; Wed = 3; Thu = 4; Fri = 5; Sat = 6; Sun = 7 1115 $\\@$classmethod 1116 def today(cls, date): 1117 return cls(date.isoweekday()) 1118 print( "today:", Week.today(date.today())) 1119 today: Week.Mon 1120 \end{python} 1121 The method @today@ retrieves the day of the week and uses it as an index to print out the corresponding label of @Week@. 1122 1123 @Flag@ allows combining several members into a single variable: 1124 \begin{python} 1125 print( repr(WeekF.Sat | WeekF.Sun) ) 1126 <WeekF.Sun|Sat: 96> 1127 \end{python} 1128 You can even iterate over a @Flag@ variable: 1129 \begin{python} 978 def !isWeekday(self)!: # method 979 return Week(self.value) !<=! Week.Fri 980 def !isWeekend(self)!: # method 981 return Week.Sat !<=! Week(self.value) 982 \end{python} 983 984 An enumeration can be used in the @if@ and @switch@ statements but only for equality tests, unless extended to @OrderedEnum@. 985 \begin{cquote} 986 \setlength{\tabcolsep}{12pt} 987 \begin{tabular}{@{}ll@{}} 988 \begin{python} 989 if day <= Week.Fri : 990 print( "weekday" ); 991 992 993 994 \end{python} 995 & 996 \begin{python} 997 match day: 998 case Week.Mon | Week.Tue | Week.Wed | Week.Thu | Week.Fri: 999 print( "weekday" ); 1000 case Week.Sat | Week.Sun: 1001 print( "weekend" ); 1002 \end{python} 1003 \end{tabular} 1004 \end{cquote} 1005 Looping is performed using the enumeration type or @islice@ from @itertools@ based on position. 1006 \begin{python} 1007 for day in !Week!: $\C[2.25in]{\# Mon : 1 Tue : 2 Wed : 3 Thu : 4 Fri : 5 Sat : 6 Sun : 7}$ 1008 print( day.name, ":", day.value, end=" " ) 1009 for day in !islice(Week, 0, 5)!: $\C{\# Mon : 1 Tue : 2 Wed : 3 Thu : 4 Fri : 5}$ 1010 print( day.name, ":", day.value, end=" " ) 1011 for day in !islice(Week, 5, 7)!: $\C{\# Sat : 6 Sun : 7}$ 1012 print( day.name, ":", day.value, end=" " ) 1013 for day in !islice(Week,0, 7, 2)!: $\C{\# Mon : 1 Wed : 3 Fri : 5 Sun : 7}\CRT$ 1014 print( day.name, ":", day.value, end=" " ) 1015 \end{python} 1016 Iterating that includes alias names only (strings) is done using attribute @__members__@. 1017 \begin{python} 1018 for day in WeekD.__members__: 1019 print( day, ":", end=" " ) 1020 Mon : Tue : Wed : Thu : Fri : Sat : Sun 1021 \end{python} 1022 1023 Enumeration subclassing is allowed only if the enumeration base-class does not define any members. 1024 \begin{python} 1025 class WeekE(OrderedEnum): !pass!; # no members 1026 class WeekDay(WeekE): Mon = 1; Tue = 2; Wed = 3; Thu = 4; Fri = 5; 1027 class WeekEnd(WeekE): Sat = 6; Sun = 7 1028 \end{python} 1029 Here, type @WeekE@ is an abstract type because the dynamic typing never uses it. 1030 \begin{cquote} 1031 \setlength{\tabcolsep}{25pt} 1032 \begin{tabular}{@{}ll@{}} 1033 \begin{python} 1034 print( type(WeekE) ) 1035 day : WeekE = WeekDay.Fri # set type 1036 print( type(day), day ) 1037 day = WeekEnd.Sat # set type 1038 print( type(day), day ) 1039 \end{python} 1040 & 1041 \begin{python} 1042 <$class$ 'enum.EnumType'> 1043 1044 <enum 'WeekDay'> WeekDay.Fri 1045 1046 <enum 'WeekEnd'> WeekEnd.Sat 1047 \end{python} 1048 \end{tabular} 1049 \end{cquote} 1050 1051 There are a number of supplied enumeration base-types: @IntEnum@, @StrEnum@, @IntFalg@, @Flag@, which restrict the values in an enum using multi-inheritance. 1052 @IntEnum@ is a subclass of @int@ and @Enum@, allowing enumerator comparison to @int@ and other enumerators of this type (like C enumerators). 1053 @StrEnum@ is the same as @IntEnum@ but a subclass of the string type \lstinline[language=python]{str}. 1054 @IntFlag@, is a restricted subclass of @int@ where the enumerators can be combined using the bitwise operators (@&@, @|@, @^@, @~@) and the result is an @IntFlag@ member. 1055 @Flag@ is the same as @IntFlag@ but cannot be combined with, nor compared against, any other @Flag@ enumeration, nor @int@. 1056 Auto increment for @IntFlag@ and @Flag@ is by powers of 2. 1057 Enumerators that are a combinations of single bit enumerators are aliases, and hence, invisible. 1058 The following is an example for @Flag@. 1059 \begin{python} 1060 class WeekF(Flag): Mon = 1; Tue = 2; Wed = 4; Thu = !auto()!; Fri = 16; Sat = 32; Sun = 64; \ 1061 Weekday = Mon | Tue | Wed | Thu | Fri; \ 1062 Weekend = Sat | Sun 1063 print( f"0x{repr(WeekF.Weekday.value)} 0x{repr(WeekF.Weekend.value)}" ) 1064 0x31 0x96 1065 \end{python} 1066 It is possible to enumerate through a @Flag@ enumerator (no aliases): 1067 \begin{python} 1068 for day in WeekF: 1069 print( f"{day.name}: {day.value}", end=" ") 1070 Mon: 1 Tue: 2 Wed: 4 Thu: 8 Fri: 16 Sat: 32 Sun: 64 1071 \end{python} 1072 and a combined alias enumerator for @Flag@. 1073 \begin{cquote} 1074 \setlength{\tabcolsep}{15pt} 1075 \begin{tabular}{@{}ll@{}} 1076 \begin{python} 1077 weekday = WeekF.Weekday 1078 for day in weekday: 1079 print( f"{day.name}:" 1080 f" {day.value}", end=" " ) 1081 Mon: 1 Tue: 2 Wed: 4 Thu: 8 Fri: 16 1082 \end{python} 1083 & 1084 \begin{python} 1085 weekend = WeekF.Weekend 1130 1086 for day in weekend: 1131 print(day) 1132 WeekF.Sat 1133 WeekF.Sun 1134 \end{python} 1135 Okay, let's get some chores set up: 1136 \begin{python} 1137 >>> chores_for_ethan = { 1138 ... 'feed the cat': Week.MONDAY | Week.WEDNESDAY | Week.FRIDAY, 1139 ... 'do the dishes': Week.TUESDAY | Week.THURSDAY, 1140 ... 'answer SO questions': Week.SATURDAY, 1141 ... } 1142 \end{python} 1143 And a function to display the chores for a given day: 1144 \begin{python} 1145 >>> def show_chores(chores, day): 1146 ... for chore, days in chores.items(): 1147 ... if day in days: 1148 ... print(chore) 1149 >>> show_chores(chores_for_ethan, Week.SATURDAY) 1150 answer SO questions 1151 \end{python} 1152 Auto incrmenet for @Flag@ is by powers of 2. 1153 \begin{python} 1154 class WeekF(Flag): Mon = auto(); Tue = auto(); Wed = auto(); Thu = auto(); Fri = auto(); \ 1155 Sat = auto(); Sun = auto(); Weekend = Sat | Sun 1156 for d in WeekF: 1157 print( f"{d.name}: {d.value}", end=" ") 1158 Mon: 1 Tue: 2 Wed: 4 Thu: 8 Fri: 16 Sat: 32 Sun: 64 WeekA.Weekend 1159 \end{python} 1160 1161 \subsection{Programmatic access to enumeration members and their attributes} 1162 1163 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). 1164 @Enum@ allows such access: 1165 \begin{python} 1166 print(RGB(1), RGB(3), ) 1167 RGB.RED RGB.GREEN 1168 \end{python} 1169 If you want to access enum members by name, use item access: 1170 \begin{python} 1171 print( RGBa['RED'], RGBa['GREEN'] ) 1172 RGB.RED RGB.GREEN 1173 \end{python} 1174 If you have an enum member and need its name or value: 1175 \begin{python} 1176 member = RGBa.RED 1177 print( f"{member.name} {member.value}" ) 1178 RED 1 1179 \end{python} 1180 1181 1182 \subsection{Ensuring unique enumeration values} 1183 1184 By default, enumerations allow multiple names as aliases for the same value. 1185 When this behavior isn't desired, you can use the @unique()@ decorator: 1186 \begin{python} 1187 from enum import Enum, unique 1188 $@$unique 1189 class DupVal(Enum): ONE = 1; TWO = 2; THREE = 3; FOUR = 3 1190 ValueError: duplicate values found in <enum 'Mistake'>: FOUR -> THREE 1191 \end{python} 1192 1193 \subsection{Using automatic values} 1194 1195 If the exact value is unimportant you can use @auto@: 1196 \begin{python} 1197 from enum import Enum, auto 1198 class RGBa(Enum): RED = auto(); BLUE = auto(); GREEN = auto() 1199 \end{python} 1200 (Like Golang @iota@.) 1201 The values are chosen by @_generate_next_value_()@, which can be overridden: 1202 \begin{python} 1203 >>> class AutoName(Enum): 1204 ... $@$staticmethod 1205 ... def _generate_next_value_(name, start, count, last_values): 1206 ... return name 1207 ... 1208 >>> class Ordinal(AutoName): 1209 ... NORTH = auto() 1210 ... SOUTH = auto() 1211 ... EAST = auto() 1212 ... WEST = auto() 1213 ... 1214 >>> [member.value for member in Ordinal] 1215 ['NORTH', 'SOUTH', 'EAST', 'WEST'] 1216 \end{python} 1217 Note The @_generate_next_value_()@ method must be defined before any members. 1218 1219 \subsection{Iteration} 1220 1221 Iterating over the members of an enum does not provide the aliases: 1222 \begin{python} 1223 >>> list(Shape) 1224 [<Shape.SQUARE: 2>, <Shape.DIAMOND: 1>, <Shape.CIRCLE: 3>] 1225 >>> list(Week) 1226 [<Week.MONDAY: 1>, <Week.TUESDAY: 2>, <Week.WEDNESDAY: 4>, <Week.THURSDAY: 8>, 1227 <Week.FRIDAY: 16>, <Week.SATURDAY: 32>, <Week.SUNDAY: 64>] 1228 \end{python} 1229 Note that the aliases @Shape.ALIAS_FOR_SQUARE@ and @Week.WEEKEND@ aren't shown. 1230 1231 The special attribute @__members__@ is a read-only ordered mapping of names to members. 1232 It includes all names defined in the enumeration, including the aliases: 1233 \begin{python} 1234 >>> for name, member in Shape.__members__.items(): 1235 ... name, member 1236 ... 1237 ('SQUARE', <Shape.SQUARE: 2>) 1238 ('DIAMOND', <Shape.DIAMOND: 1>) 1239 ('CIRCLE', <Shape.CIRCLE: 3>) 1240 ('ALIAS_FOR_SQUARE', <Shape.SQUARE: 2>) 1241 \end{python} 1242 The @__members__@ attribute can be used for detailed programmatic access to the enumeration members. 1243 For example, finding all the aliases: 1244 \begin{python} 1245 >>> [name for name, member in Shape.__members__.items() if member.name != name] 1246 ['ALIAS_FOR_SQUARE'] 1247 \end{python} 1248 Note: Aliases for flags include values with multiple flags set, such as 3, and no flags set, i.e. 0. 1249 1250 \subsection{Comparisons} 1251 1252 Enumeration members are compared by identity: 1253 \begin{python} 1254 >>> Color.RED is Color.RED 1255 True 1256 >>> Color.RED is Color.BLUE 1257 False 1258 >>> Color.RED is not Color.BLUE 1259 True 1260 \end{python} 1261 Ordered comparisons between enumeration values are not supported. 1262 Enum members are not integers (but see @IntEnum@ below): 1263 \begin{python} 1264 >>> Color.RED < Color.BLUE 1265 Traceback (most recent call last): 1266 File "<stdin>", line 1, in <module> 1267 TypeError: '<' not supported between instances of 'Color' and 'Color' 1268 \end{python} 1269 Equality comparisons are defined though: 1270 \begin{python} 1271 >>> Color.BLUE == Color.RED 1272 False 1273 >>> Color.BLUE != Color.RED 1274 True 1275 >>> Color.BLUE == Color.BLUE 1276 True 1277 \end{python} 1278 Comparisons against non-enumeration values will always compare not equal (again, @IntEnum@ was explicitly designed to behave differently, see below): 1279 \begin{python} 1280 >>> Color.BLUE == 2 1281 False 1282 \end{python} 1283 1284 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. 1285 1286 \subsection{Allowed members and attributes of enumerations} 1287 1288 Most of the examples above use integers for enumeration values. 1289 Using integers is short and handy (and provided by default by the Functional API), but not strictly enforced. 1290 In the vast majority of use-cases, one doesn't care what the actual value of an enumeration is. 1291 But if the value is important, enumerations can have arbitrary values. 1292 1293 Enumerations are Python classes, and can have methods and special methods as usual. If we have this enumeration: 1294 \begin{python} 1295 >>> class Mood(Enum): 1296 ... FUNKY = 1 1297 ... HAPPY = 3 1298 ... 1299 ... def describe(self): 1300 ... # self is the member here 1301 ... return self.name, self.value 1302 ... 1303 ... def __str__(self): 1304 ... return 'my custom str! {0}'.format(self.value) 1305 ... 1306 ... $@$classmethod 1307 ... 1308 ... def favorite_mood(cls): 1309 ... # cls here is the enumeration 1310 ... return cls.HAPPY 1311 ... 1312 \end{python} 1313 Then: 1314 \begin{python} 1315 >>> Mood.favorite_mood() 1316 <Mood.HAPPY: 3> 1317 >>> Mood.HAPPY.describe() 1318 ('HAPPY', 3) 1319 >>> str(Mood.FUNKY) 1320 'my custom str! 1' 1321 \end{python} 1322 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; 1323 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_@. 1324 1325 Note: if your enumeration defines @__new__()@ and/or @__init__()@, any value(s) given to the enum member will be passed into those methods. 1326 See Planet for an example. 1327 1328 Note: The @__new__()@ method, if defined, is used during creation of the Enum members; 1329 it is then replaced by Enum's @__new__()@ which is used after class creation for lookup of existing members. 1330 See When to use @__new__()@ vs. @__init__()@ for more details. 1331 1332 \subsection{Restricted Enum subclassing} 1333 1334 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. 1335 The order of these base classes is: 1336 \begin{python} 1337 class EnumName([mix-in, ...,] [data-type,] base-enum): 1338 pass 1339 \end{python} 1340 Also, subclassing an enumeration is allowed only if the enumeration does not define any members. 1341 So this is forbidden: 1342 \begin{python} 1343 >>> class MoreColor(Color): 1344 ... PINK = 17 1345 ... 1346 Traceback (most recent call last): 1347 ... 1348 TypeError: <enum 'MoreColor'> cannot extend <enum 'Color'> 1349 \end{python} 1350 But this is allowed: 1351 \begin{python} 1352 >>> class Foo(Enum): 1353 ... def some_behavior(self): 1354 ... pass 1355 ... 1356 >>> class Bar(Foo): 1357 ... HAPPY = 1 1358 ... SAD = 2 1359 ... 1360 \end{python} 1361 Allowing subclassing of enums that define members would lead to a violation of some important invariants of types and instances. 1362 On the other hand, it makes sense to allow sharing some common behavior between a group of enumerations. (See OrderedEnum for an example.) 1363 1364 \subsection{Dataclass support} 1365 1366 When inheriting from a @dataclass@, the @__repr__()@ omits the inherited class' name. 1367 For example: 1368 \begin{python} 1369 >>> from dataclasses import dataclass, field 1370 >>> $@$dataclass 1371 ... class CreatureDataMixin: 1372 ... size: str 1373 ... legs: int 1374 ... tail: bool = field(repr=False, default=True) 1375 ... 1376 >>> class Creature(CreatureDataMixin, Enum): 1377 ... BEETLE = 'small', 6 1378 ... DOG = 'medium', 4 1379 ... 1380 >>> Creature.DOG 1381 <Creature.DOG: size='medium', legs=4> 1382 \end{python} 1383 Use the @dataclass()@ argument repr=False to use the standard @repr()@. 1384 1385 Changed in version 3.12: Only the dataclass fields are shown in the value area, not the dataclass' name. 1386 1387 \subsection{Pickling} 1388 1389 Enumerations can be pickled and unpickled: 1390 \begin{python} 1391 >>> from test.test_enum import Fruit 1392 >>> from pickle import dumps, loads 1393 >>> Fruit.TOMATO is loads(dumps(Fruit.TOMATO)) 1394 True 1395 \end{python} 1396 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. 1397 1398 Note: With pickle protocol version 4 it is possible to easily pickle enums nested in other classes. 1399 1400 It is possible to modify how enum members are pickled/unpickled by defining @__reduce_ex__()@ in the enumeration class. 1401 The default method is by-value, but enums with complicated values may want to use by-name: 1402 \begin{python} 1403 >>> import enum 1404 >>> class MyEnum(enum.Enum): 1405 ... __reduce_ex__ = enum.pickle_by_enum_name 1406 \end{python} 1407 Note: Using by-name for flags is not recommended, as unnamed aliases will not unpickle. 1408 1409 \subsection{Functional API} 1410 1411 The @Enum@ class is callable, providing the following functional API: 1412 \begin{python} 1413 >>> Animal = Enum('Animal', 'ANT BEE CAT DOG') 1414 >>> Animal 1415 <enum 'Animal'> 1416 >>> Animal.ANT 1417 <Animal.ANT: 1> 1418 >>> list(Animal) 1419 [<Animal.ANT: 1>, <Animal.BEE: 2>, <Animal.CAT: 3>, <Animal.DOG: 4>] 1420 \end{python} 1421 The semantics of this API resemble @namedtuple@. 1422 The first argument of the call to @Enum@ is the name of the enumeration. 1423 1424 The second argument is the source of enumeration member names. 1425 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. 1426 The last two options enable assigning arbitrary values to enumerations; 1427 the others auto-assign increasing integers starting with 1 (use the @start@ parameter to specify a different starting value). 1428 A new class derived from @Enum@ is returned. 1429 In other words, the above assignment to Animal is equivalent to: 1430 \begin{python} 1431 >>> class Animal(Enum): 1432 ... ANT = 1 1433 ... BEE = 2 1434 ... CAT = 3 1435 ... DOG = 4 1436 ... 1437 \end{python} 1438 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@. 1439 1440 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). 1441 The solution is to specify the module name explicitly as follows: 1442 \begin{python} 1443 >>> Animal = Enum('Animal', 'ANT BEE CAT DOG', module=__name__) 1444 \end{python} 1445 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. 1446 1447 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. 1448 For example, if the class was made available in class SomeData in the global scope: 1449 \begin{python} 1450 >>> Animal = Enum('Animal', 'ANT BEE CAT DOG', qualname='SomeData.Animal') 1451 \end{python} 1452 The complete signature is: 1453 \begin{python} 1454 Enum( 1455 value='NewEnumName', 1456 names=<...>, 1457 *, 1458 module='...', 1459 qualname='...', 1460 type=<mixed-in class>, 1461 start=1, 1462 ) 1463 \end{python} 1464 \begin{itemize} 1465 \item 1466 @value@: What the new enum class will record as its name. 1467 \item 1468 @names@: The enum members. 1469 This can be a whitespace- or comma-separated string (values will start at 1 unless otherwise specified): 1470 \begin{python} 1471 'RED GREEN BLUE' | 'RED,GREEN,BLUE' | 'RED, GREEN, BLUE' 1472 \end{python} 1473 or an iterator of names: 1474 \begin{python} 1475 ['RED', 'GREEN', 'BLUE'] 1476 \end{python} 1477 or an iterator of (name, value) pairs: 1478 \begin{python} 1479 [('CYAN', 4), ('MAGENTA', 5), ('YELLOW', 6)] 1480 \end{python} 1481 or a mapping: 1482 \begin{python} 1483 {'CHARTREUSE': 7, 'SEA_GREEN': 11, 'ROSEMARY': 42} 1484 \end{python} 1485 \item 1486 module: name of module where new enum class can be found. 1487 \item 1488 @qualname@: where in module new enum class can be found. 1489 \item 1490 @type@: type to mix in to new enum class. 1491 \item 1492 @start@: number to start counting at if only names are passed in. 1493 \end{itemize} 1494 Changed in version 3.5: The start parameter was added. 1495 1496 \subsection{Derived Enumerations} 1497 1498 \subsection{IntEnum} 1499 1500 The first variation of @Enum@ that is provided is also a subclass of @int@. 1501 Members of an @IntEnum@ can be compared to integers; 1502 by extension, integer enumerations of different types can also be compared to each other: 1503 \begin{python} 1504 >>> from enum import IntEnum 1505 >>> class Shape(IntEnum): 1506 ... CIRCLE = 1 1507 ... SQUARE = 2 1508 ... 1509 >>> class Request(IntEnum): 1510 ... POST = 1 1511 ... GET = 2 1512 ... 1513 >>> Shape == 1 1514 False 1515 >>> Shape.CIRCLE == 1 1516 True 1517 >>> Shape.CIRCLE == Request.POST 1518 True 1519 \end{python} 1520 However, they still can't be compared to standard @Enum@ enumerations: 1521 \begin{python} 1522 >>> class Shape(IntEnum): 1523 ... CIRCLE = 1 1524 ... SQUARE = 2 1525 ... 1526 >>> class Color(Enum): 1527 ... RED = 1 1528 ... GREEN = 2 1529 ... 1530 >>> Shape.CIRCLE == Color.RED 1531 False 1532 \end{python} 1533 @IntEnum@ values behave like integers in other ways you'd expect: 1534 \begin{python} 1535 >>> int(Shape.CIRCLE) 1536 1 1537 >>> ['a', 'b', 'c'][Shape.CIRCLE] 1538 'b' 1539 >>> [i for i in range(Shape.SQUARE)] 1540 [0, 1] 1541 \end{python} 1542 1543 \subsection{StrEnum} 1544 1545 The second variation of @Enum@ that is provided is also a subclass of @str@. 1546 Members of a @StrEnum@ can be compared to strings; 1547 by extension, string enumerations of different types can also be compared to each other. 1548 1549 New in version 3.11. 1550 1551 \subsection{IntFlag} 1552 1553 The next variation of @Enum@ provided, @IntFlag@, is also based on @int@. 1554 The difference being @IntFlag@ members can be combined using the bitwise operators (@&, |, ^, ~@) and the result is still an @IntFlag@ member, if possible. 1555 Like @IntEnum@, @IntFlag@ members are also integers and can be used wherever an int is used. 1556 1557 Note: Any operation on an IntFlag member besides the bit-wise operations will lose the @IntFlag@ membership. 1558 1559 Bit-wise operations that result in invalid @IntFlag@ values will lose the @IntFlag@ membership. 1560 See @FlagBoundary@ for details. 1561 1562 New in version 3.6. 1563 1564 Changed in version 3.11. 1565 1566 Sample @IntFlag@ class: 1567 \begin{python} 1568 >>> from enum import IntFlag 1569 >>> class Perm(IntFlag): 1570 ... R = 4 1571 ... W = 2 1572 ... X = 1 1573 ... 1574 >>> Perm.R | Perm.W 1575 <Perm.R|W: 6> 1576 >>> Perm.R + Perm.W 1577 6 1578 >>> RW = Perm.R | Perm.W 1579 >>> Perm.R in RW 1580 True 1581 \end{python} 1582 It is also possible to name the combinations: 1583 \begin{python} 1584 >>> class Perm(IntFlag): 1585 ... R = 4 1586 ... W = 2 1587 ... X = 1 1588 ... RWX = 7 1589 ... 1590 >>> Perm.RWX 1591 <Perm.RWX: 7> 1592 >>> ~Perm.RWX 1593 <Perm: 0> 1594 >>> Perm(7) 1595 <Perm.RWX: 7> 1596 \end{python} 1597 Note: Named combinations are considered aliases. Aliases do not show up during iteration, but can be returned from by-value lookups. 1598 1599 Changed in version 3.11. 1600 1601 Another important difference between @IntFlag@ and @Enum@ is that if no flags are set (the value is 0), its boolean evaluation is @False@: 1602 \begin{python} 1603 >>> Perm.R & Perm.X 1604 <Perm: 0> 1605 >>> bool(Perm.R & Perm.X) 1606 False 1607 \end{python} 1608 Because @IntFlag@ members are also subclasses of int they can be combined with them (but may lose @IntFlag@ membership: 1609 \begin{python} 1610 >>> Perm.X | 4 1611 <Perm.R|X: 5> 1612 1613 >>> Perm.X + 8 1614 9 1615 \end{python} 1616 Note: The negation operator, @~@, always returns an @IntFlag@ member with a positive value: 1617 \begin{python} 1618 >>> (~Perm.X).value == (Perm.R|Perm.W).value == 6 1619 True 1620 \end{python} 1621 @IntFlag@ members can also be iterated over: 1622 \begin{python} 1623 >>> list(RW) 1624 [<Perm.R: 4>, <Perm.W: 2>] 1625 \end{python} 1626 New in version 3.11. 1627 1628 \subsection{Flag} 1629 1630 The last variation is @Flag@. 1631 Like @IntFlag@, @Flag@ members can be combined using the bitwise operators (@&, |, ^, ~@). 1632 Unlike @IntFlag@, they cannot be combined with, nor compared against, any other @Flag@ enumeration, nor @int@. 1633 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. 1634 1635 New in version 3.6. 1636 1637 Like @IntFlag@, if a combination of @Flag@ members results in no flags being set, the boolean evaluation is @False@: 1638 \begin{python} 1639 >>> from enum import Flag, auto 1640 >>> class Color(Flag): 1641 ... RED = auto() 1642 ... BLUE = auto() 1643 ... GREEN = auto() 1644 ... 1645 >>> Color.RED & Color.GREEN 1646 <Color: 0> 1647 >>> bool(Color.RED & Color.GREEN) 1648 False 1649 \end{python} 1650 Individual flags should have values that are powers of two (1, 2, 4, 8, ...), while combinations of flags will not: 1651 \begin{python} 1652 >>> class Color(Flag): 1653 ... RED = auto() 1654 ... BLUE = auto() 1655 ... GREEN = auto() 1656 ... WHITE = RED | BLUE | GREEN 1657 ... 1658 >>> Color.WHITE 1659 <Color.WHITE: 7> 1660 \end{python} 1661 Giving a name to the ``no flags set'' condition does not change its boolean value: 1662 \begin{python} 1663 >>> class Color(Flag): 1664 ... BLACK = 0 1665 ... RED = auto() 1666 ... BLUE = auto() 1667 ... GREEN = auto() 1668 ... 1669 >>> Color.BLACK 1670 <Color.BLACK: 0> 1671 >>> bool(Color.BLACK) 1672 False 1673 \end{python} 1674 @Flag@ members can also be iterated over: 1675 \begin{python} 1676 >>> purple = Color.RED | Color.BLUE 1677 >>> list(purple) 1678 [<Color.RED: 1>, <Color.BLUE: 2>] 1679 \end{python} 1680 New in version 3.11. 1681 1682 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). 1683 @IntEnum@ and @IntFlag@ should be used only in cases where @Enum@ and @Flag@ will not do; 1684 for example, when integer constants are replaced with enumerations, or for interoperability with other systems. 1685 1686 \subsection{Others} 1687 1688 While @IntEnum@ is part of the enum module, it would be very simple to implement independently: 1689 \begin{python} 1690 class IntEnum(int, Enum): 1691 pass 1692 \end{python} 1693 This demonstrates how similar derived enumerations can be defined; 1694 for example a @FloatEnum@ that mixes in float instead of @int@. 1695 1696 Some rules: 1697 \begin{itemize} 1698 \item 1699 When subclassing @Enum@, mix-in types must appear before @Enum@ itself in the sequence of bases, as in the @IntEnum@ example above. 1700 \item 1701 Mix-in types must be subclassable. 1702 For example, @bool@ and @range@ are not subclassable and will throw an error during Enum creation if used as the mix-in type. 1703 \item 1704 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. 1705 This restriction does not apply to mix-ins which only add methods and don't specify another type. 1706 \item 1707 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. 1708 \item 1709 A data type is a mixin that defines @__new__()@, or a @dataclass@ 1710 \item 1711 \%-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. 1712 \item 1713 Formatted string literals, @str.format()@, and format() will use the enum's @__str__()@ method. 1714 \end{itemize} 1715 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. 1716 1717 \subsection{When to use \lstinline{__new__()} vs. \lstinline{__init__()}} 1718 1719 @__new__()@ must be used whenever you want to customize the actual value of the @Enum@ member. 1720 Any other modifications may go in either @__new__()@ or @__init__()@, with @__init__()@ being preferred. 1721 1722 For example, if you want to pass several items to the constructor, but only want one of them to be the value: 1723 \begin{python} 1724 >>> class Coordinate(bytes, Enum): 1725 ... """ 1726 ... Coordinate with binary codes that can be indexed by the int code. 1727 ... """ 1728 ... def __new__(cls, value, label, unit): 1729 ... obj = bytes.__new__(cls, [value]) 1730 ... obj._value_ = value 1731 ... obj.label = label 1732 ... obj.unit = unit 1733 ... return obj 1734 ... PX = (0, 'P.X', 'km') 1735 ... PY = (1, 'P.Y', 'km') 1736 ... VX = (2, 'V.X', 'km/s') 1737 ... VY = (3, 'V.Y', 'km/s') 1738 1739 >>> print(Coordinate['PY']) 1740 Coordinate.PY 1741 1742 >>> print(Coordinate(3)) 1743 Coordinate.VY 1744 \end{python} 1745 Warning: Do not call @super().__new__()@, as the lookup-only @__new__@ is the one that is found; instead, use the data type directly. 1746 1747 \subsection{Finer Points} 1748 1749 Supported @__dunder__@ names 1750 1751 @__members__@ is a read-only ordered mapping of member\_name:member items. It is only available on the class. 1752 1753 @__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. 1754 Supported @_sunder_@ names 1755 \begin{itemize} 1756 \item 1757 @_name_@ -- name of the member 1758 \item 1759 @_value_@ -- value of the member; can be set / modified in @__new__@ 1760 \item 1761 @_missing_@ -- a lookup function used when a value is not found; may be overridden 1762 \item 1763 @_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 1764 \item 1765 @_order_@ -- used in Python 2/3 code to ensure member order is consistent (class attribute, removed during class creation) 1766 \item 1767 @_generate_@next@_value_@ -- used by the Functional API and by @auto@ to get an appropriate value for an enum member; may be overridden 1768 \end{itemize} 1769 Note: For standard @Enum@ classes the next value chosen is the last value seen incremented by one. 1770 1771 For @Flag@ classes the next value chosen will be the next highest power-of-two, regardless of the last value seen. 1772 1773 New in version 3.6: @_missing_@, @_order_@, @_generate_@next@_value_@ 1774 1775 New in version 3.7: @_ignore_@ 1776 1777 To help keep Python 2 / Python 3 code in sync an @_order_@ attribute can be provided. 1778 It will be checked against the actual order of the enumeration and raise an error if the two do not match: 1779 \begin{python} 1780 >>> class Color(Enum): 1781 ... _order_ = 'RED GREEN BLUE' 1782 ... RED = 1 1783 ... BLUE = 3 1784 ... GREEN = 2 1785 ... 1786 Traceback (most recent call last): 1787 ... 1788 TypeError: member order does not match _order_: 1789 ['RED', 'BLUE', 'GREEN'] 1790 ['RED', 'GREEN', 'BLUE'] 1791 \end{python} 1792 Note: In Python 2 code the @_order_@ attribute is necessary as definition order is lost before it can be recorded. 1793 1794 \subsection{\lstinline{_Private__names}} 1795 1796 Private names are not converted to enum members, but remain normal attributes. 1797 1798 Changed in version 3.11. 1799 1800 \subsection{\lstinline{Enum} member type} 1801 1802 @Enum@ members are instances of their enum class, and are normally accessed as @EnumClass.member@. 1803 In certain situations, such as writing custom enum behavior, being able to access one member directly from another is useful, and is supported; 1804 however, in order to avoid name clashes between member names and attributes/methods from mixed-in classes, upper-case names are strongly recommended. 1805 1806 Changed in version 3.5. 1807 1808 \subsection{Creating members that are mixed with other data types} 1809 1810 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: 1811 \begin{python} 1812 >>> class MyEnum(IntEnum): # help(int) -> int(x, base=10) -> integer 1813 ... example = '11', 16 # so x='11' and base=16 1814 ... 1815 MyEnum.example.value # and hex(11) is... 1816 17 1817 \end{python} 1818 1819 \subsection{\lstinline{Boolean} value of \lstinline{Enum} classes and members} 1820 1821 Enum classes that are mixed with non-@Enum@ types (such as @int@, @str@, etc.) are evaluated according to the mixed-in type's rules; 1822 otherwise, all members evaluate as @True@. 1823 To make your own enum's boolean evaluation depend on the member's value add the following to your class: 1824 \begin{python} 1825 def __bool__(self): 1826 return bool(self.value) 1827 \end{python} 1828 Plain @Enum@ classes always evaluate as @True@. 1829 1830 \subsection{\lstinline{Enum} classes with methods} 1831 1832 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: 1833 \begin{python} 1834 >>> dir(Planet) 1835 ['EARTH', 'JUPITER', 'MARS', 'MERCURY', 'NEPTUNE', 'SATURN', 'URANUS', 'VENUS', 1836 '__class__', '__doc__', '__members__', '__module__'] 1837 >>> dir(Planet.EARTH) 1838 ['__class__', '__doc__', '__module__', 'mass', 'name', 'radius', 'surface_gravity', 'value'] 1839 \end{python} 1840 1841 \subsection{Combining members of \lstinline{Flag}} 1842 1843 Iterating over a combination of @Flag@ members will only return the members that are comprised of a single bit: 1844 \begin{python} 1845 >>> class Color(Flag): 1846 ... RED = auto() 1847 ... GREEN = auto() 1848 ... BLUE = auto() 1849 ... MAGENTA = RED | BLUE 1850 ... YELLOW = RED | GREEN 1851 ... CYAN = GREEN | BLUE 1852 ... 1853 >>> Color(3) # named combination 1854 <Color.YELLOW: 3> 1855 >>> Color(7) # not named combination 1856 <Color.RED|GREEN|BLUE: 7> 1857 \end{python} 1858 1859 \subsection{\lstinline{Flag} and \lstinline{IntFlag} minutia} 1860 1861 Using the following snippet for our examples: 1862 \begin{python} 1863 >>> class Color(IntFlag): 1864 ... BLACK = 0 1865 ... RED = 1 1866 ... GREEN = 2 1867 ... BLUE = 4 1868 ... PURPLE = RED | BLUE 1869 ... WHITE = RED | GREEN | BLUE 1870 ... 1871 \end{python} 1872 the following are true: 1873 \begin{itemize} 1874 \item 1875 single-bit flags are canonical 1876 \item 1877 multi-bit and zero-bit flags are aliases 1878 \item 1879 only canonical flags are returned during iteration: 1880 \begin{python} 1881 >>> list(Color.WHITE) 1882 [<Color.RED: 1>, <Color.GREEN: 2>, <Color.BLUE: 4>] 1883 \end{python} 1884 negating a flag or flag set returns a new flag/flag set with the corresponding positive integer value: 1885 \begin{python} 1886 >>> Color.BLUE 1887 <Color.BLUE: 4> 1888 1889 >>> ~Color.BLUE 1890 <Color.RED|GREEN: 3> 1891 \end{python} 1892 \item 1893 names of pseudo-flags are constructed from their members' names: 1894 \begin{python} 1895 >>> (Color.RED | Color.GREEN).name 1896 'RED|GREEN' 1897 \end{python} 1898 \item 1899 multi-bit flags, aka aliases, can be returned from operations: 1900 \begin{python} 1901 >>> Color.RED | Color.BLUE 1902 <Color.PURPLE: 5> 1903 1904 >>> Color(7) # or Color(-1) 1905 <Color.WHITE: 7> 1906 1907 >>> Color(0) 1908 <Color.BLACK: 0> 1909 \end{python} 1910 \item 1911 membership / containment checking: zero-valued flags are always considered to be contained: 1912 \begin{python} 1913 >>> Color.BLACK in Color.WHITE 1914 True 1915 \end{python} 1916 otherwise, only if all bits of one flag are in the other flag will True be returned: 1917 \begin{python} 1918 >>> Color.PURPLE in Color.WHITE 1919 True 1920 1921 >>> Color.GREEN in Color.PURPLE 1922 False 1923 \end{python} 1924 \end{itemize} 1925 There is a new boundary mechanism that controls how out-of-range / invalid bits are handled: @STRICT@, @CONFORM@, @EJECT@, and @KEEP@: 1926 \begin{itemize} 1927 \item 1928 @STRICT@ --> raises an exception when presented with invalid values 1929 \item 1930 @CONFORM@ --> discards any invalid bits 1931 \item 1932 @EJECT@ --> lose Flag status and become a normal int with the given value 1933 \item 1934 @KEEP@ --> keep the extra bits 1935 \begin{itemize} 1936 \item 1937 keeps Flag status and extra bits 1938 \item 1939 extra bits do not show up in iteration 1940 \item 1941 extra bits do show up in repr() and str() 1942 \end{itemize} 1943 \end{itemize} 1944 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). 1945 1946 \section{How are Enums and Flags different?} 1947 1948 Enums have a custom metaclass that affects many aspects of both derived @Enum@ classes and their instances (members). 1949 1950 \subsection{Enum Classes} 1951 1952 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@. 1953 @EnumType@ is responsible for ensuring that various other methods on the final @Enum@ class are correct (such as @__new__()@, @__getnewargs__()@, @__str__()@ and @__repr__()@). 1954 1955 \subsection{Flag Classes} 1956 1957 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. 1958 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. 1959 1960 \subsection{Enum Members (aka instances)} 1961 1962 The most interesting thing about enum members is that they are singletons. 1963 @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. 1964 1965 \subsection{Flag Members} 1966 1967 Flag members can be iterated over just like the @Flag@ class, and only the canonical members will be returned. 1968 For example: 1969 \begin{python} 1970 >>> list(Color) 1971 [<Color.RED: 1>, <Color.GREEN: 2>, <Color.BLUE: 4>] 1972 \end{python} 1973 (Note that BLACK, PURPLE, and WHITE do not show up.) 1974 1975 Inverting a flag member returns the corresponding positive value, rather than a negative value -- for example: 1976 \begin{python} 1977 >>> ~Color.RED 1978 <Color.GREEN|BLUE: 6> 1979 \end{python} 1980 Flag members have a length corresponding to the number of power-of-two values they contain. For example: 1981 \begin{python} 1982 >>> len(Color.PURPLE) 1983 2 1984 \end{python} 1985 1986 \subsection{Enum Cookbook} 1987 1988 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. 1989 1990 \subsection{Omitting values} 1991 1992 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: 1993 \begin{itemize} 1994 \item 1995 use instances of auto for the value 1996 \item 1997 use instances of object as the value 1998 \item 1999 use a descriptive string as the value 2000 \item 2001 use a tuple as the value and a custom @__new__()@ to replace the tuple with an @int@ value 2002 \end{itemize} 2003 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. 2004 2005 \subsection{Using \lstinline{auto}} 2006 2007 Using @auto@ would look like: 2008 \begin{python} 2009 >>> class Color(Enum): 2010 ... RED = auto() 2011 ... BLUE = auto() 2012 ... GREEN = auto() 2013 ... 2014 >>> Color.GREEN 2015 <Color.GREEN: 3> 2016 \end{python} 2017 2018 \subsection{Using \lstinline{object}} 2019 2020 Using @object@ would look like: 2021 \begin{python} 2022 >>> class Color(Enum): 2023 ... RED = object() 2024 ... GREEN = object() 2025 ... BLUE = object() 2026 ... 2027 >>> Color.GREEN 2028 <Color.GREEN: <object object at 0x...>> 2029 \end{python} 2030 This is also a good example of why you might want to write your own @__repr__()@: 2031 \begin{python} 2032 >>> class Color(Enum): 2033 ... RED = object() 2034 ... GREEN = object() 2035 ... BLUE = object() 2036 ... def __repr__(self): 2037 ... return "<%s.%s>" % (self.__class__.__name__, self._name_) 2038 ... 2039 >>> Color.GREEN 2040 <Color.GREEN> 2041 \end{python} 2042 2043 \subsection{Using a descriptive string} 2044 2045 Using a string as the value would look like: 2046 \begin{python} 2047 >>> class Color(Enum): 2048 ... RED = 'stop' 2049 ... GREEN = 'go' 2050 ... BLUE = 'too fast!' 2051 ... 2052 >>> Color.GREEN 2053 <Color.GREEN: 'go'> 2054 \end{python} 2055 2056 \subsection{Using a custom \lstinline{__new__()}} 2057 2058 Using an auto-numbering @__new__()@ would look like: 2059 \begin{python} 2060 >>> class AutoNumber(Enum): 2061 ... def __new__(cls): 2062 ... value = len(cls.__members__) + 1 2063 ... obj = object.__new__(cls) 2064 ... obj._value_ = value 2065 ... return obj 2066 ... 2067 >>> class Color(AutoNumber): 2068 ... RED = () 2069 ... GREEN = () 2070 ... BLUE = () 2071 ... 2072 >>> Color.GREEN 2073 <Color.GREEN: 2> 2074 \end{python} 2075 To make a more general purpose @AutoNumber@, add @*args@ to the signature: 2076 \begin{python} 2077 >>> class AutoNumber(Enum): 2078 ... def __new__(cls, *args): # this is the only change from above 2079 ... value = len(cls.__members__) + 1 2080 ... obj = object.__new__(cls) 2081 ... obj._value_ = value 2082 ... return obj 2083 \end{python} 2084 Then when you inherit from @AutoNumber@ you can write your own @__init__@ to handle any extra arguments: 2085 \begin{python} 2086 >>> class Swatch(AutoNumber): 2087 ... def __init__(self, pantone='unknown'): 2088 ... self.pantone = pantone 2089 ... AUBURN = '3497' 2090 ... SEA_GREEN = '1246' 2091 ... BLEACHED_CORAL = () # New color, no Pantone code yet! 2092 ... 2093 >>> Swatch.SEA_GREEN 2094 <Swatch.SEA_GREEN: 2> 2095 >>> Swatch.SEA_GREEN.pantone 2096 '1246' 2097 >>> Swatch.BLEACHED_CORAL.pantone 2098 'unknown' 2099 \end{python} 2100 Note: The @__new__()@ method, if defined, is used during creation of the Enum members; 2101 it is then replaced by Enum's @__new__()@ which is used after class creation for lookup of existing members. 2102 2103 Warning: Do not call @super().__new__()@, as the lookup-only @__new__@ is the one that is found; 2104 instead, use the data type directly -- e.g.: 2105 \begin{python} 2106 obj = int.__new__(cls, value) 2107 \end{python} 2108 2109 \subsection{OrderedEnum} 2110 2111 An ordered enumeration that is not based on @IntEnum@ and so maintains the normal @Enum@ invariants (such as not being comparable to other enumerations): 2112 \begin{python} 2113 >>> class OrderedEnum(Enum): 2114 ... def __ge__(self, other): 2115 ... if self.__class__ is other.__class__: 2116 ... return self.value >= other.value 2117 ... return NotImplemented 2118 ... def __gt__(self, other): 2119 ... if self.__class__ is other.__class__: 2120 ... return self.value > other.value 2121 ... return NotImplemented 2122 ... def __le__(self, other): 2123 ... if self.__class__ is other.__class__: 2124 ... return self.value <= other.value 2125 ... return NotImplemented 2126 ... def __lt__(self, other): 2127 ... if self.__class__ is other.__class__: 2128 ... return self.value < other.value 2129 ... return NotImplemented 2130 ... 2131 >>> class Grade(OrderedEnum): 2132 ... A = 5 2133 ... B = 4 2134 ... C = 3 2135 ... D = 2 2136 ... F = 1 2137 >>> Grade.C < Grade.A 2138 True 2139 \end{python} 2140 2141 \subsection{DuplicateFreeEnum} 2142 2143 Raises an error if a duplicate member value is found instead of creating an alias: 2144 \begin{python} 2145 >>> class DuplicateFreeEnum(Enum): 2146 ... def __init__(self, *args): 2147 ... cls = self.__class__ 2148 ... if any(self.value == e.value for e in cls): 2149 ... a = self.name 2150 ... e = cls(self.value).name 2151 ... raise ValueError( 2152 ... "aliases not allowed in DuplicateFreeEnum: %r --> %r" 2153 ... % (a, e)) 2154 >>> class Color(DuplicateFreeEnum): 2155 ... RED = 1 2156 ... GREEN = 2 2157 ... BLUE = 3 2158 ... GRENE = 2 2159 ... 2160 Traceback (most recent call last): 2161 ... 2162 ValueError: aliases not allowed in DuplicateFreeEnum: 'GRENE' --> 'GREEN' 2163 \end{python} 2164 Note: This is a useful example for subclassing Enum to add or change other behaviors as well as disallowing aliases. 2165 If the only desired change is disallowing aliases, the @unique()@ decorator can be used instead. 2166 2167 \subsection{Planet} 2168 2169 If @__new__()@ or @__init__()@ is defined, the value of the enum member will be passed to those methods: 2170 \begin{figure} 2171 \begin{python} 2172 from enum import Enum 2173 class Planet(Enum): 2174 MERCURY = ( 3.303E23, 2.4397E6 ) 2175 VENUS = ( 4.869E24, 6.0518E6 ) 2176 EARTH = (5.976E24, 6.37814E6) 2177 MARS = (6.421E23, 3.3972E6) 2178 JUPITER = (1.9E27, 7.1492E7) 2179 SATURN = (5.688E26, 6.0268E7) 2180 URANUS = (8.686E25, 2.5559E7) 2181 NEPTUNE = (1.024E26, 2.4746E7) 2182 def __init__( self, mass, radius ): 2183 self.mass = mass # in kilograms 2184 self.radius = radius # in meters 2185 def surface_gravity( self ): 2186 # universal gravitational constant (m3 kg-1 s-2) 2187 G = 6.67300E-11 2188 return G * self.mass / (self.radius * self.radius) 2189 for p in Planet: 2190 print( f"{p.name}: {p.value}" ) 2191 2192 MERCURY: (3.303e+23, 2439700.0) 2193 VENUS: (4.869e+24, 6051800.0) 2194 EARTH: (5.976e+24, 6378140.0) 2195 MARS: (6.421e+23, 3397200.0) 2196 JUPITER: (1.9e+27, 71492000.0) 2197 SATURN: (5.688e+26, 60268000.0) 2198 URANUS: (8.686e+25, 25559000.0) 2199 NEPTUNE: (1.024e+26, 24746000.0) 2200 \end{python} 2201 \caption{Python Planet Example} 2202 \label{f:PythonPlanetExample} 2203 \end{figure} 2204 2205 2206 \subsection{TimePeriod} 2207 2208 An example to show the @_ignore_@ attribute in use: 2209 \begin{python} 2210 >>> from datetime import timedelta 2211 >>> class Period(timedelta, Enum): 2212 ... "different lengths of time" 2213 ... _ignore_ = 'Period i' 2214 ... Period = vars() 2215 ... for i in range(367): 2216 ... Period['day_%d' % i] = i 2217 ... 2218 >>> list(Period)[:2] 2219 [<Period.day_0: datetime.timedelta(0)>, <Period.day_1: datetime.timedelta(days=1)>] 2220 >>> list(Period)[-2:] 2221 [<Period.day_365: datetime.timedelta(days=365)>, <Period.day_366: datetime.timedelta(days=366)>] 2222 \end{python} 2223 2224 \subsection{Subclassing EnumType} 2225 2226 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. 1087 print( f"{day.name}:" 1088 f" {day.value}", end=" " ) 1089 Sat: 32 Sun: 64 1090 \end{python} 1091 \end{tabular} 1092 \end{cquote} 2227 1093 2228 1094 … … 2294 1160 In summary, an OCaml variant is a singleton value rather than a set of possibly ordered values, and hence, has no notion of enumerabilty. 2295 1161 Therefore it is not an enumeration, except for the simple opaque (nullary) case. 1162 1163 %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%% 2296 1164 2297 1165 \begin{comment} … … 2442 1310 To: Gregor Richards <gregor.richards@uwaterloo.ca>, Peter Buhr <pabuhr@uwaterloo.ca> 2443 1311 CC: Michael Leslie Brooks <mlbrooks@uwaterloo.ca>, Fangren Yu <f37yu@uwaterloo.ca>, 2444 1312 Jiada Liang <j82liang@uwaterloo.ca> 2445 1313 Subject: Re: Re: 2446 1314 Date: Thu, 21 Mar 2024 14:26:36 +0000 … … 2450 1318 2451 1319 enum Example { 2452 2453 2454 1320 Label, 1321 Name = 10, 1322 Tag = 3, 2455 1323 }; 2456 1324 … … 2542 1410 auto-init & & & & & & & & & & & \CM & \CM & \CM \\ 2543 1411 \hline 2544 ( un)scoped & & & & & & & & & & & U & U/S & U/S \\1412 (Un)Scoped & & & & & & & & & & & U & U/S & U/S \\ 2545 1413 \hline 2546 1414 overload & & \CM & & & & & & & & & & \CM & \CM \\
Note: See TracChangeset
for help on using the changeset viewer.