【问题标题】:Which interface for __init__ and which for classmethod__init__ 的哪个接口和 classmethod 的哪个接口
【发布时间】:2023-04-10 10:46:01
【问题描述】:

我正在编写一些 Python 代码,我们希望有几个不同的接口,一个公开为__init__,另一个通过@classmethod 公开。我正在寻找关于哪个界面应该去哪里的指导。

如果我们想象一个类计算具有不同幅度、相位和频率的正弦波之和,并且存储在对象中的是每个的数组。 __init__ 可能是:

class waveform:
    def __init__(self, amplitudes, phases, frequencies):
        self.amplitudes = amplitudes
        self.phases = phases
        self.frequencies = frequencies

为了允许将单个正弦波的参数组合在一起,我们添加了一个新对象,如下所示:

class singlewave:
    def __init__(self, amplitude, phase, frequency):
        self.amplitude = amplitude
        self.phase = phase
        self.frequency = frequency

然后我们可以在波形中添加一个@classmethod

class waveform:
    def __init__(self, amplitudes, phases, frequencies):
        self.amplitudes = amplitudes
        self.phases = phases
        self.frequencies = frequencies

    @classmethod
    def from_waves(cls, listOfWaves):
        amplitudes = [w.amplitude for w in listOfWaves]
        phases = [w.phase for w in listOfWaves]
        frequencies = [w.frequency for w in listOfWaves]
        return cls(amplitudes, phases, frequencies)

或者,我们可以反过来让waveform 看起来像这样:

class waveform:
    def __init__(self, listOfWaves):
        self.amplitudes = [w.amplitude for w in listOfWaves]
        self.phases = [w.phase for w in listOfWaves]
        self.frequencies = [w.frequency for w in listOfWaves]

    @classmethod
    def from_components(cls, amplitudes, phases, frequencies):
        waves = []
        for a, p, f in zip(amplitudes, phases, frequencies):
            waves.append(singlewave(a, p, f))
        return cls(waves)

我发现第一个更优雅,因为它不需要在@classmethod 中创建额外的对象,并且__init__ 的参数与对象的属性直接相关。其他人更喜欢第二个,因为 __init__ 然后涉及将幅度、相位和频率绑定到一个对象中。

是否有一些规则或惯例表明其中一种优于另一种,或者纯粹取决于个人喜好?

谢谢。

【问题讨论】:

    标签: python interface


    【解决方案1】:

    我认为最好将此问题视为类型强制,这两种表示是等价的,我希望用户能看到它们。

    这两种方法都很好,我希望在您的界面中都能找到它们 毕竟。

    我要做的就是让__init__ 使用起来更愉快。

    class waveform:
        def __init__(self, *args, from=None):
            if from is None:
                from = self._peek_args(args)
    
            if from == "waves":
                ...
            elif from == "components":
               ...
            else:
               ...
        def _peek_args(self, args):
                # here you have to inspect the *args tuple
                # and guess what kind of input you got
                # rememeber to raise warnings and ValueError
                # to inform your user that he made mistakes
    

    问题是现在你的__init__ 是可变参数并且你有 写一篇非常好的文档来记录可用的内容 以及它将如何识别它。

    我还要提一下,用户可以使用_peek_args 测试输入 方法,考虑一下它可以是一个类方法,这样更容易测试。

    你甚至可以扩展这个类 通过寻找用户可以使用的__from_X 方法 当他子类化时插入。

    class waveform:
        def __init__(self, *args, from=None):
            try:
                ingest = gettattr(self, '__from_{}'.format(from))
                ingest(*args)
            except AttributeError as e:
                raise ValueError("from={} is not supported".format(from))
        ...
    

    【讨论】:

    • 我会非常犹豫这样做。如果我们决定添加额外的 from_x 选项,它将为许多 it 语句打开可能性,并且会使文档和清晰度变得更加困难。
    • 是的,您是对的,这将是很多条件,您支持的越多,检查__from_x 方法就越有意义,我会将其添加到答案中,但是最后,它真的归结为你希望它被强制的容易程度。
    • 这样更好,但我认为工厂方法将更容易记录和理解,并且更不容易出错。
    • 那是你的答案!我现在不会太费心,只是把它们都放了。
    • 我想你可能误解了这个问题。我想包括两者,问题是是否有充分的理由让其中一个成为__init__
    猜你喜欢
    • 2012-05-03
    • 2023-04-01
    • 1970-01-01
    • 2023-03-21
    • 2011-02-03
    • 1970-01-01
    • 2013-12-01
    • 2010-11-19
    • 2014-02-28
    相关资源
    最近更新 更多