更改基类的__init__中调用的函数的签名

时间:2016-03-30 16:25:43

标签: python oop inheritance

我有一个班级A。在__init__的{​​{1}}中,我调用方法A

self.func(argument)

class A(object): def __init__(self, argument, key=0): self.func(argument) B的子类。在A中,我想更改B的签名。当我从func调用A的构造函数时调用它。

B

显然,这不起作用,因为class B(A): def __init__(self, argument1, argument2, key=0): super(B, self).__init__(argument1, key) # can't send argument 2 to __init__ of A def func(self, argument1, argument2): #some code here 的签名需要2个参数。我该如何解决这个问题?或者我设计课程的方式有什么不对吗?

func是A的key的默认参数。__init__不适用于argument2key是B所采用的额外参数,但A不是。 B也需要argument2并且具有默认值。

另一个限制是我不想更改A的签名。key通常是key。因此,我希望用户能够编写0而不是A(arg)

3 个答案:

答案 0 :(得分:4)

一般来说,在子类之间更改方法的签名会打破期望子类上的方法实现与父级上的方法相同的API。

但是,您可以重新设置A.__init__以允许任意额外参数,并将其传递给self.func()

class A(object):
    def __init__(self, argument, *extra, **kwargs):
        key = kwargs.get('key', 0)
        self.func(argument, *extra)
    # ...

class B(A):
    def __init__(self, argument1, argument2, key=0):
        super(B, self).__init__(argument1, argument2, key=key)
    # ...

传递给super(B, self).__init__()的第二个参数随后会在extra元组中捕获,并在self.func()之外应用于argument

在Python 2中,为了能够使用extra,您需要切换到使用**kwargs,否则key将始终捕获第二个位置参数。请务必使用keyB传递key=key

在Python 3中,您不受此限制的约束;将*args放在key=0之前,并且只在调用中使用key作为关键字参数:

class A(object):
    def __init__(self, argument, *extra, key=0):
        self.func(argument, *extra)

我也给func()一个*extra参数,这样它的界面在AB之间基本保持不变;它只是忽略了传递给A的第一个参数之外的任何内容,超出了B的前两个参数:

class A(object):
    # ...
    def func(self, argument, *extra):
        # ...

class B(A):
    # ...
    def func(self, argument1, argument2, *extra):
        # ...

Python 2演示:

>>> class A(object):
...     def __init__(self, argument, *extra, **kwargs):
...         key = kwargs.get('key', 0)
...         self.func(argument, *extra)
...     def func(self, argument, *extra):
...         print('func({!r}, *{!r}) called'.format(argument, extra))
...
>>> class B(A):
...     def __init__(self, argument1, argument2, key=0):
...         super(B, self).__init__(argument1, argument2, key=key)
...     def func(self, argument1, argument2, *extra):
...         print('func({!r}, {!r}, *{!r}) called'.format(argument1, argument2, extra))
...
>>> A('foo')
func('foo', *()) called
<__main__.A object at 0x105f602d0>
>>> B('foo', 'bar')
func('foo', 'bar', *()) called
<__main__.B object at 0x105f4fa50>

答案 1 :(得分:0)

您的设计似乎存在问题。以下可能会解决您的具体情况,但似乎进一步延续了糟糕的设计。请注意直接调用Parent.method

>>> class Parent:
    def __init__(self, a, b=None):
        Parent.method(self, a)
        self.b = b
    def method(self, a):
        self.location = id(a)


>>> class Child(Parent):
    def __init__(self, a):
        super().__init__(a, object())
    def method(self, a, b):
        self.location = id(a), id(b)


>>> test = Child(object())

请考虑在您要覆盖的方法的第二个参数中添加默认参数。否则,设计您的类并以不同方式调用结构。重组可能会消除这个问题。

答案 2 :(得分:0)

实际上我会在A的__init__中设置一个额外的布尔参数来控制func的调用,然后从B的__init__

中传递 False
class A(object):
    def __init__(self, argument, key=0, call_func=True):
        if call_func:
            self.func(argument)

class B(A):
    def __init__(self, argument):
        argument1, argument2 = argument, 'something else'
        super(B, self).__init__(argument1, argument2, call_func=False)