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

Time:01-18

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!

CodePudding user response:

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.

CodePudding user response:

I think this is making a class too generic, IMO. First, any optional kwargs that you are searching for in this fashion should just have a default attached:

class Signal:
    def __init__(
        self, 
        filepath, 
        foo=None,
        bar=None,
        **kwargs
    ):
        self.filepath = filepath
        self.foo = foo
        self.bar = bar

By selecting a suitable default, you can now check:

    ~snip~
    def some_method(self):
        if self.foo is not None:
            # do something with foo
        elif self.bar is not None:
            # do something with bar
        else:
            # do something with 1

Otherwise, it might be better to use different classes if the attributes need to differ in this manner.

  • Related