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.
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.
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.
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.
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.
_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).
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
**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).MyClass
instances is None
, set in the MyClass
constructor.If you love us? You can donate to us via Paypal or buy me a coffee so we can maintain and grow! Thank you!
Donate Us With