Python装饰器以确保kwargs正确

3

我已经写了一个装饰器,用于确保传递给构造函数的关键字参数是正确/预期的。以下是代码:

from functools import wraps

def keyargs_check(keywords):
"""
This decorator ensures that the keys passed in kwargs are the onces that
are specified in the passed tuple. When applied this decorate will
check the keywords and will throw an exception if the developer used
one that is not recognized.

@type keywords: tuple
@param keywords: A tuple with all the keywords recognized by the function.
"""

def wrap(f):
    @wraps(f)
    def newFunction(*args, **kw):
        # we are going to add an extra check in kw
        for current_key in kw.keys():
            if not current_key in keywords:
                raise ValueError(
                    "The key {0} is a not recognized parameters by {1}.".format(
                        current_key, f.__name__))
        return f(*args, **kw)
    return newFunction
return wrap

这个修饰器的一个例子如下:
class Person(object):

@keyargs_check(("name", "surname", "age"))
def __init__(self, **kwargs):
    # perform init according to args

使用上述代码,如果开发者传递了一个名为“blah”的关键字参数,它将会抛出一个异常。不幸的是,我的实现在继承方面存在一个重大问题,如果我定义以下内容:
class PersonTest(Person):

@keyargs_check(("test"))
def __init__(self, **kwargs):
    Person.__init__(self,**kwargs) 

因为我将kwargs传递给超类init方法,所以我会得到一个异常,因为“test”不在传递给超类装饰器的元组中。有没有办法让超类中使用的装饰器知道额外的关键字?或者更好的是,有没有标准的方法来实现我想要的?更新:我更感兴趣的是自动化地抛出异常的方式,当开发人员传递错误的kwarg时,而不是我使用kwargs而不是args的事实。我的意思是,我不想在每个类中编写检查传递给方法的参数的代码。
1个回答

4

您的装饰器是不必要的。装饰器做的唯一一件事情,无法使用标准语法完成的是防止关键字参数吸收位置参数。因此,

class Base(object):
    def __init__(name=None,surname=None,age=None):
        #some code

class Child(Base):
    def __init__(test=None,**kwargs):
        Base.__init__(self,**kwargs)

这样做的好处是,在Child中的kwargs将不包含test。问题在于,像c = Child('red herring')这样的调用可能会搞砸它。这在Python 3.0中得到了解决
您的方法存在问题,因为您试图使用装饰器来执行宏的工作,这是不符合Python风格的。唯一可以帮助您实现目标的方法是修改内部函数(特别是您代码中的fkwargs变量)的本地变量。您的装饰器应该如何知道包装器的内部情况?它怎么知道它调用了一个超类?

我理解你的观点,我确实考虑过使用*args,但这会给我带来一个问题,我不想做以下操作:example = Base(u"", u"", 26)而更愿意使用:example = Base(age=29)使用kwargs的原因是我有很多参数,我不希望开发人员必须传递所有默认值! - mandel
1
David编写的代码方式,您不必提供默认参数。您可以使用Base(age=29)构造一个基础类。 - Ned Batchelder
确实如此,但是我希望在每个类中执行检查,并且我认为使用装饰器可以增加代码的重用性。我不想在每个对象中添加检查。我更喜欢使用装饰器,但即使我使用函数,我也会遇到同样的问题。 - mandel

网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接