在Python中检测kwarg覆盖

时间:2014-12-02 21:30:21

标签: python

我正在寻找一种方法来检测是否明确传递了关键字arg,而不使用**kwargs

以下是一个例子:

def foo(first, second=None):
    pass

如果在此函数中我发现second包含None,是否有办法知道None是来自默认值,还是已明确传入?基本上,我有一个可选的参数,可以想象是任何值或类型。所以我要么需要某种“唯一”默认值,这样用户就不会故意传递那个默认值,或者我需要一种方法来检测参数是否实际上是明确传递的。

我希望我可以通过检查堆栈来发现它,但我觉得这太过分了。

我也知道我可以这样做:

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

但我不想接受**kwargs,因为它使我的函数签名不那么有用并且可以隐藏错误。

2 个答案:

答案 0 :(得分:4)

您始终可以创建唯一对象并将其用作默认对象:

_default = object()

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

答案 1 :(得分:2)

NPE's answer完全正确:使用不是None的{​​{3}}。任何旧的sentinel value对象都会在很长一段时间内完成。

_default = object()有一个缺点:_default不会在条件中共享None在逻辑上等于False的方便属性。也就是说,None是“假的”,但_default是“真实的”。更正式的是bool(None) == False bool(_default) == True

如果您可以创建新的NoneType个实例,就像None一样,但不是None,那就太棒了。但NoneType无法进一步实例化。但是,您可以创建一个类似的类。所以:

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

将该代码放入nulltype.py,然后:

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

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

但你也可以这样做:

    if second:
        ...

(在某些情况下)因为second的真值等于None。如果您使用了更简单的_default = object()哨兵,则无法执行此操作,因为bool(object()) == True

现在_default是唯一的,在需要打印时打印得很好,并且始终等同于False。这是你自己的私人None