Making Arithmetic Operators | should i use metaclass?

I would like to implement an object that limits values โ€‹โ€‹in a given range after applying arithmetic operations. The code below works fine, but I rewrite the methods pointlessly. Of course, there is a more elegant way to do this. Is a metaclass a way?

def check_range(_operator):
    def decorator1(instance,_val):
        value =  _operator(instance,_val)
        if value > instance._upperbound:
            value = instance._upperbound
        if value < instance._lowerbound:
            value = instance._lowerbound
        instance.value = value
        return Range(value, instance._lowerbound, instance._upperbound)
    return decorator1

class Range(object):
    '''
    however you add, multiply or divide, it will always stay within boundaries
    '''
    def __init__(self, value, lowerbound, upperbound):
        '''

        @param lowerbound:
        @param upperbound:
        '''
        self._lowerbound = lowerbound
        self._upperbound = upperbound
        self.value = value

    def init(self):
        '''
        set a random value within bounds
        '''
        self.value = random.uniform(self._lowerbound, self._upperbound)

    def __str__(self):
        return self.__repr__()

    def __repr__(self):
        return "<Range: %s>" % (self.value)

    @check_range
    def __mul__(self, other):
        return self.value * other

    @check_range
    def __div__(self, other):
        return self.value / float(other)

    def __truediv__(self, other):
        return self.div(other)     

    @check_range
    def __add__(self, other):
        return self.value + other

    @check_range
    def __sub__(self, other):
        return self.value - other
+3
source share
2 answers

, , . , , @decorator, , . ( , , BTW: , , instance.value -, ).

, , , - - :

import operator

class Range(object):

    def __init__(self, value, lowerbound, upperbound):
        self._lowerbound = lowerbound
        self._upperbound = upperbound
        self.value = value

    def __repr__(self):
        return "<Range: %s>" % (self.value)

    def _from_value(self, val):
        val = max(min(val, self._upperbound), self._lowerbound)
        # NOTE: it nice to use type(self) instead of writing the class
        # name explicitly; it then continues to work if you change the
        # class name, or use a subclass
        return type(self)(val, rng._lowerbound, rng._upperbound)

    def _make_binary_method(fn):
        # this is NOT a method, just a helper function that is used
        # while the class body is being evaluated
        def bin_op(self, other):
            return self._from_value(fn(self.value, other))
        return bin_op

    __mul__ = _make_binary_method(operator.mul)
    __div__ = _make_binary_method(operator.truediv)
    __truediv__ = __div__
    __add__ = _make_binary_method(operator.add)
    __sub__ = _make_binary_method(operator.sub)

rng = Range(7, 0, 10)
print rng + 5
print rng * 50
print rng - 10
print rng / 100

<Range: 10>
<Range: 10>
<Range: 0>
<Range: 0.07>

, , . - , , , , .

def check_range(fn):
    def wrapper(self, other):
        value = fn(self, other)
        value = max(min(value, self._upperbound), self._lowerbound)
        return type(self)(value, self._lowerbound, self._upperbound)
    return wrapper

class ApplyDecoratorsType(type):
    def __init__(cls, name, bases, attrs):
        for decorator, names in attrs.get('_auto_decorate', ()):
            for name in names:
                fn = attrs.get(name, None)
                if fn is not None:
                    setattr(cls, name, decorator(fn))

class Range(object):
    __metaclass__ = ApplyDecoratorsType
    _auto_decorate = (
            (check_range, 
             '__mul__ __div__ __truediv__ __add__ __sub__'.split()),
        )

    def __init__(self, value, lowerbound, upperbound):
        self._lowerbound = lowerbound
        self._upperbound = upperbound
        self.value = value

    def __repr__(self):
        return "<Range: %s>" % (self.value)

    def __mul__(self, other):
        return self.value * other

    def __div__(self, other):
        return self.value / float(other)

    def __truediv__(self, other):
        return self / other

    def __add__(self, other):
        return self.value + other

    def __sub__(self, other):
        return self.value - other
+2

: , , , .

, BoundedValue , .

 class BoundedValue(object):
    default_lower = 0
    default_upper = 1
    def __init__(self, upper=None, lower=None):
        self.upper = upper or BoundedValue.default_upper
        self.lower = lower or BoundedValue.default_lower
    @property
    def val(self):
        return self._val
    @val.setter
    def val(self, value):
        assert self.lower <= value <= self.upper
        self._val = value


v = BoundedValue()
v.val = 0.5 # Correctly assigns the value 0.5
print v.val # prints 0.5
v.val = 10  # Throws assertion error

, ( ) assert , ; , . - val.

, BoundedValue float int s.

+1

Source: https://habr.com/ru/post/1753438/


All Articles