Search code examples
pythonpython-3.xoopkeyword-argument

Is there a standard way to implement optional keyword arguments in python classes?


So I'm writing a class for the purpose of doing data analysis on a signal that I am measuring. There is many ways that I can process the signal and other optional metadata that can be associated with each trial that I measure the signal. I guess my questions boils down to the best way in which I can handle multiple keyword arguments in a way my class can auto-detect the relevant arguments that isn't just a bunch of if-else statements, I guess similar to how you can add many optional keywords to matplotlib plots?

For example, lets say I have this hypothetical class that looks like this:

class Signal:
      def __init__(self, filepath, **kwargs):
          self.filepath = filepath
          self.signal_df = pd.read_csv(self.filepath)

          for k,v in kwargs.items():
              setattr(self, key, value)

After the initial construction of the objects there would then be pertinent methods dependent on what keyword arguments have been passed. Thus I could easily create the two following objects with ease:

signal_1 = Signal('filepath_0', **{'foo':1, 'bar':'9.2'})
signal_2 = Signal('filepath_1', **{'foo':12, 'baz':'red'})

To try and solve this I've pretty much just implemented statements in the init() method such that I'm doing something like this:

class Signal:
      def __init__(self, filepath, **kwargs):
          self.filepath = filepath
          self.signal_df = pd.read_csv(self.filepath)

          for k,v in kwargs.items():
              setattr(self, key, value)


          if hasattr(self, 'foo'):
             self.method_0(self.foo) # generic method that takes foo as argument

          if hasattr(self, 'bar'):
             self.method_1(self.bar) # generic method that takes bar as argument
          else:
             self.method_2(1.0) # alternate method if bar is not there

This just seems like a really clunky way of doing things and was hoping there may be a better solution. I appreciate any and all help!


Solution

  • I wouldn't use **kwargs here at all. Just define keyword-only parameters with the same kinds of sentinel defaults that you would use with positional parameters.

    class Signal:
          def __init__(self, filepath, *, foo=None, bar=None)):
              self.filepath = filepath
              self.signal_df = pd.read_csv(self.filepath)
              if self.foo is not None:
                  self.foo = foo
                  self.method_0(foo)
              if self.bar is not None:
                  self.bar = bar
                  self.method_1(bar)
              else:
                  self.method_2(1.0)
    

    Use **kwargs when you need to accept arguments you really know nothing about, except that they need to be passed on to some other method.