Say I have this sample code I’m writing for a Python lesson I’m holding:
class Enum(dict):
def __init__(self, *values):
values = {mnemo: num
for num, mnemo in enumerate(values)}
dict.__init__(self, values) # ← this
def __getattr__(self, name):
return self[name]
This seems to work as expected, at least on a shallow method, but what I see most of the time on the literature on the Internet is:
- Avoid inheriting from inbuilt types: it’s much better to type out proxy methods by hand!
- Using
super().methodrather than explicitly namingsuperclass.method
I guess especially the latter is more DRY, but super() is going to be trickier to explain once I start throwing in multiple inheritance. I want to make sure, then, that there are no nasty gotchas in there that other people will then stumble upon.
I don’t see why you should generally avoid to derive from built-in types. It was considered an improvement that the arrival of new-style classes enabled to do this, and even before this the standard library provided the
UserDictandUserListclasses to derive from. Of course you have to evaluate in every specific case whether inheritance or composition is what you need, but that’s how in always is in OOP — nothing special about built-in types here.I wouldn’t recommend
super()in the example. It is only useful if all cooperating methods have the same signature, or at least cooperating signatures. For single inheritance and “diamond-free” multiple inheritance, explicitly calling base-class methods is fine in my opinion. If you want to get things right in multiple inheritance with diamonds, you are forced to usesuper(), but you have to design your methods carefully, see Raymond Hettinger’s “Python’ssuper()considered super!”It is generally safe to use
super()in cases where all involved methods have exactly the same signature, including methods likes__getattr__()and__setattr__().Your example class is a rather questionable implementation of an
Enumtype, but not for any of the reasons you mentioned in your post. It mixes up two namespaces: The namespace of the attributes ofdictand the enumeration entries. TryEnum("clear", "update")to see what I mean. In this particular case, the follwing implementation would be much better:This adds the enumeration entries as attributes to a standard instance without deriving from
dict. Every custom class instance brings its own dictionary anyway, namely__dict__.