I'm writing a utility that will likely be called by a script that uses
argparse, but should be useable by any script. The engine object constructor needs a dozen or so parameters to tune its behavior. The question is: What is the best way to allow the caller to send the necessary parameters without requiring extra work setting params that have defaults.
First thought: Since some users will have an
argparse.Namespace instance already, I might make use of that. But other users would need to create a Namespace (or just a subclass of
object) which seems like a problem.
Second thought: Normal methods are written to just take a sequnce of (named) parameters, which is what programmers will expect. Better yet, Python allows
**kwargs in constructor's parameters, and
vars(aNamespace) is a map, so it seems we could call
EngineObject(*args, **vars(an_argparse_namespace)) and have the best of all worlds. But, alas, if the caller's
Namespace instance has attributes that the
Engine constructor doesn't recognize, it won't run.
Third thought: But... if we write the constructor as
__init__(self,arg1,arg2,arg3=def3,...argN=defN, **kwargs) that final
**kwargs will eat all the extra attributes. I like this... But, as I found by accident, if I pass a needed
..., misspelled=value, ... there is no warning that I got it wrong because that
**kwargs eats the misspelled parameter name just like it would eat other named parameters that we don't care about.
Current thought: I now have this code:
class Engine: @classmethod def clean_map(cls, amap): """return a map holding only valid keys/values from amap""" ... return safe_map def __init__(self, arg1, arg2, arg3=default3, ... argN=defaultN) self.arg1 = arg1 # etc ...
This gives me a slightly awkward way to give every user a good experience: Users who are just calling the service can pass the parameters they care about... and they'll be warned if they misspell something. Users who have an
argparse.Namespace object can write this somewhat clunky code:
engine = Engine(**Engine.clean _map(vars(myNamespace)))
An alternative would be to wrap the
clean_map method into an alternate constructor:
class Engine: @classmethod safe_construct(cls, amap): ... return cls(**safe_map)
So: What would be the ideal way to handle this? Is my "current thought" the best way to be Pythonic? Do you prefer the "safe_construct" technique to what I'm now using? Something else?