【问题标题】:Is it possible to be selective about which kwargs to pass to a superclass in python?是否可以选择性地将哪些 kwargs 传递给 python 中的超类?
【发布时间】:2018-02-03 08:56:30
【问题描述】:

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

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

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}}

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

在尝试从A 继承并将额外的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 被传递给超类,尽管它只在子类中需要。但是,ab 参数在A 中使用,并且应该从B 传递到A

【问题讨论】:

  • del kwargs['d']?
  • 需要将kwargs 传递给不在self.default_properties 中的超类。这是您的期望@CiaranWelsh 吗?
  • 我希望将 B().default_properties() 中的 ab 传递给 A 而不是 B().default_properties() 中的 d
  • 我试过del kwargs['d'],它确实有效。但是它不是很优雅,我想以此为基础构建一个 python 包——如果它有缺陷,我认为我最好找到一个没有缺陷的解决方案。感谢您的建议@Rawing

标签: python class inheritance subclass superclass


【解决方案1】:

有没有办法阻止某些但不是所有参数被发送到超类?

嗯,很简单:不要通过它们。你应该知道你的类接受哪些参数以及它的超类也接受哪些参数,所以只传递超类所期望的:

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

现在,如果您的班级的职责是针对给定的“模式”(您的 sn-p 中的default_properties)验证任意用户输入,那么您的代码中确实存在一些逻辑错误 - 主要是,您 1. 验证您的初始化器中的输入和 2. 在覆盖对象的 default_properties 之前调用父类的初始化器,因此当调用超类初始化器时,它不会针对正确的模式进行验证。此外,您在初始化程序中将default_properties 定义为实例属性,因此如果您只是交换指令以首先定义default_propertie,然后才调用父初始化程序,这将重新定义default_properties

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

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 将使用当前实例的类 default_properties 并且你不必关心“选择传递给超类的 kwargs”。

现在这仍然是一种有效地作为输入验证框架工作的简单方法,特别是如果你想要继承......如果BA 的正确子类,它应该能够添加到default_properties无需完全重新定义它(这是明显的 DRY 违规)。并且用户输入验证通常比仅检查参数名称涉及更多...您可能想研究其他库/框架如何解决问题(此处想到 Django 的表单)。

【讨论】:

  • 您好 bruno,非常感谢您的详细回答——它看起来对我来说非常宝贵(作为一个自学成才的 Python 程序员)。我只是有几个问题。首先,当我对 Base 类中的 self.check_integrity() 进行建议更改时,会引发 NameError。这是一个link 来演示错误。其次,是否需要定义一个空的default_properties?这有什么价值?谢谢。
  • @CiaranWelsh 我的代码 sn-p 确实有错误,现在已修复。空的default_properties 不是严格地 必需的(也绝不要求为空),但没有它check_integrity() 会因没有default_properties 的子类而中断。它也使意图更加清晰。
猜你喜欢
  • 1970-01-01
  • 2012-12-29
  • 2021-04-20
  • 2021-01-20
  • 1970-01-01
  • 2017-08-20
  • 2014-09-12
  • 1970-01-01
  • 2017-04-06
相关资源
最近更新 更多