我有一个抽象类,我将其子类化。抽象类的特征之一是它有一个属性,用于存储实例化该类时输入的所有参数。例如,以下代码实现了我的目标:
class Parent:
def __init__(self, parent_parameter):
pass
class Child(Parent):
def __init__(self, parent_parameter, child_parameter_1, child_parameter_2):
self.input_params = locals()
super(Child, self).__init__(parent_parameter)
使用此代码,我可以通过
self.input_params
属性访问原始输入参数。然而,问题是第一行代码self.input_params = locals()
必须包含在每个子类中。我想要某种解决方案,例如
class Parent:
def __init__(self, parent_parameter):
self.input_params = locals()
class Child(Parent):
def __init__(self, parent_parameter, child_parameter_1, child_parameter_2):
super(Child, self).__init__(parent_parameter)
基本上,无论谁编写子类,都不需要注意包含参数日志记录的代码。当然,此块中的代码只会捕获
parent_parameter
参数,不会捕获任何子参数。
子参数当然可以显式传递给父类
__init__
,但这打破了实现者不必担心日志记录的要求,并引入了*args
和**kwargs
,这感觉是多余的。
class Parent:
def __init__(self, parent_parameter, *args, **kwargs):
self.input_params = locals()
class Child(Parent):
def __init__(self, parent_parameter, child_parameter_1, child_parameter_2):
super(Child, self).__init__(parent_parameter, child_parameter_1, child_parameter_2)
编辑: 也许最好的只是所需代码的文档:
class Parent:
def __init__(self, parent_parameter):
# subclass __init__() must begin with `self.input_params = locals()`
pass
class Child(Parent):
def __init__(self, parent_parameter, child_parameter_1, child_parameter_2):
self.input_params = locals()
super(Child, self).__init__(parent_parameter)
好吧,我想,我想出了一些可以通过覆盖
__new__
来起作用的东西。如果这不是Pythonic或hacky,或者这是否是__new__
的典型用例,我将不胜感激。
class Parent:
def __new__(cls, *args, **kwargs):
input_params = {'args': args, 'kwargs': kwargs}
obj = super(Parent, cls).__new__(cls)
obj.input_params = input_params
return obj
def __init__(self, parent_parameter):
pass
class Child(Parent):
def __init__(self, parent_parameter, child_parameter_1, child_parameter_2):
super(Child, self).__init__(parent_parameter)
我看到了你的建议,但我注意到一个问题,如果子类 init 函数有一个未填充的默认参数,它不会在 init_params 中。
我在下面提出一个新版本。我添加了一个包装器,在这个新版本中,如果删除
frame.f_locals["self"] == self
,您可以混合包装器和包装类的初始化参数,这是不可取的。
import inspect
class Parent:
def __new__(cls, *args, **kwargs):
input_params = {'args': args, 'kwargs': kwargs}
obj = super(Parent, cls).__new__(cls)
obj.v0_input_params = input_params
return obj
def __init__(self, parent_arg, parent_kwarg=1):
# Get the init parameters
self.init_params = {}
frame = inspect.stack()[0][0]
while frame is not None and "self" in frame.f_locals and frame.f_locals["self"] == self:
self.init_params.update(frame.f_locals)
frame = frame.f_back
for ignored in ["self", "frame", "__class__"]:
self.init_params.pop(ignored)
# Do your stuff here
self.parent_arg = parent_arg
self.parent_kwarg = parent_kwarg
class Child(Parent):
def __init__(self, parent_arg, child_arg, parent_kwarg=1, child_kwarg=2):
self.child_arg = child_arg
self.child_kwarg = child_kwarg
super().__init__(parent_arg, parent_kwarg=parent_kwarg)
class Wrapper(Parent):
def __init__(self, parent_arg, child_arg, parent_kwarg=1, child_kwarg=2, wrapper_kwarg=3):
self.wrapper_kwarg = 3
self.wrapped_child = Child(parent_arg, child_arg, child_kwarg="replaced")
super().__init__(parent_arg, parent_kwarg=1)
if __name__ == '__main__':
agent = Wrapper("set_parent_arg", "set_child_arg")
print("V0:")
print("wrapper init params: ", agent.v0_input_params)
print("wrapper.wrapped_child init params: ", agent.wrapped_child.v0_input_params)
print()
print("V1:")
print("agent init params: ", agent.init_params)
print("agent.wrapped_child init params: ", agent.wrapped_child.init_params)
这是输出:
V0:
wrapper init params: {'args': ('set_parent_arg', 'set_child_arg'), 'kwargs': {}}
wrapper.wrapped_child init params: {'args': ('set_parent_arg', 'set_child_arg'), 'kwargs': {'child_kwarg': 'replaced'}}
V1:
agent init params: {'parent_arg': 'set_parent_arg', 'parent_kwarg': 1, 'child_arg': 'set_child_arg', 'child_kwarg': 2, 'wrapper_kwarg': 3}
agent.wrapped_child init params: {'parent_arg': 'set_parent_arg', 'parent_kwarg': 1, 'child_arg': 'set_child_arg', 'child_kwarg': 'replaced'}
Process finished with exit code 0