Подтвердить что ты не робот

Добавление аргумента ключевого слова в переопределенный метод и использование ** kwarg

Я подклассифицирую объект, чтобы переопределить метод, к которому я хочу добавить некоторые функции. Я не хочу полностью его заменять или добавлять другой метод, но оставаться совместимым с методом суперклассов, просто добавив необязательный аргумент в метод. Возможно ли работать с *args и **kwargs, чтобы передать все аргументы суперклассу и добавить дополнительный аргумент со значением по умолчанию? Я интуитивно придумал следующее, но это не работает:

class A(object):
    def foo(self, arg1, arg2, argopt1="bar"):
        print arg1, arg2, argopt1

class B(A):
    def foo(self, *args, argopt2="foo", **kwargs):
        print argopt2
        A.foo(self, *args, **kwargs)


b = B()
b.foo("a", "b", argopt2="foo")

Конечно, я могу заставить его работать, когда я явно добавляю все аргументы метода суперкласса:

class B(A):
    def foo(self, arg1, arg2, argopt1="foo", argopt2="bar"):
        print argopt2
        A.foo(self, arg1, arg2, argopt1=argopt1)

Какой правильный способ сделать это, должен ли я знать и явно указывать все аргументы переопределенных методов?

4b9b3361

Ответ 1

class A(object):
    def foo(self, arg1, arg2, argopt1="bar"):
        print arg1, arg2, argopt1

class B(A):
    def foo(self, *args, **kwargs):
        argopt2 = kwargs.get('argopt2', default_for_argopt2)
        # remove the extra arg so the base class doesn't complain. 
        del kwargs['argopt2']
        print argopt2
        A.foo(self, *args, **kwargs)


b = B()
b.foo("a", "b", argopt2="foo")

Ответ 2

Какой правильный способ сделать это, сделаю ли я должны знать и четко указывать все аргументов переопределенных методов?

Если вы хотите охватить все случаи (вместо того, чтобы просто полагаться на вызывающего, чтобы всегда делать что-то по-вашему, например, всегда вызывать вас только с дополнительным аргументом, переданным по имени, никогда не положением), вам нужно закодировать ( или динамически обнаруживать) много знаний о сигнатуре метода, который вы переопределяете - вряд ли удивительно: наследование является сильной формой связи, а переопределяющие методы - это один из способов, который представляет собой связь.

Вы можете динамически обнаруживать аргументы метода суперкласса через inspect.getargspec, чтобы убедиться, что вы его правильно назовете... но эта интроспекция метод может оказаться сложным, если классы два пытаются сделать то же самое (как только вы узнаете, что ваш метод суперкласса принимает *a и/или **kw, вы можете сделать чуть больше, чем передать все соответствующие аргументы вверх и надеяться, скрестив пальцы, что цепочка методов вверх по течению в конечном итоге делает правильную уборку, прежде чем вызывать версию, которая не настолько толерантна).

Такие цены могут стоить заплатить, когда вы разрабатываете обертку, которая должна динамически применяться к вызывающим материалам с широким спектром подписей (особенно, поскольку в настройке декоратора вы можете организовать высокую стоимость интроспекции только один раз за функцию, которую вы украшаете, а не каждый раз, когда вызывается результирующая оболочка). Кажется маловероятным, чтобы быть подходящей техникой в ​​таком случае, как ваш, где вам лучше знать, что вы подклассифицируете (подклассификация - сильная связь: делать это слепо, определенно нецелесообразно!), И поэтому вы можете также указать аргументы явно.

Да, если код суперкласса сильно меняется (например, путем изменения сигнатур метода), вам также придется пересмотреть подкласс - это (часть) цены наследования. Общая цена достаточно высока, что новый Go язык программирования полностью без него - заставляя вас применять Gang of 4 отличный совет предпочитает композицию над наследованием. В Python полное воздержание от наследования было бы просто непрактичным, но использование его трезво и в умеренных количествах (и принятие цены, которую вы заплатите в плане сцепления, когда вы это сделаете) остается целесообразным.

Ответ 3

При подклассификации и переопределении методов всегда нужно решить, является ли использование super() хорошей идеей, а эта страница хороша для этого.

Я не говорю, что следует избегать super(), как, например, автор статьи: Я говорю, что super() имеет некоторые очень важные предпосылки, которые необходимо соблюдать, если вы не хотите, чтобы super() вернитесь и кусайте вас.