Why use Enums instead simple primitive types
Tags: beginners, python, refactoring, tutorial
Published: 2023-07-20 17:42:46

Some data value use a basic type like `str` or `int`,
but not all str and int are valid values of that data value.
Enums can limit the possibilities to only a defined set of values.

### Example:
Blood type is a string, but have a set of only 8 valid strings.
Don't exist a blood called `'randombloodtype'` but `'AB-'` is valid.
```python
from enum import Enum

class BloodType(str, Enum):
    A_negative = "A-"
    B_negative = "B-"
    AB_negative = "AB-"
    O_negative = "O-"
    A_positive = "A+"
    B_positive = "B+"
    AB_positive = "AB+"
    O_positive = "O+"

assert "A-" in list(BloodType)
assert "O+" == BloodType.O_positive
```

### So why not just use list?
The below code do the same as the above:
```python
BLOOD_TYPE = [
    "A-",
    "B-",
    "AB-",
    "O-",
    "A+",
    "B+",
    "AB+",
    "O+",
]

assert "A-" in BLOOD_TYPE
```

But let us suppose we should make a new piece of code. Given the donor blood type, returns the list of allowed recipients.

```python
from typing import List

def blood_type_can_donate_to(donor: str) -> List[str]:
    _recipients = {
        "A-": ["A-", "AB-", "A+", "AB+"],
        "B-": ["B-", "AB-", "B+", "AB+"],
        "AB-": ["AB-", "AB+"],
        "O-": ["A-","B-", "AB-", "O-", "A+", "B+", "AB+", "0+"],
        "A+": ["A+", "AB+"],
        "B+": ["B+", "AB+"],
        "AB+": ["AB+"],
        "O+": ["A+", "B+", "AB+", "O+"]
    }
    return _recipients[donor]
```

And using enum:
```python
from enum import Enum
from typing import List

class BloodType(str, Enum):
    A_negative = "A-"
    B_negative = "B-"
    AB_negative = "AB-"
    O_negative = "O-"
    A_positive = "A+"
    B_positive = "B+"
    AB_positive = "AB+"
    O_positive = "O+"

def blood_type_can_donate_to(donor: BloodType) -> List[BloodType]:
    if donor == BloodType.A_negative:
        return [
            BloodType.A_negative,
            BloodType.AB_negative,
            BloodType.A_positive,
            BloodType.AB_positive
        ]
    elif donor == BloodType.B_negative:
        return [
            BloodType.B_negative,
            BloodType.AB_negative,
            BloodType.B_positive,
            BloodType.AB_positive
        ]
    elif donor == BloodType.AB_negative:
        return [
            BloodType.AB_negative,
            BloodType.AB_positive
        ]
    elif donor == BloodType.O_negative:
        return list(BloodType)
    elif donor == BloodType.A_positive:
        return [
            BloodType.A_positive,
            BloodType.AB_positive
        ]
    elif donor == BloodType.B_positive:
        return [
            BloodType.B_positive,
            BloodType.AB_positive
        ]
    elif donor == BloodType.AB_positive:
        return [
            BloodType.AB_positive
        ]
    elif donor == BloodType.O_positive:
        return [
            BloodType.A_positive,
            BloodType.B_positive,
            BloodType.AB_positive,
            BloodType.O_positive
        ]
```

The second one does exactly the same as the first one, but have some big advantages:
- Help you to avoiding mistakes.
- Help to code editor to warning if you make a mistake.
- Help to the next person to works in the code, avoid mistakes.


> See the first version again: It has a typo error, a 0 instead of a O.