2

我希望能够对对象实例执行“数学”操作。假设我有一个课程,Fruit然后是附加课程。Orange(Fruit)Apple(Fruit)

Apple具有颜色属性,并且该类知道“红苹果”与“绿”或“黄”苹果不同的面孔。

现在我希望能够:

1) 表示特定水果实例的数量。例如 3 个红苹果或 2 个橙子或 1 个香蕉(是的,这可能需要一个新的香蕉类)。

2)代表“水果袋”。例如 1 个红苹果和 2 个橙子。或 1 个红苹果和 2 个橙子

3)按预期对“水果袋”和“水果数量”进行操作。即“2个红苹果”+“1个红苹果和1个橙子”=>“3个红苹果和1个橙子”甚至可能是“2个红苹果”* 2 =>“4个红苹果”等等。

现在,在某些方面它看起来类似于Counter该类的功能,但我不确定我应该如何实现它。

我在这里的基本困境是,该类似乎Counter根据它们的散列确定两个对象是否相同,并根据相同的散列进行分组,并且没有为我提供一个选项来决定分组“3 red”的键苹果”是“红苹果”。

我知道如何处理所有的数学重载问题,我想我可以从头开始实现它,但我希望有一些其他现成的解决方案可以支持这些概念。

实际的应用程序当然更复杂,但我认为我在这里描述的问题的解决方案很容易扩展到我的实际需求。

你会建议我采取什么方法?Counter 可以如何使用还是我需要自己完成完整的实现?

编辑 1:我真的很喜欢@jbndlr 关于“你说整数 w/值 3,而不是 3 个整数”的评论。但是有区别...

您如何将一个值为 1 的整数、一个值为 1 的整数和一个值为 4 的第三个整数一起计算?三个整数是正确的答案吗?或者也许是“2个整数整数w/值1和1个整数w/值4?

数数不像求和...

从某种抽象层面来看,它会根据事物的类型发送计数,这将迫使你在 '1 red_apple' 和 '1 apple which is red' 之间做出强烈区分,即 '1 red_apple' + '1 green_apple 只是 '1 red_apple + 1 green_apple' (因为 green_apple 与 red_apple 不同),而 '1 apple which is red' + '1 apple which is green' 可以认为是 '2 apples' (因为一个苹果任何其他颜色都会像苹果一样)

问题是,如果您的应用程序域要求您按颜色对苹果进行分组,您仍然不想被迫创建 3 个不同类别的苹果,您只想按颜色区分苹果实例。

这可能是我走错了路,决定如何计数的正确方法是由进行实际计数的人进行,以便您可以提供散列方案作为调用某些 CounterFactory 函数的一部分是否会返回一个计数器,该计数器知道他对任何提供的某种类型的对象实例的期望。

另一种选择是假设每种类型只能以某种特定方式可数,并且由类知道计算其实例的正确方法是什么,因此提供类似 __counting_key__ 的东西来支持向后兼容的方式来控制Counter 类的行为。

感谢所有出色的答案,我当然有足够的能力与之合作。我会接受看起来最接近我最终实际选择的解决方案的那个。

4

3 回答 3

2

实际上,您可以很容易地设置自己的课程。我认为,如果您只想允许对新类型(此处为:类)进行常规算术,那么重用类似的东西Counter(甚至使用 new 扩展 python )将是一项艰巨的工作。types

此示例概述了如何实现比较器和运算符重载;在此示例脚本的末尾查看如何使用这些类:

class FruitKind(object):
    def __init__(self, count=1):
        self.count = count


class Apple(FruitKind):
    def __init__(self, color, count=1):
        super(Apple, self).__init__(count)
        if isinstance(color, basestring):
            self.color = color
        else:
            raise TypeError('Color must be string-like.')

    def __str__(self):
        return '{} {} Apple(s)'.format(self.count, self.color)

    def __eq__(self, other):
        if all([
                type(self) is type(other),
                self.color == other.color
                ]):
            return True
        return False

    def __ne__(self, other):
        return not self.__eq__(other)

    def __add__(self, other):
        if self == other:
            self.count += other.count
            return self
        else:
            raise TypeError('Cannot add different Fruits.')
    def __sub__(self, other):
        if self == other:
            self.count += other.count
            return self
        else:
            raise TypeError('Cannot subtract different Fruits.')


class FruitBag(object):
    def __init__(self, items=[]):
        self.items = items

    def __add__(self, other):
        if isinstance(other, FruitBag):
            # Merge self.items and other.items
            pass
        elif isinstance(other, FruitKind):
            # Merge other into self.items
            pass
        else:
            raise NotImplementedError(
                'Cannot add instance of {} to Fruitbag.'
                .format(str(type(other))))


if __name__ == '__main__':
    a1 = Apple('red')
    a2 = Apple('red', count=3)
    a1 += a2
    print(a1)

    a3 = Apple('green')
    try:
        a1 += a3
    except TypeError as te:
        print(te.message)

运行它会产生以下输出:

4 red Apple(s)
Cannot add different Fruits.

然而,在这个例子中,我认为是FruitKind对水果的描述以及使其在其他产品中独一无二的属性(两个苹果可能是两个苹果,但在这里,颜色也用于区分它们)。因此,继承自 的类FruitKind(例如,Apple确实如此)也总是携带项目数。

于 2017-10-18T08:33:57.323 回答
1

这是在 Gist 上发布的 Jon Clements 答案
将他的答案发布为社区 wiki 答案。

class Fruit:
    def __init__(self, colour):
        self.colour = colour

    def __hash__(self):
        return hash((self.__class__.__name__, self.colour))

    def __eq__(self, other):
        return type(self) == type(other) and self.colour == other.colour

    def __repr__(self):
        return '{} ({})'.format(self.__class__.__name__, self.colour)

class Apple(Fruit):
    pass

class Berry(Fruit):
    pass

from collections import Counter

fruits = [
    Apple('red'), Apple('green'), Berry('black'), Berry('red'),
    Berry('black'), Apple('red'), Berry('red'), Apple('green'),
    Berry('blue'), Apple('pink')
]
counts = Counter(fruits)
#Counter({Apple (green): 2,
#         Apple (pink): 1,
#         Apple (red): 2,
#         Berry (black): 2,
#         Berry (blue): 1,
#         Berry (red): 2})
于 2017-10-18T09:00:25.037 回答
1

您可以覆盖__hash__(self)对象的 ,以便根据您要隔离的属性进行计算;您还可以覆盖该__eq__方法和其他比较器方法。

例如:

class Apple(Fruit):
    def __init__(self, ...):
        self.color = 'red'     # maybe create an Enum for the colors?

    def __hash__(self):
        return hash(('apple', self.color))   # Thanks @Jon Clements in the comments

    def __eq__(self, other):   # recommended when overwriting __hash__ - thanks @Jon Clements
        return True if self equals other, False otherwise

您可能需要考虑使该哈希更通用...而不是苹果,您可以使用self.__class__.__name__或其他任何东西(来自@Jon Clements 评论)

编辑 - 与 Counter() 一起使用:

class FruitBasket:
    def __init__(self):
        self.fruits = []
    def add_fruit(self, fruit):
        self.fruit.append(fruit)
    def __iter__(self):
        return iterable over self.fruits

counter = Counter(FruitBasket)
于 2017-10-18T08:19:09.630 回答