【问题标题】:Execute parent class method without calling super()在不调用 super() 的情况下执行父类方法
【发布时间】:2019-04-04 09:18:39
【问题描述】:

我们有以下类结构:

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)

我希望在调用PhoneCaller.phone() 时执行NamePrinter.phone(),而不必在PhoneCaller 中提及super.phone()

这个想法是,要应用于 PhoneCaller 以使其在执行 PhoneCaller.phone 时执行 NamePrinter 的行为的唯一修改是 PhoneCaller 从父级继承,仅此而已。特别是,无需修改任何单独的 PhoneCaller 方法。

简单地说:

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

这可能吗?

【问题讨论】:

  • class NamePrinter 中重命名为def _phone(self):,在def phone(self): 中做self._phone()
  • @stovfl 与调用 super() 有何不同?
  • 前两个要点似乎暗示您可能会修改PhoneCaller 的基类。如果是这种情况,为什么不首先定义两个类,然后正确定义 NamePrinter 的子类呢?这似乎是一个巨大的 XY 问题。
  • 参见import this:“显式优于隐式。”
  • @chepner 现实世界的场景是我们有几十个不同的子类,我们想要一个简单的开关来触发替代行为,这取决于这个开关是打开还是关闭

标签: python python-3.x class inheritance


【解决方案1】:

是的,至少可以使用元类:

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

【讨论】:

  • 看起来不错。不得不通过这些恶作剧来实现一个相当简单的概念,这对 Python 来说是一种耻辱……不过答案很好。
  • @Jivan 在运行时重写现有代码并不“简单”。
  • @chepner 我很想听听这个用例的更好的解决方案,如果你有一个,我完全开放
  • 我会放弃不能修改PhoneCaller的限制。
【解决方案2】:

还有另一个使用装饰器的解决方案。它使您免于滥用继承,并且更加清晰和灵活(恕我直言):

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

【讨论】:

    猜你喜欢
    • 2021-05-06
    • 1970-01-01
    • 2012-11-14
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 2022-01-06
    • 1970-01-01
    相关资源
    最近更新 更多