是否可以选择将哪些 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 中需要。但是,a
和 b
参数在 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".
现在这仍然是简单有效地作为输入验证框架工作的方法,特别是如果你想要继承...如果 B
是 A
的适当子 class ,它应该能够添加到 default_properties
而不必完全重新定义它(这是一个明显的 DRY 违规)。用户输入验证通常比检查参数名称要复杂得多...您可能想研究其他库/框架如何解决问题(这里想到 Django 的表单)。
有没有办法阻止一些但不是所有的参数被发送到超级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 中需要。但是,a
和 b
参数在 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".
现在这仍然是简单有效地作为输入验证框架工作的方法,特别是如果你想要继承...如果 B
是 A
的适当子 class ,它应该能够添加到 default_properties
而不必完全重新定义它(这是一个明显的 DRY 违规)。用户输入验证通常比检查参数名称要复杂得多...您可能想研究其他库/框架如何解决问题(这里想到 Django 的表单)。