执行父类方法而无需调用super()

时间:2019-04-04 09:18:40

标签: python python-3.x class inheritance

我们具有以下类结构:

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(),而不必在PhoneCaller中提及super.phone()

这个想法是,为了执行PhoneCaller.phone时执行NamePrinter的行为而对PhoneCaller进行的唯一修改是,PhoneCaller继承自父对象,仅此而已。特别是,无需修改任何单独的PhoneCaller方法。

简单点说

  • PhoneCaller继承自NamePrinter =>名称在组成号码之前被打印
  • PhoneCaller不继承自NamePrinter =>名称未打印
  • 无需弄乱PhoneCaller.phone

这可能吗?

2 个答案:

答案 0 :(得分:2)

是的,至少有可能使用元类:

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 :(得分:1)

还有一个带有装饰器的解决方案。它可以避免滥用继承,并且更加清晰灵活(IMHO):

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()