是否可以选择将哪些 kwargs 传递给 python 中的超类?

Is it possible to be selective about which kwargs to pass to a superclass in python?

有没有办法阻止一些但不是所有的参数被发送到超级class?

我有一个验证用户输入的基础class:

class Base(object):
    def __init__(self, **kwargs):
        self.kwargs = kwargs
        super(Base, self).__init__()

    @staticmethod
    def check_integrity(allowed, given):
        """
        Verify user input.
        :param allowed: list. Keys allowed in class
        :param given: list. Keys given by user
        :return:
        """
        for key in given:
            if key not in allowed:
                raise Exception('{} not in {}'.format(key, allowed))

>>> base = Base()
>>> print base.__dict__
output[0]: {'kwargs': {}}

A继承自Base并使用方法检查其关键字

class A(Base):
    def __init__(self, **kwargs):
        super(A, self).__init__(**kwargs)
        self.default_properties = {'a': 1,
                                   'b': 2}

        self.check_integrity(self.default_properties.keys(), kwargs.keys())

>>> a = A(a=4)
>>> print a.__dict__
output[1]: {'default_properties': {'a': 1, 'b': 2}, 'kwargs': {'a': 4}}

我还应该提一下,我已经提取了我在这个 class 中使用的另一种方法来更新 class 属性,因为它与问题无关(因此 a在上面的例子中没有更新为4)

当尝试从 A 继承并向子 class 添加额外的 kwargs 时出现问题:

class B(A):
    def __init__(self, **kwargs):
        super(B, self).__init__(**kwargs)

        self.default_properties = {'a': 2,
                                   'c': 3,
                                   'd': 4}

        self.check_integrity(self.default_properties.keys(), kwargs.keys())



>>> b = B(d=5)


Traceback (most recent call last):
  File "/home/b3053674/Documents/PyCoTools/PyCoTools/Tests/scrap_paper.py", line 112, in <module>
    b = B(d=5)
  File "/home/b3053674/Documents/PyCoTools/PyCoTools/Tests/scrap_paper.py", line 96, in __init__
    super(B, self).__init__(**kwargs)
  File "/home/b3053674/Documents/PyCoTools/PyCoTools/Tests/scrap_paper.py", line 92, in __init__
    self.check_integrity(self.default_properties.keys(), kwargs.keys())
  File "/home/b3053674/Documents/PyCoTools/PyCoTools/Tests/scrap_paper.py", line 84, in check_integrity
    raise Exception('{} not in {}'.format(key, allowed))
Exception: d not in ['a', 'b']

这里 d 被向上传递给 superclass,即使它只在 subclass 中需要。但是,ab 参数在 A 中使用,应该从 B 传递到 A

Is there a way to prevent some but not all arguments from being sent up to a superclass?

嗯,是的,很简单:不要超过他们。你应该知道你的 class 需要哪些参数以及它的 superclass(es) 也需要哪些参数,所以只传递 superclass 期望的参数:

class Base(object):
    def __init__(self, arg1, arg2):
        self.arg1 = arg1
        self.arg2 = arg2

class Child(object):
    def __init__(self, arg1, arg2, arg3):
        super(Child, self).__init__(arg1, arg2)
        self.arg3 = arg3

以上是简单易读和可维护的,并且保证可以工作。如果你想要默认值,这也不是问题:

class Base(object):
    def __init__(self, arg1=1, arg2=2):
        self.arg1 = arg1
        self.arg2 = arg2

class Child(object):
    def __init__(self, arg1=1, arg2=2, arg3=3):
        super(Child, self).__init__(arg1, arg2)
        self.arg3 = arg3

现在,如果您的 class 的职责是针对给定的 "schema"(您的代码段中的 default_properties)验证任意用户输入,那么您的代码中确实存在一些逻辑错误代码 - 主要是,你 1. 在初始化器中验证你的输入和 2. 在覆盖你的对象的 default_properties 之前调用父级的初始化器,所以当调用 superclass 初始化器时它不会验证正确的架构。此外,您将 default_properties 定义为初始化程序中的实例属性,因此如果您只是将指令交换为先定义 default_propertie ,然后才调用父级的初始化程序,则此初始化程序将重新定义 default_properties

一个简单的解决方法是将 default_properties 设为 class 属性:

class Base(object):

    # empty by default
    default_properties = {}

    def __init__(self, **kwargs):                
        self.kwargs = kwargs
        # object.__init__ is a noop so no need for a super call here
        self.check_integrity()                

    def check_integrity(self):
        """
        Verify user input.
        """
        for key in self.kwargs:
            if key not in self.default_properties:
                raise ValueError('{} not allowed in {}'.format(key, self.default_properties))

然后你根本不需要覆盖初始化程序:

class A(Base):
    default_properties = {'a': 1,
                          'b': 2}


class B(A):
    default_properties = {'a': 1,
                          'b': 2,
                          'd': 3}

你就大功告成了 - check_integrity 将使用当前实例的 class default_properties 而你不必关心 "being selective about which kwargs you pass to the superclass".

现在这仍然是简单有效地作为输入验证框架工作的方法,特别是如果你想要继承...如果 BA 的适当子 class ,它应该能够添加到 default_properties 而不必完全重新定义它(这是一个明显的 DRY 违规)。用户输入验证通常比检查参数名称要复杂得多...您可能想研究其他库/框架如何解决问题(这里想到 Django 的表单)。