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

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

class ClassA(some.package.Class):
    def __init__(self, *args, **kwargs):
        super().__init__(*args, **kwargs)

class ClassB(ClassA): 
    def __init__(self, *args, a='A', b='B', c='C', **kwargs):
        super().__init__(*args, **kwargs)
        self.a=a
        self.b=b
        self.c=c

терпит неудачу, потому что я не могу перечислить такие параметры для ClassB __init__. И

class ClassB(ClassA):   
    def __init__(self, *args, **kwargs):
        super().__init__(*args, **kwargs)
        self.a=a
        self.b=b
        self.c=c

конечно не работает, потому что новые ключевые слова не указаны.

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


person orome    schedule 14.12.2014    source источник
comment
Вы пытались удалить их перед вызовом суперкласса?   -  person Ignacio Vazquez-Abrams    schedule 14.12.2014
comment
@IgnacioVazquez-Abrams: Это подход Pythonic (другого пути нет?)? Если это так, это, вероятно, ответ, который я ищу.   -  person orome    schedule 14.12.2014
comment
Обычно мы пишем наш код так. class Derived(Base): def meth (self): super(Derived, self).meth() Любая конкретная причина, по которой вы не следуете этому пути.   -  person kvivek    schedule 14.12.2014
comment
@kvivek: Моя ошибка!   -  person orome    schedule 14.12.2014


Ответы (4)


Попробуйте сделать это так:

class ClassA:
    def __init__(self, *args, **kwargs):
        pass

class ClassB(ClassA):
    def __init__(self, *args, **kwargs):            
        self.a = kwargs.pop('a', 'A')
        self.b = kwargs.pop('b', 'B')
        self.c = kwargs.pop('c', 'C')
        super().__init__(*args, **kwargs)

По сути, вы добавляете аргументы ключевого слова a, b и c в ClassB, а другие аргументы ключевого слова передаете в ClassA.

person aknuds1    schedule 14.12.2014
comment
Разве pop не должны идти первыми, потому что это ключевые слова, которых производный класс не ожидает? - person erewok; 14.12.2014
comment
@erewok Да, я испортил это при первоначальном редактировании, упс. - person aknuds1; 14.12.2014
comment
Итак, действительно нет никакого способа получить аргументы нового ключевого слова в сигнатуре метода? - person orome; 14.12.2014
comment
Ответ @raxacoricofallapatorius Rawing показывает, как это сделать. - person aknuds1; 14.12.2014
comment
@ aknuds1: Кажется, есть спор о том, являются ли это аргументами с ключевыми словами. похоже, что это просто позиционные аргументы (со значениями по умолчанию). - person orome; 14.12.2014
comment
**kwargs просто получает аргументы ключевого слова, которые не соответствуют никаким формальным параметрам. Это определение. Так что это зависит от того, нужны ли вам формальные параметры или нет. Таким образом, кажется, что согласно определению Python аргументы ключевого слова относятся к именованным аргументам во время вызова. То есть вы можете рассматривать формальные параметры как либо позиционные аргументы, либо аргументы ключевого слова. - person aknuds1; 14.12.2014
comment
@aknuds1: Да, извини. Побочный эффект заключается в том, что они становятся формальными параметрами (и, следовательно, позиционными) также; технически они все еще являются аргументами ключевого слова, но не теми, которые появляются (как ни странно) в kwargs — верно? - person orome; 14.12.2014
comment
@raxacoricofallapatorius На самом деле не сбивает с толку то, что их нет в kwargs, поскольку последний содержит аргументы ключевого слова, которые не являются формальными параметрами. То же самое и с аргументами, это позиционные параметры, а не формальные. - person aknuds1; 14.12.2014
comment
@ aknuds1: я просто имел в виду, что терминология сбивает с толку (для меня). Я вижу, что происходит (я думаю). - person orome; 14.12.2014
comment
@raxacoricofallapatorius Надеюсь, мое объяснение проясняет, как все это работает. Просто рассмотрите пакеты *args и **kwargs для неформализованных (дополнительных) аргументов. - person aknuds1; 14.12.2014
comment
@aknuds1: Теперь мне сказали (см. комментарии ниже), что они не аргументы ключевого слова. См. комментарии к ответу Халимура Али. Являются они или нет, имеет решающее значение для принятия решения о правильном ответе. Можете ли вы присоединиться к обсуждению там и внести ясность? - person orome; 16.12.2014
comment
@raxacoricofallapatorius Думаю, я пояснил, что такое аргумент ключевого слова в своих комментариях к его ответу. - person aknuds1; 16.12.2014
comment
Разве не должно быть super(ClassB, self), а не super(ClassA, self) в последней строке? - person Bill; 09.10.2016

Все, что вам нужно сделать, это переставить аргументы.

def __init__(self, a='A', b='B', c='C', *args, **kwargs):
person Aran-Fey    schedule 14.12.2014
comment
Это имеет разный эффект. - person Oleh Prypin; 14.12.2014
comment
@OlehPrypin: Чем эффект отличается от приведенных ниже решений? Кажется, он делает то, что я хочу, но, возможно, я не спросил четко. - person orome; 14.12.2014
comment
Разница, конечно, в порядке аргументов. Предполагая, что вы вызываете ClassB(5), мое решение будет эквивалентно ClassB(a=5), в то время как другие решения будут чем-то вроде ClassB(args=[5]). - person Aran-Fey; 14.12.2014
comment
если пользователь инициирует объект класса ClassB следующим образом: b = ClassB('hello') это присвоит 'hello' атрибуту экземпляра a и не будет рассматривать его как позиционный аргумент. Это не решает проблему OP добавления новых аргументов ключевого слова в подкласс. - person Haleemur Ali; 14.12.2014
comment
Итак, если у __init__ ClassA есть (x, y, z, p='P'), как мне получить вызов ClassB(...) для установки x, y или z? Я думаю, что это работает для меня только потому, что база ClassA (matplotlib.figure.Figure) принимает только аргументы ключевого слова. - person orome; 14.12.2014
comment
@HaleemurAli: Можете ли вы объяснить, что вы подразумеваете под плохим назначением ... атрибуту экземпляра? - person orome; 14.12.2014
comment
конечно, я могу опубликовать побочные эффекты вышеуказанной сигнатуры метода в ответе, комментарии не позволяют сделать это надлежащим форматированием кода, дайте мне минуту. - person Haleemur Ali; 14.12.2014

Эффект def __init__(self, a='A', b='B', c='C', *args, **kwargs):

Изменение кода OP с помощью конструктора дочернего класса, имеющего указанную выше подпись:

class BaseClass(object):
    def __init__(self, *args, **kwargs):
        self.args = args
        for k, v in kwargs.items():
            setattr(self, k, v)

class ClassA(BaseClass):
    def __init__(self, *args, **kwargs):
        super(ClassA, self).__init__(*args, **kwargs)

class ClassB(ClassA):
    def __init__(self, a='A', b='B', c='C', *args, **kwargs):
        self.a = a
        self.b = b
        self.c = c
        super(ClassA, self).__init__(*args, **kwargs)


A = ClassA('hello', 'world', myname='hal',myemail='[email protected]')
B = ClassB('hello', 'world', myname='hal', myemail='[email protected]')

print("CLASS A:", A.__dict__)
print("CLASS B:", B.__dict__)
# yields the following:
CLASS A: {'args': ('hello', 'world'), 'myname': 'hal', 'myemail': '[email protected]'}
CLASS B: {'args': (), 'myname': 'hal', 'a': 'hello', 'myemail': '[email protected]', 'c': 'C', 'b': 'world'}

Код не будет генерировать исключение, однако a, b и c теперь позиционно являются первыми тремя аргументами, переданными методу (игнорируя self) , как видно из двух объектов dicts.

Доступ к атрибутам a, b и c показывает этот другой эффект

>>> B.a # expect to see 'A'
'hello'
>>> B.b # similarly
'world'
>>> B.c # but this retails the default value
'C'

Насколько мне известно, вы не можете добавлять в сигнатуру метода только аргументы нового ключевого слова. Пожалуйста, поправьте меня, если я ошибаюсь.

Однако и @aknuds1, и @Oleh Prypin предоставили решения, которые эффективно добавляют новые аргументы ключевого слова в дочерний класс. Решение Олега немного умнее, но мне проще понять версию aknuds1.

  • всплывающие элементы из словаря kwargs, объединенные со значением по умолчанию
  • присвоить атрибуту экземпляра с помощью setattr
  • вызвать родительский конструктор с аргументами, kwargs
person Haleemur Ali    schedule 14.12.2014
comment
Итак, подход Rawing на самом деле не добавляет аргументы ключевого слова: он добавляет позиционные аргументы (со значениями по умолчанию)? - person orome; 14.12.2014
comment
Да, подход Rawing добавит позиционные аргументы со значениями по умолчанию. Поддержка аргументов Key word only была добавлена ​​в python 3+, и она работает так, как вы ожидали, то есть def __init__(self, *args, a='A', b='B', c='C', **kwargs): является допустимым синтаксисом и делает именно это. - person Haleemur Ali; 14.12.2014
comment
Так что это ставит меня в затруднительное положение относительно принятия: ответ Равинга является лучшим для моих целей, и в целом, я думаю, учитывая желание иметь как можно больше информации в подписи (и что нет никаких требований - что я знаю о том, что конструкторы подклассов сохраняют порядок конструкторов в своих родительских классах); НО ваш ответ дает лучшее объяснение его ответа. Предложения? - person orome; 15.12.2014
comment
Ответы, опубликованные aknuds1 или Олегом Припиным, отвечают на ваш вопрос в том виде, в каком вы его задали (добавляя аргумент ключевого слова к методу, который переопределяет родителя). Подход Rawing, хотя и полезен для вас, не дает точного ответа на поставленный вопрос. И мой пост — не столько ответ на ваш вопрос, сколько объяснение того, что на самом деле делает код Rawing. - person Haleemur Ali; 16.12.2014
comment
Но, как указывает aknuds1, Rawing действительно отвечает на вопрос, поскольку действительно добавляет аргументы с ключевыми словами. И у этого есть дополнительное преимущество, заключающееся в том, что они помещаются в подпись. Разве это не правильно? - person orome; 16.12.2014
comment
Нет, это неправильно. Хотя предложенный Rawing метод является вполне допустимым способом добавления аргументов, он не добавляет аргументы ключевых слов. Он добавляет позиционные аргументы, позволяя операторам, таким как этот B = ClassB('hello', 'world'), который эквивалентен B = ClassB(a='hello', b='world') - person Haleemur Ali; 16.12.2014
comment
Так что комментарии aknuds1 неверны, верно? Хотя у меня было это (как я спрашиваю выше), подход Rawing добавляет позиционные аргументы (со значениями по умолчанию); но aknuds1, кажется, говорит об обратном: эти аргументы действительно являются аргументами ключевого слова. Я не знаю, кто прав. - person orome; 16.12.2014
comment
попробуйте сами, если вы можете передать аргумент без указания его имени, это не аргумент ключевого слова. - person Haleemur Ali; 16.12.2014
comment
@raxacoricofallapatorius Если вы читаете глоссарий Python, вы увидите, что аргумент ключевого слова < i>аргумент, которому предшествует идентификатор (например, name=) в вызове функции или переданный как значение в словаре, которому предшествует ** - person aknuds1; 16.12.2014
comment
Следовательно, является ли аргумент аргументом ключевого слова или нет, определяется тем, назван ли он при вызове рассматриваемой функции. - person aknuds1; 16.12.2014
comment
@aknuds1: я невинный свидетель. Я думаю, ты имеешь в виду Халимура Али. - person orome; 16.12.2014
comment
@raxacoricofallapatorius Ну, это адресовано вам обоим. - person aknuds1; 16.12.2014
comment
@aknuds1, спасибо за разъяснение. Я узнал что-то новое сегодня :) - person Haleemur Ali; 17.12.2014
comment
@aknuds1: Итак, в заключение: ... как указывает aknuds1, Rawing действительно отвечает на вопрос, поскольку он действительно добавляет аргументы ключевого слова. И у этого есть дополнительное преимущество, заключающееся в том, что они помещаются в подпись. - person orome; 17.12.2014
comment
@raxacoricofallapatorius Ну, согласно определению Python, на самом деле не имеет значения, имеют ли аргументы в подписи значения по умолчанию или нет (хотя такие аргументы могут быть позиционными). Но это более простой способ убедиться, что, например. аргумент a относится к классу B, а не к классу A. Моя альтернатива тоже работает, это зависит только от того, что вам нужно. - person aknuds1; 17.12.2014
comment
@aknuds1: Да, я думаю, мне придется согласиться с тем, что думает обычный читатель SO, когда сталкивается с этим вопросом (ваш ответ), хотя для меня (и в строгом техническом смысле) ответ Rawing более полезен ( и Халимур Али предоставляет более полную версию). - person orome; 17.12.2014

Используйте Python 3.x, который делает действительными аргументы, состоящие только из ключевых слов.

Или используйте этот обходной путь...

class ClassB(ClassA):
    def __init__(self, *args, **kwargs):
        for arg, default in [('a', 'A'), ('b', 'B'), ('c', 'C')]:
            setattr(self, arg, kwargs.pop(arg, default))
        super(ClassA, self).__init__(*args, **kwargs)
person Oleh Prypin    schedule 14.12.2014
comment
Так что нет возможности перечислить их в подписи — все, что я могу сказать, это **kwargs? - person orome; 14.12.2014
comment
В старых версиях Python не было возможности перечислить их в подписи. - person Oleh Prypin; 14.12.2014
comment
Давайте не будем говорить о 2 против 3. - person orome; 14.12.2014