Вызов super () .__ init __ (** kwargs) и множественное наследование?

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

Например, у меня работает такой метод:

class Employee:        
    def __init__(self, firstname, lastname, age, sex, dob):
        self.firstname = firstname
        self.lastname = lastname
        self.age = age 
        self.sex = sex
        self.dob = dob
        self.all_staff.append(self)

class Hourly(Employee):
    def __init__(self, firstname, lastname, age, sex, dob, rate, hours):
        self.rate = rate
        self.hours = hours
        super().__init__(firstname, lastname, age, sex, dob)

    def __str__(self):
    return "{} {}\nAge: {}\nSex: {}\nDOB: {}\n".format(self.firstname, self.lastname, self.age, 
        self.sex, self.dob)

    def get_rate(self):
        print('The hourly rate of {} is {} '.format(self.firstname, self.rate))

hourlystaff1 = Hourly('Bob', 'Foo', '23', 'M', '12/1/1980', '$15', '30')

print(hourlystaff1)

print(hourlystaff1.get_rate())

возвращает следующее:

Bob Foo
Age: 23
Sex: M
DOB: 12/1/1980

The hourly rate of Bob is $15 
None

Это то, что я ожидал (хотя я не уверен, почему также возвращается «None», возможно, кто-нибудь сможет объяснить?).

А потом я хотел попробовать это с помощью super, но с такими ** kwargs:

class Employee:
    def __init__(self, firstname='', lastname='', age='', dob='', **kwargs):
        super().__init__(**kwargs)
        self.firstname = firstname
        self.lastname = lastname
        self.age = age 
        self.dob = dob 

class Hourly(Employee):

    def __init__(self, rate=''):
        self.rate = rate
        super().__init__(**kwargs)

    def __str__(self):
        return "{} {}\nAge: {}\nSex: {}".format(self.firstname, self.lastname, self.age, 
            self.sex, self.dob, self.rate)

    def get_rate(self):
        print('The hourly rate of {} is {} '.format(self.firstname, self.rate))

bob = Hourly('Bob', 'Bar', '23', '12/1/2019')


bob.get_rate('$12')

возвращает эту ошибку:

  File "staff_b.py", line 33, in <module>
    bob = Hourly('Bob', 'Bar', '23', '12/1/2019')
TypeError: __init__() takes from 1 to 2 positional arguments but 5 were given

что я делаю не так во втором подходе? Как мне здесь правильно использовать ** kwargs и super?

Редактировать:

это скриншот примера из книги, за которой я следил:

Вызов super () .__ init __ (** kwargs) и множественное наследование?

в чем разница между тем, как я использую ** kwargs и super во втором примере?

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

https://pastebin.com/NYGJfMik

Что касается None, это потому, что ваш метод get_rate ничего не возвращает. См. Чем возврат вывода функции отличается от его печати?. Что касается **kwargs: вы их не туда положили. Их следует использовать в методе Hourly.__init__. И Employee не должен называть super().__init__(**kwargs) или super().__init__() или что-то в этом роде.

Aran-Fey 27.05.2018 09:59

Хорошо, спасибо, так что для второго я должен просто удалить ** kwargs из Employee в этом, а также super, и он должен работать? Я сделал это, но все равно получаю ту же ошибку.

goblin_rocket 27.05.2018 10:12

Удалите их из Employee и добавьте их в Hourly: def __init__(self, rate='', **kwargs):

Aran-Fey 27.05.2018 10:13

Как этот pastebin.com/xsrEBDpx? Это по-прежнему возвращает ту же ошибку. Также, пожалуйста, смотрите мои изменения в сообщениях выше, спасибо.

goblin_rocket 27.05.2018 10:17

Ах, это потому, что вы передаете их как позиционные аргументы, а не аргументы ключевого слова. Вы можете поменять все свои **kwargs на *args, **kwargs. Но обратите внимание, что вы передаете 'Bob' в качестве значения параметра rate, поэтому, возможно, вам следует просто использовать аргументы ключевого слова, чтобы избежать подобных ошибок.

Aran-Fey 27.05.2018 10:20

Хорошо, теперь я понимаю вроде, как бы, что-то вроде, но что эти примеры из книги делают по-другому с ** kwargs?

goblin_rocket 27.05.2018 10:31

Не уверен, что вы имеете в виду. Не думаю, что они делают что-то по-другому.

Aran-Fey 27.05.2018 10:35

Ах, думаю, я понимаю, о чем вы говорите. Классы в книге предназначены для множественного наследования. (Вы можете видеть, что Friend наследуется как от Contact, так и от AddressHolder.) Вы можете найти мой ответ здесь полезным.

Aran-Fey 27.05.2018 10:39

Хорошо, спасибо, я прочитаю этот ответ, до сих пор не понимаю всего этого, но добираюсь до цели медленно! Не стесняйтесь отвечать, и я приму это :)

goblin_rocket 27.05.2018 10:41

Я не уверен, стоит ли мне это делать. В вашем коде так много отдельных мелких проблем, что он, вероятно, слишком широк, чтобы хорошо подходить для StackOverflow.

Aran-Fey 27.05.2018 10:44
Почему в Python есть оператор "pass"?
Почему в Python есть оператор "pass"?
Оператор pass в Python - это простая концепция, которую могут быстро освоить даже новички без опыта программирования.
Некоторые методы, о которых вы не знали, что они существуют в Python
Некоторые методы, о которых вы не знали, что они существуют в Python
Python - самый известный и самый простой в изучении язык в наши дни. Имея широкий спектр применения в области машинного обучения, Data Science,...
Основы Python Часть I
Основы Python Часть I
Вы когда-нибудь задумывались, почему в программах на Python вы видите приведенный ниже код?
LeetCode - 1579. Удаление максимального числа ребер для сохранения полной проходимости графа
LeetCode - 1579. Удаление максимального числа ребер для сохранения полной проходимости графа
Алиса и Боб имеют неориентированный граф из n узлов и трех типов ребер:
Оптимизация кода с помощью тернарного оператора Python
Оптимизация кода с помощью тернарного оператора Python
И последнее, что мы хотели бы показать вам, прежде чем двигаться дальше, это
Советы по эффективной веб-разработке с помощью Python
Советы по эффективной веб-разработке с помощью Python
Как веб-разработчик, Python может стать мощным инструментом для создания эффективных и масштабируемых веб-приложений.
3
10
4 728
2
Перейти к ответу Данный вопрос помечен как решенный

Ответы 2

Это должно работать

class Employee:
    def __init__(self, firstname='', lastname='', age='', dob=''):
        self.firstname = firstname
        self.lastname = lastname
        self.age = age 
        self.dob = dob 

Тогда у вас есть дочерний класс

class Hourly2(Employee):
    def __init__(self,*args, **kwargs):
        super(Hourly2,self).__init__(*args, **kwargs)

    def get_rate(self,rate=None):
    self.data=rate
    print ('My hourly rate is {}'.format(self.data))

Теперь давайте создадим пример

bob = Hourly2('Bob', 'Bar', '23', '12/1/2019')

Мы можем проверить атрибуты

dir(bob)
['__class__',
 '__delattr__',
 '__dict__',
 '__dir__',
 '__doc__',
 '__eq__',
 '__format__',
 '__ge__',
 '__getattribute__',
 '__gt__',
 '__hash__',
 '__init__',
 '__init_subclass__',
 '__le__',
 '__lt__',
 '__module__',
 '__ne__',
 '__new__',
 '__reduce__',
 '__reduce_ex__',
 '__repr__',
 '__setattr__',
 '__sizeof__',
 '__str__',
 '__subclasshook__',
 '__weakref__',
 'age',
 'dob',
 'firstname',
 'get_rate',
 'lastname']

Ну наконец то

bob.age
'23'

А также

bob.get_rate('$12')
My hourly rate is $12

Этот дочерний класс совершенно бессмыслен. Зачем нужен метод, который принимает аргумент и возвращает его? И если вы собираетесь присвоить этот аргумент атрибуту, почему он называется «получать_rate»? И вообще, почему бы не использовать rate в качестве параметра конструктора?

Aran-Fey 27.05.2018 11:01

Да, но он сделал нечто совершенно иное, чем ваше.

Aran-Fey 27.05.2018 11:03

Я внес правку, можете ли вы привести пример или ссылку относительно параметра конструктора.

Richard Rublev 27.05.2018 11:30

здесь не рассматривается, как работать с бородавками и super, что, если я не хочу явно указывать параметры в __init __ () super?

Rodrigo Rivera 12.09.2018 19:02
Ответ принят как подходящий

Проблема, которую вы здесь видите, на самом деле не специфична для super, а более специфична для kwargs. Если мы выбросим большую часть вашего кода и удалим супер, это будет выглядеть так:

class Hourly(Employee):

    def __init__(self, rate=''):
        self.rate = rate
        some_crazy_function(**kwargs)

hourlystaff1 = Hourly('Bob', 'Foo', '23', 'M', '12/1/1980', '$15', '30')

Есть две очевидные проблемы: функция __init__ получает больше аргументов, чем ожидалось, а в теле функции __init__ есть ссылка на kwargs, которая нигде не определена. Хотя здесь понимания **kwargs (и его брата *args) достаточно, чтобы решить проблему, здесь super и **kwargs очень полезны вместе. Давайте сначала посмотрим, чем полезен super. Давайте представим, что мы пишем несколько оболочек для подпроцессов с некоторыми хорошими вспомогательными методами (архитектура, возможно, не наилучшим образом подходит для проблемы, но только когда-либо видеть животных с наследованием также не очень полезно. Множественное наследование - действительно редкий случай, поэтому это сложно чтобы придумать хорошие примеры, которые не являются животными, GameEntities или GUIwidgets):

class Process:
    def __init__(self, exe):
        self.exe = exe
        self.run()

class DownloadExecutableBeforeProcess(Process):
    def __init__(self, exe):
        self.download_exe(exe)
        Process.__init__(self, exe)

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

class AuthenticationCheckerProcess(Process):
    def __init__(self, exe, use_sha=True):
        self.check_if_authorized(exe, use_sha)
        Process.__init__(self, exe)

class DownloadAndCheck(DownloadExecutableBefore, AuthenticationCheckerProcess):
    def __init__(self, exe):
        DownloadExecutableBefore.__init__(exe)
        AuthenticationCheckerProcess.__init__(exe, use_sha=False)

Если мы проследим за инициализацией DownloadAndCheck, мы увидим, что Process.__init__ вызывается дважды: один раз через DownloadExecutableBefore.__init__ и один раз через AuthenticationCheckerProcess.__init__! Итак, наш процесс, который мы хотим обернуть, также запускается дважды, а это не то, что нам нужно. Здесь, в этом примере, мы могли бы легко исправить это, не вызывая self.run() в инициализации процесса, но в реальных случаях это не всегда так легко исправить, как здесь. В данном случае вызов Process.__init__ кажется неправильным. Можно как-то это исправить?

class DownloadAndCheck(DownloadExecutableBefore, AuthenticationCheckerProcess):
    def __init__(self, exe):
        super().__init__(exe, use_sha=False)
        # also replace the Process.__init__ cals in the other classes with super

super устраняет эту проблему и вызывает Process.__init__ только один раз. Он также позаботится о порядке, в котором должна выполняться функция, но здесь это не является большой проблемой. У нас все еще есть проблема: use_sha=False будет передан инициализаторам все, но на самом деле он нужен только одному. На самом деле мы не можем передать переменную только тем функциям, которые в ней нуждаются (потому что выяснить это было бы кошмаром), но мы можем научить другие __init__ просто игнорировать клавиатуру:

class Process:
    def __init__(self, exe, **kwargs):
        # accept arbitrary keywoards but ignore everything but exe
        # also put **kwargs in all other initializers
        self.exe = exe
        self.run()

class DownloadExecutableBeforeProcess(Process):
    def __init__(self, exe, **kwargs):
        self.download_exe(exe)
        # pass the keywoards into super so that other __init__s can use them
        Process.__init__(self, exe, **kwargs)

Теперь вызов super().__init__(exe, use_sha=False) будет успешным, каждый инициализатор берет только те клавиатуры, которые он понимает, и просто передает остальные ниже.

Итак, если у вас есть множественное наследование и вы используете разные (ключевые) аргументы, super и kwargs могут решить вашу проблему. Но супер- и множественное наследование - это сложно, особенно если у вас больше слоев наследования, чем здесь. Иногда порядок, в котором должны вызываться функции, даже не определен (и тогда python должен выдать ошибку, см., Например, объяснение изменения алгоритма ТОиР). Примеси могут даже потребовать вызова super().__init__(), хотя они даже не наследуются ни от одного класса. В целом, вы усложняете свой код, если используете множественное наследование, поэтому, если оно вам действительно не нужно, часто лучше подумать о других способах моделирования вашей проблемы.

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

Aran-Fey 27.05.2018 16:21

Моя единственная жалоба по этому вопросу связана с вашим заявлением: «Если вы наследуете только один класс super, вам не нужен super». Это определенно верно, но использование super по-прежнему является хорошей практикой, хотя бы потому, что это позволяет избежать необходимости жесткого кодирования имени родительского класса. Я боюсь, что то, как вы сформулировали это предложение, может отговорить людей от использования super без уважительной причины.

Aran-Fey 27.05.2018 16:25

@ Аран-Фей Спасибо. Мне кажется, что для понимания вопроса в цепочке, которую вы связали, уже требуется базовое понимание super, которое я попытался передать в своем ответе. Я действительно не нашел SO-вопроса, который требует базовых знаний, представленных в моем ответе, хотя связанная запись в блоге, вероятно, лучший ресурс по super, который я нашел на сегодняшний день. Я немного изменил формулировку, чтобы включить вашу жалобу, хотя лично я предпочитаю избегать использования super, если он не нужен.

syntonym 27.05.2018 16:56

спасибо, отличный ответ, и мне определенно нужно больше читать / практиковаться с этим

goblin_rocket 28.05.2018 04:27

@goblin_rocket Если вы еще не видели его, эта статья в блоге, также связанный в ответе, на который ссылается Аран-Фей, действительно хорош. Единственное, чего не хватает, - это миксины, где вам нужно вызвать super, хотя у него нет суперкласса.

syntonym 28.05.2018 16:04

Другие вопросы по теме