简而言之:我想要的是我编写的大多数数学函数(例如my_average(a, b)
)与任何a
和b
合作{{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)
也可以正常工作,但我在每个函数中都有全局变量和混乱
其中哪些(或其他)有意义?
答案 0 :(得分:1)
如果my_average(a, b)
以add
和div
函数实现,例如:
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
其中Addable
,Divisable
可以定义为:
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.register
和div.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))