2

I'm working through a mechanical systems (for example, a simple helical gear) and want to prevent users from assigning values not found in an associated enum. I am new to enums and 'pythonic' approaches to answering this question...so my request is two-fold:

  1. Is it more appropriate in python to let input errors happen and use subsequent code to manage the fallout?
  2. Should the enum be defined within the class that uses it? I don't intend for this enum to be available elsewhere...I feel weird just letting it hang out at the beginning of my file.

Here's the code:

HAND = Enum('HAND', ['LH', 'RH'])


class HelicalGear(Gear):
    def __init__(self, hand):
        self.type_ = 'Helical'
        self.hand = hand

    @property
    def hand(self):
        return self._hand

    @hand.setter
    def hand(self, hand):
        if not hand:
            raise ValueError("Gear hand definition required")
        elif hand not in [e.name for e in HAND]:
            raise ValueError("Gear hand must be LH or RH")

        self._hand = hand

2 Answers 2

3
  1. Error handling should happen as close to the origin of the error as possible. So whether users are entering data in or functions/classes are accepting data, that data should be checked and dealt with immediately.

  2. Where you define the Enum is entirely up to you, but I tend to leave them at the top level for several reasons:

    • Other code that needs to use them:
      somemod.HAND.LH, or
      somemod.HelicalGear.HAND.LH?
    • What if you later create another class that has right- and left- handed items?

Minor correction: your __init__ should be setting self._hand. As MisterMiyagi commented: using hand instead of _hand has the advantage of the error detecting code in hand.setter.

In your setter code I would do something like:

@hand.setter
def hand(self, hand):
    if hand in HAND:
        # already an Enum, pass
        pass
    elif hand in HAND.__members__:
        # it's the name of a hand
        hand = HAND[hand]
    else:
        # numeric?
        hand = HAND(hand)
        # if not, a ValueError will be raised:
        # `ValueError: 0 is not a valid Hand`
    # save the Enum value
    self._hand = hand  # or hand.value or hand.name depending on what you want
                       # returned when HelicalGear.hand is accessed
Sign up to request clarification or add additional context in comments.

3 Comments

__init__ is implicitly setting self._hand via hand.setter. Using the setter has the benefit of adding the type check.
Thanks for the quick feedback! Very helpful!
@MisterMiyagi: Ah, right you are! Thanks for catching that.
1

There is no answer appropriate in general - it really depends on how you use your objects.

An important concept of python is duck typing - that is, whether an object is appropriate is defined by its features, not its type. In your example, think about what an enum is: a mapping of a name to an integer.

So, should one enforce that users provide the enum entry? Or accept the name as well, as in your code? If following code just operates on the enum value, simply providing that value (e.g. 1 for LH) would work as well.

Personally, I prefer to do implicit type checking in client code that actually uses data[1], not strict type checking by interface code that only stores values. The later requires to explicitly write something that is implicitly performed later on anyways. This means you have to keep your type checking up to date with any client code.

So the question shouldn't be "do I raise an error on wrong input?" but "can I identify wrong input without using it?". If you answer the later with "yes" then it's simply more practical to raise an error right away. If you answer it with "dunno, the enum may be applicable outside my class" then being too quick on raising errors will cost you lots of flexibility.


[1] The exception is input sanitization. For example, if you feed input to an eval, better be safe than sorry.

3 Comments

Minor nit: Enums start at 1, not 0.
@EthanFurman So that's how to identify python3 newbies. ^^ Thanks for the hint, still haven't gotten around to getting accustomed with the new toys.
If you want to play with Enum sooner rather than later check out the backport: pypi.python.org/pypi/enum34.

Your Answer

By clicking “Post Your Answer”, you agree to our terms of service and acknowledge you have read our privacy policy.

Start asking to get answers

Find the answer to your question by asking.

Ask question

Explore related questions

See similar questions with these tags.