用数据模型方法覆盖 * 和 ** 解包?

Posted

技术标签:

【中文标题】用数据模型方法覆盖 * 和 ** 解包?【英文标题】:Overwrite * and ** unpacking with datamodel methods? 【发布时间】:2021-12-16 15:31:54 【问题描述】:

我试图弄清楚如何让对象能够解包值。

我想出的用例如下:

让我们有一个 Interval 类,我们将使用它来评估实值函数。 我们想要求 会员,因此__contains__。 通过使用特定步骤调用或仅使用默认步骤对其进行迭代,因此__call____iter__。 以后我会支持联合、交集。

我正在寻找的功能之一是能够调用一个 Interval 对象并将其解包,这样如果 I = Interval(1, 2)a, b = *I 然后是 a == 1b == 2

这主要适用于 函数,它们的 参数中会有 间隔限制

例如函数def Integrate(f, a, b): ...,然后我们评估integral = Integrate(f, *I),其中$f$ 实值函数和I 是我们讨论的区间。

问题是我不完全确定应该使用哪种数据模型方法。

到目前为止,我想出的示例类如下。

class Interval:
    def __init__(self, a, b):
        if a >= b:
            raise Exception("Invalid values for an interval")
        self.upper = b
        self.lower = a

    def __contains__(self, value):
        return self.lower <= value <= self.upper

    def __call__(self, h=_h):
        yield from dense_range(self.lower, self.upper, h)

    def __iter__(self):
        return self()

    def __repr__(self):
        return f"<Interval [self.lower, self.upper]>"

有什么想法吗?

【问题讨论】:

听起来有一个endpoints 元组并做a, b = I.endpoints 会更有意义。 a, b = *I 不是有效的 Python 语法。 解压元组只是a, b = I。我相信你只需要实现__next__ @ddejohn:通常你只实现__iter__,而不是__next__Interval 似乎不是一个逻辑迭代器,但它是可迭代的)。您要么实现__iter__ 作为标识方法(除了return self 什么都不做)并提供一个“真实的”__next__(制作一个在这里看起来不合适的一次性迭代器),或者只实现__iter__(通常最容易这样做作为一个生成器函数)没有__next__,使一个可重用的迭代器(可以用来制作迭代器的东西)。 至于**,如果你使用**,我不清楚你会期望它做什么(我看不出它会解压成什么合乎逻辑的名字)。 【参考方案1】:

我没有很多实现迭代器的经验,但我相信您所需要的只是从__iter__ 产生相关项目:

class Interval:
    def __init__(self, a, b):
        if a >= b:
            raise Exception("Invalid values for an interval")
        self.upper = b
        self.lower = a

    def __contains__(self, value):
        return self.lower <= value <= self.upper

    def __call__(self, h=_h):
        yield from dense_range(self.lower, self.upper, h)

    def __iter__(self):
        yield self.lower
        yield self.upper

    def __repr__(self):
        return f"<Interval [self.lower, self.upper]>"

一些用途:

In [5]: interval = Interval(1, 7)

In [6]: a, b = interval

In [7]: a
Out[7]: 1

In [8]: b
Out[8]: 7

In [9]: [*interval]
Out[9]: [1, 7]

In [10]: [value for value in interval]
Out[10]: [1, 7]

In [11]: dict(zip("ab", interval))
Out[11]: 'a': 1, 'b': 7

In [12]: a, b, *c = interval

In [13]: a
Out[13]: 1

In [14]: b
Out[14]: 7

In [15]: c
Out[15]: []

In [16]: a, *b, c = interval

In [17]: a
Out[17]: 1

In [18]: b
Out[18]: []

In [19]: c
Out[19]: 7

【讨论】:

不过,这破坏了提问者也想要的迭代功能。无法将可迭代解包和迭代的行为分开。 我不确定我是否理解您的评论。 OP 希望它不支持哪些功能? 他们希望for x in I 产生与*I 不同的元素。 请注意问题中的要求,“通过使用特定步骤调用或仅使用默认步骤对其进行迭代,因此__call____iter__。” 它不会干扰__call__,如果提问者使用for x in I(),他们仍然会得到他们想要的行为。然而,他们希望for x in I 的行为与for x in I() 相同,因此他们的__iter__ 方法与return self() 相同。

以上是关于用数据模型方法覆盖 * 和 ** 解包?的主要内容,如果未能解决你的问题,请参考以下文章

覆盖 Django 模型 __init__ 方法

覆盖 ExtJS 模型 getter 和 setter 的最佳实践

[Unreal]UE4 虚幻4 凹面体模型物理碰撞框架覆盖孔洞的解决方法

测试设计

Django覆盖模型get()方法不适用于外键

如何以 django 模型形式覆盖保存方法