我可以更改 python 子类中的基类值吗?

发布于 2024-11-18 22:33:53 字数 1513 浏览 3 评论 0原文

当子类化基本类型(例如 float)时,是否可以“重新计算”或“重新分配”原始值?如果我有以下类定义,

import collections

class MovingAverage(float):
    def __new__(self, initial_value, window):
        self.d = collections.deque([initial_value], window)
        return float.__new__(self, initial_value)
    def add(self, new_value):
        self.d.append(new_value)
        print sum(self.d) / len(self.d)
        # here, I want to _set_ the value of MovingAverage to
        #     sum(self.d) / len(self.d)

当我从

>>> ma = MovingAverage(10, 3)
>>> ma
10.0

but

>>> ma.add(3)
6.5
>>> ma
10.0

开始时,我尝试过的另一个类定义是这样的:

import collections

class MovingAverage(float):
    def __new__(self, iterable, window):
        self.d = collections.deque(iterable, window)
        initial_value = sum(iterable) / len(iterable)
        return float.__new__(self, initial_value)
    def add(self, new_value):
        self.d.append(new_value)
        return MovingAverage(self.d, self.d.maxlen)

这次,当我从

>>> ma = MovingAverage([10], 3)
>>> ma
10.0

and

>>> ma.add(3)
6.5
>>> ma
10.0
>>> ma = ma.add(3)
>>> ma
5.333333333333333

开始时,我认为(我还没有测试发现)这使得这显着慢点。那么,可以吗?我可以以某种方式设置它,以便从 ma 返回的值就是我正在寻找的值吗?或者我是否需要定义一个 value 属性,将基类更改为 object,并放弃我有机会控制该类的返回值的假象?

When subclassing a base type, like float, is it possible to "recalculate" or "reassign" the original value? If I have the following class definition,

import collections

class MovingAverage(float):
    def __new__(self, initial_value, window):
        self.d = collections.deque([initial_value], window)
        return float.__new__(self, initial_value)
    def add(self, new_value):
        self.d.append(new_value)
        print sum(self.d) / len(self.d)
        # here, I want to _set_ the value of MovingAverage to
        #     sum(self.d) / len(self.d)

When I start with

>>> ma = MovingAverage(10, 3)
>>> ma
10.0

but

>>> ma.add(3)
6.5
>>> ma
10.0

The other class definition I've tried is this:

import collections

class MovingAverage(float):
    def __new__(self, iterable, window):
        self.d = collections.deque(iterable, window)
        initial_value = sum(iterable) / len(iterable)
        return float.__new__(self, initial_value)
    def add(self, new_value):
        self.d.append(new_value)
        return MovingAverage(self.d, self.d.maxlen)

This time, when I start with

>>> ma = MovingAverage([10], 3)
>>> ma
10.0

and

>>> ma.add(3)
6.5
>>> ma
10.0
>>> ma = ma.add(3)
>>> ma
5.333333333333333

However, I think (I haven't tested to find out) it makes this significantly slower. So, can it be done? Can I somehow set it so that the return from ma is the value that I'm looking for? Or do I need to define a value attribute, change the base class to object, and abandon my pretense that I have a chance of controlling the return value of the class?

如果你对这篇内容有疑问,欢迎到本站社区发帖提问 参与讨论,获取更多帮助,或者扫码二维码加入 Web 技术交流群。

扫码二维码加入Web技术交流群

发布评论

需要 登录 才能够评论, 你可以免费 注册 一个本站的账号。

评论(2

夜空下最亮的亮点 2024-11-25 22:33:53

不可以。由于这些类型是不可变的,因此您应该使用封装,而不是继承。

No. Since these types are immutable, you should be using encapsulation, not inheritance.

揪着可爱 2024-11-25 22:33:53

我不认为上面的 Ignacios 答案需要任何改进,但由于我有这门课,我只是认为我应该分享。它避免了多次进行大和运算,还避免了使用(更)幼稚的算法时可能出现的舍入错误:

class MovingAverage:
    def __init__(self):
        self.sum = None
        self.num = 0

    def add(self, val):
        if self.sum is None:
            self.sum = val
        else:
            self.sum += val
        self.num += 1
        return self.val

    @property
    def val(self):
        if self.sum is None:
            return None
        else:
            return self.sum/self.num

if __name__ == "__main__":
    print("Running small test suite")
    fail = False

    m = MovingAverage()
    try:
        assert m.val is None, "A new MovingAverage should be None"
        assert m.add(10) == 10, "New average should be returned"
        assert m.val == 10, "The average should update for each add"
        assert m.add(20) == 15, "New average should be returned"
        assert m.val == 15, "The average should update for each add"
        assert m.add(0) == 10, "Adding zero should work"
        assert m.add(-10) == 5, "Adding a negative number should work"
        assert m.add(-1) == 19/5, "Result should be able to be a fraction"
    except AssertionError as e:
        print("Fail: %s" % e.args[0])
        fail = True

    if not fail: print("Pass")

I don't think that Ignacios answer above needs any improvements, but since I had this class laying around I just thought I should share. It avoids doing big sum operations many times and also avoids rounding errors that might occur if you use a (more) naïve algorithm:

class MovingAverage:
    def __init__(self):
        self.sum = None
        self.num = 0

    def add(self, val):
        if self.sum is None:
            self.sum = val
        else:
            self.sum += val
        self.num += 1
        return self.val

    @property
    def val(self):
        if self.sum is None:
            return None
        else:
            return self.sum/self.num

if __name__ == "__main__":
    print("Running small test suite")
    fail = False

    m = MovingAverage()
    try:
        assert m.val is None, "A new MovingAverage should be None"
        assert m.add(10) == 10, "New average should be returned"
        assert m.val == 10, "The average should update for each add"
        assert m.add(20) == 15, "New average should be returned"
        assert m.val == 15, "The average should update for each add"
        assert m.add(0) == 10, "Adding zero should work"
        assert m.add(-10) == 5, "Adding a negative number should work"
        assert m.add(-1) == 19/5, "Result should be able to be a fraction"
    except AssertionError as e:
        print("Fail: %s" % e.args[0])
        fail = True

    if not fail: print("Pass")
~没有更多了~
我们使用 Cookies 和其他技术来定制您的体验包括您的登录状态等。通过阅读我们的 隐私政策 了解更多相关信息。 单击 接受 或继续使用网站,即表示您同意使用 Cookies 和您的相关数据。
原文