Make my_average(a, b) work with any a and b for which f_add and d_div are defined. As well as builtins

后端 未结 2 1604
眼角桃花
眼角桃花 2021-01-16 01:38

In short: what I want is for the majority of mathematical functions I\'ve written (e.g., my_average(a, b)) to work with any a and b fo

相关标签:
2条回答
  • 2021-01-16 02:10

    if you have my_average(a, b) that is implemented in terms of add and div functions e.g.:

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

    then to provide different implementations for different types, you could use 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
    

    where Addable, Divisable could be defined as:

    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
    

    Example

    >>> 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'
    

    Builtin numbers such as int define __add__, __truediv__ method so they are supported automatically. As class A shows, you could use classes even if they don't define the specific methods such as __add__ by calling .register method explicitly if they still can be used in the given implementation.

    Use add.register and div.register to define implementations for other types if necessary e.g.:

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

    After that:

    >>> my_average("%s", "b") # -> `("%s" + "b") % 2`
    '2b'
    
    0 讨论(0)
  • 2021-01-16 02:13

    This might be an idea:

    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))
    
    0 讨论(0)
提交回复
热议问题