如何定义两个相同的 类 方法参数名称不同?

How to define two identical classes with different method parameter names?

我正在开发一个科学图书馆,我将在其中定义时域和频域中的向量函数(由 FFT 链接)。我为频域中的矢量公式创建了一个 class,现在我想为时域定义一个相同的 class。

我希望在时域中,class 函数 - 尽管与它们的频域双胞胎相同 - 有一个名为 t 而不是 omega 的参数。 有没有更简单的方法来实现这一点,而不是重复定义每个方法,同时保持可读性?

我的代码:
(注意:我的 classes 要复杂得多,不能只使用 formula.x_func(...) 中的函数 - 包括一些检查等。此外,实际上有 6 个组件.)

class VecFormula(object):
    pass

class FreqFormula(VecFormula):

    def __init__(self, x_func, y_func, z_func):
        self.x_func = x_func
        self.y_func = y_func
        self.z_func = z_func

    def x(self, x, y, z, omega, params):
        return self.x_func(x, y, z, omega, params)

    def y(self, x, y, z, omega, params):
        return self.y_func(x, y, z, omega, params)

    def z(self, x, y, z, omega, params):
        return self.z_func(x, y, z, omega, params)

    def component(self, comp, x, y, z, omega, params):
        if comp == 'x':
            return self.x(x, y, z, omega, params)
        elif comp == 'y':
            return self.y(x, y, z, omega, params)
        elif comp == 'z':
            return self.z(x, y, z, omega, params)
        else:
            raise ValueError(f'invalid component: {comp}')


class TimeFormula(FreqFormula):
    "same as FreqFormula, but the omega parameter is renamed to t"
    
    def x(self, x, y, z, t, params):
        return super(TimeFormula, self).x(x, y, z, t, params)

    def y(self, x, y, z, t, params):
        return super(TimeFormula, self).y(x, y, z, t, params)

    def z(self, x, y, z, t, params):
        return super(TimeFormula, self).z(x, y, z, t, params)

    def component(self, comp, x, y, z, t, params):
        return super(TimeFormula, self).component(x, y, z, t, params)

听起来您可以通过优雅地使用 class 继承来实现您的需要。

请查看class inheritance python documentation or here

在创建后向 class 添加方法很容易,它们只是 class 属性。这里的难点在于,您需要动态创建新函数以从原始 class 克隆方法,以便能够更改它们的签名。

这不是Python最清楚的部分,动态函数创建在官方参考文档中没有记载,但可以在SO上找到:Python: dynamically create function at runtime

所以这里有一个可行的方法:

# have the new class derive from the common base
class TimeFormula(VecFormula):
    "same as FreqFormula, but the omega parameter is renamed to t"
    pass

# loop over methods of origina class
for i,j in inspect.getmembers(FreqFormula, inspect.isfunction):
    # copy the __init__ special method
    if i == '__init__':
        setattr(TimeFormula, i, j)
    elif i.startswith('__'): continue  # ignore all other special attributes
    if not j.__qualname__.endswith('.'.join((FreqFormula.__name__, i))):
        continue   # ignore methods defined in parent classes
    # clone the method from the original class
    spec = inspect.getfullargspec(j)
    newspec = inspect.FullArgSpec(['t' if i == 'omega' else i
                                   for i in spec.args], *spec[1:])
    f = types.FunctionType(j.__code__, j.__globals__, i, newspec, j.__closure__)
    f.__qualname__ = '.'.join((TimeFormula.__qualname__, i))
    # adjust the signature
    sig = inspect.signature(j)
    if ('omega' in sig.parameters):
        f.__signature__ = sig.replace(
            parameters = [p.replace(name='t') if name == 'omega' else p
                          for name, p in sig.parameters.items()])
    # and finally insert the new method in the class
    setattr(TimeFormula, i, f)