实现from_iterable替代c-tor的最佳方法是什么?

时间:2015-07-16 18:28:06

标签: python class

  

应该有一个 - 最好只有一个 - 显而易见的方法。

我开始使用这样的代码:

class Base(tuple):
    def __new__(cls, *args, **kw):
        return super().__new__(cls, args, **kw)

class Usual(Base):
    pass

class Optimized(Base): # optimized to case with 2 arguments
    def __init__(self, *_, **kw):
        super().__init__(**kw)
        if len(self) != 2:
            self.__class__ = Usual

我决定将from_iterable c-tor添加到Base,就像itertools.chain一样。很高兴。
然而,我无法在PyPy的源代码中找到用Python实现的chain,所以我决定找出自己的解决方案。

方法1 - 已加星标的表达

@classmethod
def from_iterable(cls, iterable):
    return cls(*iterable)

我想到的第一个。简单,可读,pythonic。但是,我 belive 主演的表达式很昂贵,所以如果它不能使代码更灵活,我就避免使用不必要的表达式。

方法2 - super().__new__

@classmethod
def from_iterable(cls, iterable):
    obj = super().__new__(cls, iterable)
    obj.__init__()
    return obj

使用Base.__new__而不是使用修改后的tuple.__new__,它基于iterable构造实例。工作很好,但让我担心__init__ - 它接收传递给__new__的位置参数,如果对象是通过调用类来创建的,但是当对象是对象时没有接收到位置参数(self除外)由from_iterable创建。这种不一致性不是问题,因为__init__无论如何都会忽略它们,但我担心在子类中覆盖__init__会导致细微的错误,很难追踪。

我可以写obj.__init__(*iterable),但之后这个解决方案比之前更糟糕 - 更复杂,更不易阅读,而且我仍然希望避免使用星号表达。

方法3 - 元类

class WithFromIterableCtor(type):
    def __call__(cls, *args):
        return super().__call__(args)
    def from_iterable(self, iterable):
        return super().__call__(iterable)

class Base(tuple, metaclass=WithFromIterableCtor):
    pass

class Optimized(Base):
    def __init__(self, iterable):
        if len(self) != 2:
            self.__class__ = Usual
            # self.__init__() # Usual has no __init__
        else:
            super().__init__()

这是我决定使用的解决方案。我编写了简单的元类来改变实例的创建。它以封装的方式处理问题。它也避免了星号表达。

然而,它使类混淆:__init__定义表明它需要迭代,但是要创建一个实例,你传递可变数量的参数...另外,是不是在这里使用元类过度?

我错过了更好的方法吗?如果不是,哪个灵魂更好 - 第一或第三?

1 个答案:

答案 0 :(得分:1)

这种(metaclasse方式)显然是而不是这种“明显的”方式。 一般来说,在“Python talk”中我们谈论“一种明显的”方式,这是最简单的方式(即使它可能不是最高效的方式)。在这些例子中,你走的方式,简单的方式只是因为你假设(在你的话中“相信”)主演的电话将是“昂贵的”。 嗯,事实并非如此。但即便如此,解决方案的收益也可能微不足道。

在Python中,除了“明显的......”之外还有其他的说法 - 其中之一就是“过早优化是万恶之源”。如果“python绝杀”上的一个短语适用于上述列表,那么这是迄今为止匹配最佳的短语。

IF - 和IF - 在你的项目工作之后,你决定有时间和需要进行一些优化,然后你可以使用一个分析器,并检查其他方法之一是否会给你一些收益(暗示:很可能不是 - 上面的“星形”打包/解包或参数是在本机代码中完成的;你用元类方法进行的旋转和转动只是添加了一层间接,并且很难显示在分析中)。但是,如果绝对远离光线,则使用元类方法添加的维护负担。我通常被认为是一个“大”的元类用户和支持者,我必须盯着你在那里做的事情(而且看起来它只是一个很大的无操作)。 (此外,它会隐藏from_iterable对你的类实例的普通内省 - 通常不是预期的。)

上面还有另一个巨大的反模式,它在初始化时改变了实例类。现在,如果你真的想要它,应该躺在__new__上,或者,也许,这对于元类来说是合适的用法。换句话说,为了清楚起见:根据输入选择创建的实例类应该在元类或新方法上完成,而不是__init__ ,如果你正在这样做。

但要比普通函数更好地用作实例工厂:这个函数根据收到的参数实例化适当类的对象:

def MyUbberTuple(*args, **kwargs):
    if len(args) == 2:
        return Usual(*args)
    return Optimized(*args)
相关问题