Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

How to use named parameters in Python methods that are defaulting to a class level value?

Usage scenario:

# case #1 - for classes
a = MyClass() # default logger is None
a = MyClass(logger="a") # set the default logger to be "a"
a.test(logger="b") # this means that logger will be "b" only inside this method
a.test(logger=None) # this means that logger will be None but only inside this method
a.test() # here logger should defaults to the value specified when object was initialized ("a")

How can I implement MyClass in order to be able to use it as above?

Let's assume that I have several methods inside MyClass that can accept the logger named parameter so I would appreciate a solution that does not require to add a lot of duplicate code at the beginning of each test...() method.

I read about the sentinel example, but this does not work for classes and I would not like to add a global variable to keep the sentinel object inside.

like image 779
sorin Avatar asked Aug 25 '10 15:08

sorin


People also ask

How do you use named arguments in Python?

Requiring your arguments be named You can create a function that accepts any number of positional arguments as well as some keyword-only arguments by using the * operator to capture all the positional arguments and then specify optional keyword-only arguments after the * capture.

How do you pass named parameters?

When you pass an argument by name, you specify the argument's declared name followed by a colon and an equal sign ( := ), followed by the argument value. You can supply named arguments in any order.

How do you define a default argument in Python?

Python has a different way of representing syntax and default values for function arguments. Default values indicate that the function argument will take that value if no argument value is passed during the function call. The default value is assigned by using the assignment(=) operator of the form keywordname=value.

How do you handle a named argument in a function?

When you mix named and unnamed arguments in a function call, the unnamed arguments must come before the named ones in the argument list. Thus, when writing a function, you should put required arguments to the function in the argument list before the ones that you consider optional.


2 Answers

_sentinel = object()

class MyClass(object):
  def __init__(self, logger=None):
    self.logger = logger
  def test(self, logger=_sentinel):
    if logger is _sentinel: logger = self.logger

# in case you want to use this inside a function from your module use:
_sentinel = object()
logger = None
def test(logger=_sentinel)
    if logger is _sentinel: logger = globals().get('logger')

two core ideas: capturing the set of named values that may be (or may not be) locally overridden into a keywords-parameters dict; using a sentinel object as the default value to uniquely identify whether a certain named argument has been explicitly passed or not (None is often used for this purpose, but when, as here, you want None as a "first class value" for the parameter, a unique sentinel object will do just as well).

like image 121
Alex Martelli Avatar answered Sep 23 '22 19:09

Alex Martelli


class MyClass(object):    
    def __init__(self, logger=None):
        self.logger = logger
    def test(self, **kwargs):
        logger = kwargs.get("logger", self.logger)
        # use logger, which is sourced as given in OP

Notes

  • The use of **kwargs is necessary as you're allowing None values for the logger named variable in MyClass.test. You could do away with this if you picked some other sentinel value (but None is most common).
  • It was unclear in the original question if you wanted a class or instance default. The one given above is an instance default, that is the default logger across all MyClass instances is None, set in the MyClass constructor.
like image 45
Matt Joiner Avatar answered Sep 21 '22 19:09

Matt Joiner