Почему можно вызвать метод, которого суперкласс не имеет - PullRequest
1 голос
/ 18 мая 2019

Я читаю исходный код, реализующий алгоритм paxos с Python 2.7. В коде есть много методов, которые вызывают методы суперкласса, которых в суперклассе точно нет, и имена этих двух методов всегда одинаковы. Это особенность Python? например. супер (SimpleSynchronizationStrategyMixin, самость) .set_messenger (мессенджер) Суперклассом SimpleSynchronizationStrategyMixin является объект, не содержащий метод "set_messenger". точно имя метода, которому принадлежит эта строка кода, также "set_messenger"

class SimpleSynchronizationStrategyMixin(object):

    sync_delay = 10.0

    def set_messenger(self, messenger):
        super(SimpleSynchronizationStrategyMixin,self).set_messenger(messenger)

        def sync():
            self.messenger.send_sync_request(random.choice(self.peers), self.instance_number)

        self.sync_task = task.LoopingCall(sync)
        self.sync_task.start(self.sync_delay)


    def receive_sync_request(self, from_uid, instance_number):
        if instance_number < self.instance_number:
            self.messenger.send_catchup(from_uid, self.instance_number, self.current_value)


    def receive_catchup(self, from_uid, instance_number, current_value):
        if instance_number > self.instance_number:
            print 'SYNCHRONIZED: ', instance_number, current_value
            self.advance_instance(instance_number, current_value, catchup=True)

1 Ответ

1 голос
/ 19 мая 2019

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

class SimpleSynchronizationStrategyMixin(object):

    def set_messenger(self, messenger):
        super(SimpleSynchronizationStrategyMixin,self).set_messenger(messenger)
        print('test')


test = SimpleSynchronizationStrategyMixin()
test.set_messenger(None)

выдаст ошибку, как и следовало ожидать:

AttributeError: 'super' object has no attribute 'set_messenger'

Однако, имя класса показывает ответ: itкласс "mixin", класс, который предназначен для смешивания с другим классом .Таким образом, класс добавляет только определенную функциональность и может делать предположения о методах, присутствующих в суперобъекте (который не object в случае конечного объекта).

Для демонстрациидавайте расширим приведенный выше пример следующим кодом:

class SomeOtherClass(object):

    def set_messenger(self, messenger):
        print('setting the messenger to %s.' % repr(messenger))


class Demo(SimpleSynchronizationStrategyMixin, SomeOtherClass):

    def demo(self):
        self.set_messenger('some messenger')


demo = Demo()
demo.demo()

Здесь метод set_messenger определен в SomeOtherClass.Класс Demo затем использует SomeOtherClass с миксином для создания конечного объекта.Когда вызывается demo(), он печатает:

setting the messenger to 'some messenger'.
test

Обратите внимание, что порядок суперклассов важен.Если вы напишите class Demo(SomeOtherClass, SimpleSynchronizationStrategyMixin), строка «test» не будет напечатана.

Для вашего конкретного примера paxos см. server.py, который содержит:

class ReplicatedValue(DedicatedMasterStrategyMixin, ExponentialBackoffResolutionStrategyMixin, SimpleSynchronizationStrategyMixin, BaseReplicatedValue):
    """
    Mixes the dedicated master, resolution, and synchronization strategies into the base class
    ""

Здесь вы можетеобратите внимание, что ряд классов mixin «добавлен» в BaseReplicatedValue для создания ReplicatedValue.

Обратите внимание, что super() не всегда возвращает «родительский» объект.Если метод DedicatedMasterStrategyMixin.propose_update(self, ...) вызывает super(DedicatedMasterStrategyMixin, self).propose_update(...), он находит следующий метод propose_update() в соответствии с Порядком разрешения метода (MRO) .Проще говоря, он просматривает все базовые классы слева направо и возвращает первый найденный метод.Таким образом, каждый метод может вызывать super(), не зная, какой класс является «родительским», поэтому все необходимые методы могут быть объединены в цепочку.Следующий пример кода демонстрирует это:

class Base(object):
    def test(self):
        # NB: No super() call in the base class!
        print('Test Base')
        print('')

class MixinX(object):
    def test(self):
        print('Test X')
        super(MixinX, self).test()

class MixinY(object):
    def test(self):
        print('Test Y')
        super(MixinY, self).test()

class MixinZ(object):
    def test(self):
        print('Test Z')
        super(MixinZ, self).test()

class Final(Base):
    pass

class FinalX(MixinX, Base):
    pass

class FinalXYZ(MixinX, MixinY, MixinZ, Base):
    pass

class FinalZYX(MixinZ, MixinY, MixinX, Base):
    pass

class WrongOrder(Base, MixinX, MixinY, MixinZ):
    pass

class MixinsOnly(MixinX, MixinY, MixinZ):
    pass


"""
>>> Final().test()
Test Base

>>> FinalX().test()
Test X
Test Base

>>> FinalXYZ().test()
Test X
Test Y
Test Z
Test Base

>>> FinalZYX.test()
Test Z
Test Y
Test X
Test Base

>>> WrongOrder().test()
Test Base

>>> MixinsOnly().test()
Test X
Test Y
Test Z
Traceback (most recent call last):
  File "superdemo.py", line 36, in <module>
    MixinsOnly().test()
  File "superdemo.py", line 9, in test
    super(MixinX, self).test()
  File "superdemo.py", line 14, in test
    super(MixinY, self).test()
  File "superdemo.py", line 19, in test
    super(MixinZ, self).test()
AttributeError: 'super' object has no attribute 'test'

>>> FinalXYZ.mro()
[<class '__main__.FinalXYZ'>, <class '__main__.MixinX'>, <class '__main__.MixinY'>, <class '__main__.MixinZ'>, <class '__main__.Base'>, <type 'object'>]
"""
...