使my_average(a,b)与任何定义了f_add和d_div的a和b一起使用。以及内置

时间:2014-05-08 19:30:56

标签: python python-3.x

简而言之:我想要的是我编写的大多数数学函数(例如my_average(a, b))与任何ab合作{{1}已经定义了{}和f_add。没有重载+和/而且没有在python 3中打破f_div

具体来说,我正在处理我创建的Pigment对象的实例。重载这些对象的运算符并不简单。例如:

两个实例之间的差异(用于距离目的)可能是my_average(built_in_type, built_in_type)。 (Lab色彩空间在感知距离和欧几里德距离之间具有良好的相关性。)

两个实例的总和(用于混合目的)可能是a.lab - b.lab。 (srgb颜色空间是线性的,适合数学操作。)

出于其他目的,总和和差异可能意味着其他目的。

所以,在我现有的模块中输入鸭子不会起作用。

a.srgb + b.srgb

很好,只要我不介意重写每个函数(作为一种方法)我每次都需要这样的新对象。我想做的是再次重写我的功能以更加健壮。

我尝试了一些事情:

pigment_instance.distance(self, b)
pigment_instance.mix(self, b)

这没关系,但我最终只是将整个模块埋在一个类中。

class Averager():
    __init__(self, f_mix, f_distance):
        self.f_mix = f_mix
        ...
    def __call__(self, a, b):
        # return the average calculated with self.f_something functions

再次,工作正常,但我必须保留长默认参数或每次我想计算2 + 2时提供f_add。

def mix(a, b, f_mix=lambda x, y: x + y, f_distance=lambda x, y: x - y)
# or, same as above with decorators.

与第二种类似的选择。

def pigment_functions(f_mix, f_distance):
    return [
        functools.partial(mix, f_mix=somefunc, f_distance=somefunc),
        functools.partial(distance, f_mix=somefunc, f_distance=somefunc)]

mix, difference = pigment_functions(f_mix, f_distance)

也可以正常工作,但我在每个函数中都有全局变量和混乱

其中哪些(或其他)有意义?

2 个答案:

答案 0 :(得分:1)

如果my_average(a, b)adddiv函数实现,例如:

def my_average(a, b):
    return div(add(a, b), 2)

然后为不同类型提供不同的实现,您可以使用functools.singledispatch

import functools

@singledispatch
def div(x, y:int): # default implementation
    raise NotImplementedError('for type: {}'.format(type(x)))

@div.register(Divisible) # anything with __truediv__ method
def _(x, y):
    return x / y

@singledispatch
def add(a, b): 
    raise NotImplementedError('for type: {}'.format(type(a)))

@add.register(Addable) # anything with __add__ method
def _(a, b):
    return a + b

其中AddableDivisable可以定义为:

from abc import ABCMeta, abstractmethod

class Divisible(metaclass=ABCMeta):
    """Anything with __truediv__ method."""
    __slots__ = ()
    __hash__ = None # disable default hashing

    @abstractmethod
    def __truediv__(self, other):
        """Return self / other."""

    @classmethod
    def __subclasshook__(cls, C):
        if cls is Divisible:
            if any("__truediv__" in B.__dict__ for B in C.__mro__):
                return True
        return NotImplemented

class Addable(metaclass=ABCMeta):
    """Anything with __add__ method."""
    __slots__ = ()
    __hash__ = None # disable default hashing

    @abstractmethod
    def __add__(self, other):
        """Return self + other."""

    @classmethod
    def __subclasshook__(cls, C):
        if cls is Addable:
            if any("__add__" in B.__dict__ for B in C.__mro__):
                return True
        return NotImplemented

实施例

>>> isinstance(1, Addable) # has __add__ method
True
>>> isinstance(1, Divisible) # has __truediv__ method
True
>>> my_average(1, 2)
1.5
>>> class A:
...   def __radd__(self, other):
...     return D(other + 1)
...
>>> isinstance(A(), Addable)
False
>>> _ = Addable.register(A) # register explicitly
>>> isinstance(A(), Addable)
True
>>> class D:
...   def __init__(self, number):
...     self.number = number
...   def __truediv__(self, other): 
...     return self.number / other
...
>>> isinstance(D(1), Divisible) # via issubclass hook
True
>>> my_average(1, A())
1.0
>>> my_average(A(), 1) # no A.__div__
Traceback (most recent call last):
...
TypeError: unsupported operand type(s) for +: 'A' and 'int'

诸如int之类的内置数字定义了__add____truediv__方法,因此它们会自动得到支持。正如班级A所示,即使他们不能通过明确调用__add__方法来定义特定方法(例如.register,如果它们仍然可以在给定中使用),也可以使用类实施

如有必要,请使用add.registerdiv.register定义其他类型的实现,例如:

@div.register(str)
def _(x, y):
    return x % y

之后:

>>> my_average("%s", "b") # -> `("%s" + "b") % 2`
'2b'

答案 1 :(得分:0)

这可能是一个想法:

import operator

f_add = {}

def add(a,b):
    return f_add.get(type(a),operator.add)(a,b)


# example
class RGB:
    def __init__(self, r,g,b):
        self.r, self.g, self.b = (r,g,b)

    def __str__(self):
        return '<%s,%s,%s>'%(self.r,self.g,self.b)

f_add[RGB] = lambda a,b: RGB(a.r+b.r,a.g+b.g,a.b+b.b)
print(add(RGB(0.4,0.7,0.1), RGB(0.1, 0.2, 0.5)))
print(add(4,5))