Reputation: 315
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:
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
Upvotes: 2
Views: 295
Reputation: 69248
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.
Where you define the Enum is entirely up to you, but I tend to leave them at the top level for several reasons:
somemod.HAND.LH
, or somemod.HelicalGear.HAND.LH
?Minor correction: your As MisterMiyagi commented: using __init__
should be setting self._hand
.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
Upvotes: 3
Reputation: 52139
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.
Upvotes: 1