Detecting kwarg override in Python

I am looking for a way to determine if the arg keyword was passed explicitly, without using **kwargs

.

Here's an example:

def foo(first, second=None):
    pass

      

If in this function I found what it second

contains None

, is there a way to know if this None

was the default or passed explicitly? Basically, I have an optional argument, which could possibly be any value or type. So I either need some kind of "unique" default so that the user never intentionally passes this default, or I need a way to determine if the argument was actually passed explicitly.

I expect to be able to open it by checking the stack, but I feel like this is overkill.

I also know that I can do it this way:

def foo(first, **kwargs):
    if 'second' in kwargs:
        # Overridden!
        pass

      

But I would rather not accept **kwargs

, as it makes my function signature less useful and can hide errors.

+3


source to share


2 answers


You can always create a unique object and use it as the default:



_default = object()

def foo(first, second=_default):
    if second is not _default:
        # Overridden!
        pass

      

+4


source


NPE's answer is exactly: use the hourly value , t None

. Any old singleton object will do most of the time.

There is one drawback _default = object()

though: _default

does not share the None

convenient boolean equality property False

in conditionals. That is, it None

is "false" but _default

is "true". More formally, bool(None) == False

yet bool(_default) == True

.

It would be great if you could create new instances NoneType

like None

, but not really None

. But NoneType

it cannot be additionally instantiated. However, you can create a similar class. So:

import sys

_PY3 = sys.version_info[0] == 3

class NullType(object):

    """
    A 'null' type different from, but parallel to, None. Core function
    is representing emptyness in a way that doesn't overload None.
    This helps create designated identifiers with specific meanings
    such as Passthrough, Prohibited, and Undefined.

    Instantiate to create desired null singletons. While they are
    singletons, depends on usage convention rather than strict
    enforcement to maintain their singleton-ness. This is a problem
    roughly 0% of the time.
    """

    def __init__(self, name=None):
        self.name = name

    def __repr__(self):
        if self.name is not None:
            return self.name
        else:
            return 'NullType(id: {0})'.format(id(self))

    if _PY3:
        def __bool__(self):
            """I am always False."""
            return False
    else:  # PY2
        def __nonzero__(self):
            """I am always False."""
            return False

      

Throw this code in nulltype.py

and then:



from nulltype import NullType
_default = NullType('_default')

def foo(first, second=_default):
    if second is not _default:
        # Overridden!
        pass

      

But you can also do:

    if second:
        ...

      

(in some contexts) because the truth value t217 is equivalent None

. If you used a simpler _default = object()

sentinel, you couldn't do it because bool(object()) == True

.

Now _default

unique, prints nicely when needed to be printed, and always equivalent False

. This is your own None

.

+2


source







All Articles