Вызов super () .__ init __ (** kwargs) и множественное наследование? - PullRequest
0 голосов
/ 27 мая 2018

Я пытаюсь понять и понять, как использовать 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 здесь?

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

Это скриншот примера из книги, за которым я следую:

enter image description here

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

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

https://pastebin.com/NYGJfMik

Ответы [ 2 ]

0 голосов
/ 27 мая 2018

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

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) достаточно, чтобы решить проблему, супер и **kwargs очень полезны вместе.Давайте сначала посмотрим, почему super полезен.Давайте представим, что мы пишем некоторые обертки вокруг подпроцессов с некоторыми хорошими вспомогательными методами (архитектура, возможно, не совсем подходит для этой проблемы, но только когда-либо видение животных с наследованием также не супер полезно. Многократное наследование - действительно редкий случай, так что это трудночтобы привести хорошие примеры, которые не являются Animals, 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() в init процесса, но в случаях реального мира это не всегда так легко исправить, как здесь.В этом случае вызов 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__ s просто игнорировать клавиатуру:

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) будет успешным, каждый инициализатор берет только те ключевые слова, которые он понимает, и просто передает другие дальше.

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

0 голосов
/ 27 мая 2018

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

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
Добро пожаловать на сайт PullRequest, где вы можете задавать вопросы и получать ответы от других членов сообщества.
...