Выполнить метод родительского класса без вызова super () - PullRequest
1 голос
/ 04 апреля 2019

У нас есть следующая структура класса:

class NamePrinter():
    def phone():
        print(self.name)

    def email():
        print(self.name)


class PhoneCaller(NamePrinter):
    def __init__(self, name, number, mail):
        self.name = name
        self.number = number
        self.mail = mail

    def phone(self):
        # here, NamePrinter.phone() should be executed
        compose_number(self.number)

    def email(self):
        # here, NamePrinter.email() should be executed
        compose_mail(self.mail)

Я хочу, чтобы NamePrinter.phone() выполнялся при вызове PhoneCaller.phone(), без упоминания super.phone() в PhoneCaller.

Идея состоит в том, что единственная модификация, которая будет применена к PhoneCaller для того, чтобы он выполнял поведение NamePrinter при выполнении PhoneCaller.phone, состоит в том, что PhoneCaller наследует от родительского элемента, и ничего более.В частности, нет необходимости изменять какой-либо отдельный метод PhoneCaller.

Проще говоря:

  • PhoneCaller наследуется от NamePrinter => имя печатается перед составлением номера
  • PhoneCaller не наследуется от NamePrinter => имя не печатается
  • Нет необходимости возиться с PhoneCaller.phone

Возможно ли это?

Ответы [ 2 ]

2 голосов
/ 04 апреля 2019

Да, это возможно по крайней мере с метаклассом:

def new_attr(attr_name, attr):
    name_printer_attr = getattr(NamePrinter, attr_name)

    def _new_attr(self, *args, **kwargs):
        name_printer_attr(self)
        return attr(self, *args, **kwargs)

    return _new_attr


class Meta(type):
    def __new__(cls, name, bases, attrs):
        if name == 'NamePrinter':
            cls.attrs = attrs
        else:
            for attr_name, attr in attrs.items():
                if callable(attr) and attr_name in cls.attrs:
                    attrs[attr_name] = new_attr(attr_name, attr)
        return type.__new__(cls, name, bases, attrs)


class NamePrinter(metaclass=Meta):
    def phone(self):
        print('NamePrinter phone')


class PhoneCaller1:
    def phone(self):
        print('PhoneCaller1 phone')


class PhoneCaller2(NamePrinter):
    def phone(self):
        print('PhoneCaller2 phone')


p1 = PhoneCaller1()
p1.phone()  # will print only "PhoneCaller1 phone"
p2 = PhoneCaller2()
p2.phone()  # will print "NamePrinter phone" and "PhoneCaller2 phone" on next line
1 голос
/ 04 апреля 2019

И есть еще одно решение с декоратором.Это избавляет вас от неправильного использования наследования и является более понятным и гибким (ИМХО):

def new_attr(attr_name, attr, from_cls):
    from_cls_attr = getattr(from_cls, attr_name)

    def _new_attr(self, *args, **kwargs):
        from_cls_attr(self)
        return attr(self, *args, **kwargs)

    return _new_attr


def use_methods(from_cls):
    dir_from_cls = dir(from_cls)
    def modify(cls):
        for attr_name in dir(cls):
            if not attr_name.startswith('__') and attr_name in dir_from_cls:
                attr = getattr(cls, attr_name)
                if callable(attr):
                    setattr(cls, attr_name, new_attr(attr_name, attr, from_cls))
        return cls
    return modify


class NamePrinter:
    def phone(self):
        print('NamePrinter phone')


class PhoneCaller1:
    def phone(self):
        print('PhoneCaller1 phone')


@use_methods(NamePrinter)
class PhoneCaller2:
    def phone(self):
        print('PhoneCaller2 phone')


p1 = PhoneCaller1()
p1.phone()
p2 = PhoneCaller2()
p2.phone()
Добро пожаловать на сайт PullRequest, где вы можете задавать вопросы и получать ответы от других членов сообщества.
...