使__call__在基类中的特定接口可用于所有派生类

时间:2018-12-12 02:45:24

标签: python python-3.x

我有一个带有公共fn的基类。我想使层次结构系统中的所有类都可调用,并且它们的__call__实际上将其工作转换为fn,其语义类似于Class.__call__ = fn。以下代码显示了我当前的实现方式。

class Base:
    def fn(self, *input):
        # ...

    __call__ = fn

class Derived(Base):
    def fn(self, input):
        # ...

    __call__ = fn

我知道有一种设计只能在基类和__call__的函数体中定义一个__call__,以将其工作转换为fn,每个派生类实现自己的fn。语义就像“继承自Base的公共接口并实现,在此公共接口中,每个Derived类定义其自己的实现版本”。

但是,有些fn具有不同的参数列表,与我的上面所示的情况并不总是与基本列表相同。上面的代码可以工作,但是我想必须有更好的解决方案,而不必在层次结构系统的所有类中都重复定义。

因此,在基类中是否仅一次特定了__call__,但是使它可用于所有派生类?

2 个答案:

答案 0 :(得分:1)

如果您使用的Python版本> = 3.6,则可以编写Base类的__init_subclass__方法,该方法将子类的__call__属性分配为等于其fn属性:

class Base:
    def fn(self, *args):
        return args
    __call__ = fn
    def __init_subclass__(cls):
        cls.__call__ = cls.fn

class Derived(Base):
    def fn(self, arg):
        print("In derived")
        return arg


Base()(1, 2, 3)
# (1, 2, 3)

Derived()(1)
# In derived
# 1

如果您使用的是旧版本的Python,则可以通过元类实现类似的效果。

class BaseMeta(type):
    def __init__(cls, *args, **kwargs):
        super().__init__(*args, **kwargs)
        cls.__call__ = cls.fn

class Base(metaclass=BaseMeta):
    def fn(self, *args):
        return args

class Derived(Base):
    def fn(self, arg):
        print("In derived")
        return arg

答案 1 :(得分:0)

在您提供的示例中,无需使用__call__。已经存在一个非常好的机制,因为python将调用类构造函数。因此,只需def __init__(self, *args):,然后适当地调用该代码即可fn

您提到了不同的参数列表,但没有描述如何区分它们。如果这是一个简单的标准,例如len(args),那么您所拥有的就足够了。如果更复杂,则可能需要将**kwargs放在签名中,并让每种方法只挑选它可以识别的关键字参数。